この記事はUnreal Engine (UE) Advent Calendar 2024 シリーズ2の19日目の記事です。
以前、wigetでシーケンスのフォルダを操作するという記事を作成しました。
その記事では、『フォルダの追加』『フォルダの削除』『フォルダの探し方』について記載しました。
今回の記事は、『フォルダの取得』について記載していきます。
検証環境
UnrealEngine5.3.2
前知識
今回の記事は、C++ を扱った内容になります。
前回の記事では、MovieSceneFolder型でフォルダを扱うという説明をしました。
このMovieSceneFolder型は、ブループリントで扱う場合には、厄介なクラスになります。
その理由は、以下の点です。
- 数化できない
- キャストできない
- サブクラスを作っても変数化できない
その為、ブループリントで扱う場合は、AddRootFoldertoSequenceノードでフォルダを作成した場合は、戻り値をそのまま使用するしかブループリントでは書けません。
これは、追加時は扱いやすいですが、既に存在するフォルダにBindingProxyを入れていく場合には、扱い難いと感じました。
今回は、その部分を取り扱っています。
サンプルコード①
まずは、単純にフォルダを探すコードを作成しました。
//
// ルートフォルダと一つ下の階層を探す
//
UMovieSceneFolder* UMyBlueprintFunctionLibrary::FindFolder(ULevelSequence* LevelSequence, const FString FolderName)
{
TArray<UMovieSceneFolder*> rootFolders;
UMovieScene* MovieScene = LevelSequence->GetMovieScene();
if (!MovieScene)
{
return nullptr;
}
rootFolders = MovieScene->GetRootFolders();
for (UMovieSceneFolder* rootFolder : rootFolders)
{
FString name = rootFolder->GetFolderName().ToString();
if (FolderName == name)
{
return rootFolder;
}
for (UMovieSceneFolder* childFolder : rootFolder->GetChildFolders())
{
name = childFolder->GetFolderName().ToString();
if (FolderName == name)
{
return childFolder;
}
}
}
return nullptr;
}
このサンプルは、見つけた場合は、MovieSceneFolderを戻し、見つけられなかった場合は、nullptrを返します。その為、レベルシーケンスがFolderNameのフォルダを既に持っているかを調べる事にも使えます。
サンプルコード②
次に、見つけられなかった場合は、MovieSceneFolderを新規に作成するようにしました。
サンプルコード①では、見つけられなかった場合は、AddRootFoldertoSequenceノードを実行する必要がありました。
//
// ルートフォルダと一つ下の階層を探す。見つからなければ、新規にフォルダのインスタンスを作成する。
//
UMovieSceneFolder* UMyBlueprintFunctionLibrary::FindFolder_v2(ULevelSequence* LevelSequence, const FString FolderName)
{
TArray<UMovieSceneFolder*> rootFolders;
UMovieScene* MovieScene = LevelSequence->GetMovieScene();
if (!MovieScene)
{
return nullptr;
}
rootFolders = MovieScene->GetRootFolders();
for (UMovieSceneFolder* rootFolder : rootFolders)
{
FString name = rootFolder->GetFolderName().ToString();
if (FolderName == name)
{
return rootFolder;
}
for (UMovieSceneFolder* childFolder : rootFolder->GetChildFolders())
{
name = childFolder->GetFolderName().ToString();
if (FolderName == name)
{
return childFolder;
}
}
}
// フォルダを新規作成
UMovieSceneFolder* resultFolder = nullptr;
#if WITH_EDITORONLY_DATA
if (MovieScene)
{
MovieScene->Modify();
resultFolder = NewObject<UMovieSceneFolder>(MovieScene, NAME_None, RF_Transactional);
resultFolder->SetFolderName(FName(*FolderName));
MovieScene->AddRootFolder(resultFolder);
}
#endif
return resultFolder;
}
説明に『ルートフォルダと一つ下の階層を探す』と書いてあるように、サンプルコード①もサンプルコード②も、『ルートフォルダ』または『ルートフォルダの一つ下の階層』のみしか探しに行きません。
これは、『GetChildFolders』が、そのフォルダが持つ孫階層のフォルダを返さない為です。
これを直す為には、再起関数を使って、階層を遡って探していく必要があります。
サンプルコード③
このサンプルは、再起関数を使って、階層を遡って探していくように改良したものになります。
サンプルコード①を改良したコードになります。
//
// 2階層以降も探しに行く
//
UMovieSceneFolder* UMyBlueprintFunctionLibrary::FindFolder_v3(ULevelSequence* LevelSequence, const FString FolderName)
{
TArray<UMovieSceneFolder*> rootFolders;
UMovieScene* MovieScene = LevelSequence->GetMovieScene();
if (!MovieScene)
{
return nullptr;
}
rootFolders = MovieScene->GetRootFolders();
for (UMovieSceneFolder* rootFolder : rootFolders)
{
UE_LOG(LogTemp, Warning, TEXT("@@@@ RootFolders:%s"), *rootFolder->GetFolderName().ToString());
FString name = rootFolder->GetFolderName().ToString();
if (FolderName == name)
{
UE_LOG(LogTemp, Warning, TEXT("@@@@ Match : %s"), *name);
return rootFolder;
}
UMovieSceneFolder* childFolder = SerchChildFolder(rootFolder, FolderName);
if (childFolder)
{
return childFolder;
}
}
return nullptr;
}
//
// 2階層以降も探しに行く為の再起関数
//
UMovieSceneFolder* UMyBlueprintFunctionLibrary::SerchChildFolder(UMovieSceneFolder* SceneFolder, const FString FolderName)
{
UMovieSceneFolder* resultFolder = nullptr;
if (SceneFolder)
{
for (UMovieSceneFolder* childFolder : SceneFolder->GetChildFolders())
{
FString name = childFolder->GetFolderName().ToString();
// UE_LOG(LogTemp, Warning, TEXT("@@@@ SerchChildFolder -> ChildFolders:%s"), *childFolder->GetFolderName().ToString());
if (FolderName == name)
{
return childFolder;
}
else
{
resultFolder = SerchChildFolder(childFolder, FolderName);
if (resultFolder)
{
return resultFolder;
}
}
}
}
return resultFolder;
}
まとめ
エディタの仕様で、UMovieSceneFolderを扱っていて、面倒な箇所が見つかりました。
その為、前回の記事の補足として、今回作成したの記事の対応を行ってみました。