フォルダ検索&取得

この記事は[Unreal Engine (UE) Advent Calendar 2024 シリーズ2](https://qiita.com/advent-calendar/2024/ue)の19日目の記事です。


以前、[wigetでシーケンスのフォルダを操作する](https://qiita.com/_akoto_/items/bd605ff1b079fae25428)という記事を作成しました。
その記事では、『**フォルダの追加**』『**フォルダの削除**』『**フォルダの探し方**』について記載しました。
今回の記事は、『**フォルダの取得**』について記載していきます。


# 検証環境
UnrealEngine5.3.2

# 前知識
今回の記事は、**C++** を扱った内容になります。
[前回の記事](https://qiita.com/_akoto_/items/bd605ff1b079fae25428)では、MovieSceneFolder型でフォルダを扱うという説明をしました。
このMovieSceneFolder型は、ブループリントで扱う場合には、<font color="red">**厄介**</font>なクラスになります。
その理由は、以下の点です。

- **数化できない**
- **キャストできない**
- **サブクラスを作っても変数化できない**

その為、ブループリントで扱う場合は、**AddRootFoldertoSequenceノードでフォルダを作成した場合は、戻り値をそのまま使用するしかブループリントでは書けません**。

:::note warn
これは、追加時は扱いやすいですが、**既に存在するフォルダに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;
}
```

:::note info
このサンプルは、見つけた場合は、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;
}
```

:::note warn
説明に『**ルートフォルダと一つ下の階層を探す**』と書いてあるように、サンプルコード①もサンプルコード②も、『**ルートフォルダ**』または『**ルートフォルダの一つ下の階層**』のみしか探しに行きません。
これは、『GetChildFolders』が、<font color="red">**そのフォルダが持つ孫階層のフォルダを返さない**</font>為です。
これを直す為には、<font color="red">**再起関数を使って、階層を遡って探していく**</font>必要があります。
:::




# サンプルコード③

このサンプルは、再起関数を使って、階層を遡って探していくように改良したものになります。
サンプルコード①を改良したコードになります。

```
//
// 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を扱っていて、面倒な箇所が見つかりました。
その為、[前回の記事](https://qiita.com/_akoto_/items/bd605ff1b079fae25428)の補足として、今回作成したの記事の対応を行ってみました。