// Copyright Epic Games, Inc. All Rights Reserved. #include "PaperFlipbookHelpers.h" #include "ObjectTools.h" #include "PaperSprite.h" ////////////////////////////////////////////////////////////////////////// // FPaperFlipbookHelpers FString FPaperFlipbookHelpers::GetCleanerSpriteName(const FString& Name) { int32 LastCharacter = Name.Len() - 1; // Strip off "_Sprite" if (Name.Right(7) == TEXT("_Sprite")) { LastCharacter = Name.Len() - 7; } while (LastCharacter >= 0 && FChar::IsPunct(Name[LastCharacter])) { LastCharacter--; } if (LastCharacter > 0) { return Name.Left(LastCharacter + 1); } else { return Name; } } bool FPaperFlipbookHelpers::ExtractSpriteNumber(const FString& String, FString& BareString, int32& Number) { bool bExtracted = false; int32 LastCharacter = String.Len() - 1; if (LastCharacter >= 0) { // Find the last character that isn't a digit (Handle sprite names with numbers inside inverted commas / parentheses) while (LastCharacter >= 0 && !FChar::IsDigit(String[LastCharacter])) { LastCharacter--; } // Only proceed if we found a number in the sprite name if (LastCharacter >= 0) { while (LastCharacter > 0 && FChar::IsDigit(String[LastCharacter - 1])) { LastCharacter--; } if (LastCharacter >= 0) { const int32 FirstDigit = LastCharacter; int32 EndCharacter = FirstDigit; while (EndCharacter > 0 && !FChar::IsAlnum(String[EndCharacter - 1])) { EndCharacter--; } if (EndCharacter == 0) { // This string consists of non alnum + number, eg. _42 // The flipbook / category name in this case will be _ // Otherwise, we strip out all trailing non-alnum chars EndCharacter = FirstDigit; } const FString NumberString = String.Mid(FirstDigit); BareString = GetCleanerSpriteName((EndCharacter > 0) ? String.Left(EndCharacter) : String); Number = FCString::Atoi(*NumberString); bExtracted = true; } } } if (!bExtracted) { BareString = String; Number = -1; } return bExtracted; } void FPaperFlipbookHelpers::ExtractFlipbooksFromSprites(TMap >& OutSpriteFlipbookMap, const TArray& Sprites, const TArray& InSpriteNames) { OutSpriteFlipbookMap.Reset(); // Local copy check((InSpriteNames.Num() == 0) || (InSpriteNames.Num() == Sprites.Num())); TArray SpriteNames = InSpriteNames; if (InSpriteNames.Num() == 0) { SpriteNames.Reset(); for (int32 SpriteIndex = 0; SpriteIndex < Sprites.Num(); ++SpriteIndex) { check(Sprites[SpriteIndex] != nullptr); SpriteNames.Add(Sprites[SpriteIndex]->GetName()); } } // Group them TMap SpriteNameMap; TArray RemainingSprites; for (int32 SpriteIndex = 0; SpriteIndex < Sprites.Num(); ++SpriteIndex) { UPaperSprite* Sprite = Sprites[SpriteIndex]; const FString SpriteName = SpriteNames[SpriteIndex]; SpriteNameMap.Add(SpriteName, Sprite); int32 SpriteNumber = 0; FString SpriteBareString; if (ExtractSpriteNumber(SpriteName, /*out*/ SpriteBareString, /*out*/ SpriteNumber)) { SpriteBareString = ObjectTools::SanitizeObjectName(SpriteBareString); OutSpriteFlipbookMap.FindOrAdd(SpriteBareString).Add(Sprite); } else { RemainingSprites.Add(Sprite); } } // Natural sort using the same method as above struct FSpriteSortPredicate { FSpriteSortPredicate() {} // Sort predicate operator bool operator()(UPaperSprite& LHS, UPaperSprite& RHS) const { FString LeftString; int32 LeftNumber; ExtractSpriteNumber(LHS.GetName(), /*out*/ LeftString, /*out*/ LeftNumber); FString RightString; int32 RightNumber; ExtractSpriteNumber(RHS.GetName(), /*out*/ RightString, /*out*/ RightNumber); return (LeftString == RightString) ? (LeftNumber < RightNumber) : (LeftString < RightString); } }; // Sort sprites TArray Keys; OutSpriteFlipbookMap.GetKeys(Keys); for (auto SpriteName : Keys) { OutSpriteFlipbookMap[SpriteName].Sort(FSpriteSortPredicate()); } // Create a flipbook from all remaining sprites // Not sure if this is desirable behavior, might want one flipbook per sprite if (RemainingSprites.Num() > 0) { RemainingSprites.Sort(FSpriteSortPredicate()); const FString DesiredName = GetCleanerSpriteName(RemainingSprites[0]->GetName()) + TEXT("_Flipbook"); const FString SanitizedName = ObjectTools::SanitizeObjectName(DesiredName); OutSpriteFlipbookMap.Add(SanitizedName, RemainingSprites); } }