Unreal EngineのC++エディタ拡張機能を使って、コンテンツブラウザで選択したテクスチャを自動で分割し、複数のPaper2Dスプライトアセットを一括生成するツールの実装方法と、その内部処理について解説します。
実装コード
void UMakeFlipBookAssetLibrary::CreateFlipbookSelectedTexture(int32 CellWidth,int32 CellHeight)
{
//コンテンツブラウザで選択中のアセットを取得
TArray<FAssetData>SelectedAssets;
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
ContentBrowserModule.Get().GetSelectedAssets(SelectedAssets);
if (SelectedAssets.Num() == 0)return;
IAssetTools& AssetTools = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools").Get();
for (const FAssetData& Asset : SelectedAssets)
{
//テクスチャ
UTexture2D* Texture = Cast<UTexture2D>(Asset.GetAsset());
//分割サイズの設定
int32 TextureWidth = Texture->GetSizeX();
int32 TextureHeight = Texture->GetSizeY();
int32 Cols = TextureWidth / CellWidth;
int32 Rows = TextureHeight / CellHeight;
//Pathの設定
FString PackagePath = Asset.PackagePath.ToString();
PackagePath = PackagePath.Replace(TEXT("Texture"), TEXT("Sprite"));
//スプライト作成
for (int32 y = 0; y < Rows; ++y)
{
for (int32 x = 0; x < Cols; ++x)
{
//SpriteNameの名前作成
FString SpriteName = TEXT("SP_");
SpriteName.Append(Asset.AssetName.ToString());
SpriteName.Append("_");
SpriteName = SpriteName.Replace(TEXT("T_"), TEXT(""));
int32 SpriteIndex = y * Cols + x;
SpriteName.Append(FString::FromInt(SpriteIndex));
//Spriteの作成
UPaperSpriteFactory* SpriteFactory = NewObject<UPaperSpriteFactory>();
UPaperSprite* NewSprite = Cast<UPaperSprite>(AssetTools.CreateAsset(SpriteName, PackagePath, UPaperSprite::StaticClass(), SpriteFactory));
if (NewSprite)
{
// 1. 初期化用のパラメータ構造体を作成
FSpriteAssetInitParameters InitParams;
// 2. 構造体のメンバ変数に直接代入(関数ではなく変数への代入です)
InitParams.Texture = Texture;
InitParams.Offset = FIntPoint(x * CellWidth, y * CellHeight); // 切り出し開始位置(x, y)
InitParams.Dimension = FIntPoint(CellWidth, CellHeight); // 切り出しサイズ(width, height)
// 3. 構造体を渡してスプライトを初期化
// この関数が内部でジオメトリの再構築などを行ってくれます
NewSprite->InitializeSprite(InitParams);
// エディタ側に変更を通知して表示を更新
NewSprite->PostEditChange();
}
}
}
}
}
実装全体の流れ
- コンテンツブラウザの機能を取得し、選択中のアセット情報を取得
- テクスチャの取得と、分割サイズに基づく行・列の計算
- 保存先パスの生成
- ループ処理によるスプライトアセットの作成と初期化
1. コンテンツブラウザの機能とアセットの取得
// コンテンツブラウザで選択中のアセットを取得
TArray<FAssetData> SelectedAssets;
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
ContentBrowserModule.Get().GetSelectedAssets(SelectedAssets);
if (SelectedAssets.Num() == 0) return;
// アセット作成ツール(AssetTools)の取得
IAssetTools& AssetTools = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools").Get();
💡 コードの解説
-
FModuleManager
UEの機能を管理する**「倉庫番」**を担当するクラスです。
LoadModuleChecked関数を使うことで、指定したモジュール(今回は"ContentBrowser")を倉庫から呼び出して使えるようにしています。< >内はテンプレートによるキャスト指定です。 -
FContentBrowserModule
UEエディタのファイル管理システムを統括するマネージャーです。外部モジュールから、複雑なコンテンツブラウザの内部機能に安全にアクセスするための**「窓口」的な役割を果たします。
ContentBrowserModule.Get()でシングルトンとして機能を呼び出し、そこでやっとGetSelectedAssetsを使って「現在選択しているアセット」**を取得できるようになります。 -
IAssetTools
最後に、新しいアセットを生成するための強力な機能群であるAssetToolsも併せて取得しておきます。
2. テクスチャの取得と分割サイズの設定
// テクスチャの取得
UTexture2D* Texture = Cast<UTexture2D>(Asset.GetAsset());
// 分割サイズの設定
int32 TextureWidth = Texture->GetSizeX();
int32 TextureHeight = Texture->GetSizeY();
// 行と列の計算
int32 Cols = TextureWidth / CellWidth;
int32 Rows = TextureHeight / CellHeight;
取得したアセットを UTexture2D にキャストし、UI等で事前に指定した分割サイズ(CellWidth, CellHeight)をもとに、テクスチャ全体から何行(Rows)・何列(Cols)に分割できるかを算出します。
3. 保存先パスの生成
FString PackagePath = Asset.PackagePath.ToString();
PackagePath = PackagePath.Replace(TEXT("Texture"), TEXT("Sprite"));
FAssetData からはパッケージパス(保存場所)を取得できます。
元のテクスチャと同じフォルダ(Texture フォルダなど)にスプライトが混ざるのを防ぐため、文字列の Replace を使って保存先パスの一部を Sprite に差し替えています。
4. スプライトの作成と初期化
行と列の数だけ二重ループ(for)を回し、個々のスプライトを作成していきます。
① スプライト名の設定
// SpriteNameの名前作成
FString SpriteName = TEXT("SP_");
SpriteName.Append(Asset.AssetName.ToString());
SpriteName.Append("_");
SpriteName = SpriteName.Replace(TEXT("T_"), TEXT(""));
int32 SpriteIndex = y * Cols + x;
SpriteName.Append(FString::FromInt(SpriteIndex));
元のテクスチャ名から接頭辞 T_ を取り除き、代わりに SP_ を付与。最後に通し番号(SpriteIndex)をつけて一意の名前にします。
② アセットの物理的な作成
// Spriteの作成
UPaperSpriteFactory* SpriteFactory = NewObject<UPaperSpriteFactory>();
UPaperSprite* NewSprite = Cast<UPaperSprite>(AssetTools.CreateAsset(
SpriteName,
PackagePath,
UPaperSprite::StaticClass(),
SpriteFactory
));
ここがエディタ拡張の肝となる部分です。UEにおいて、「コンテンツブラウザに新しいアセット」を生み出すための**工場の役割を果たすのが「ファクトリクラス」です(普段エディタで手動作成する際も裏で動いています)。
-
工場の建設:
NewObject<UPaperSpriteFactory>()で、スプライトを作り出すための工場をメモリ上に準備します。 -
アセット生成の依頼: 先ほど取得した
AssetTools.CreateAssetに対して、「この名前・場所・クラスで、この工場を使ってアセットを作って」と依頼します。
UPaperSpriteFactory はPaper2Dスプライトを正しく初期化する知識を持っているため、これを渡すことでエラーなく正常な .uasset ファイルが生成されます。
③ 作成したスプライトの初期化(切り出し処理)
if (NewSprite)
{
// 1. 初期化用のパラメータ構造体を作成
FSpriteAssetInitParameters InitParams;
// 2. 構造体のメンバ変数に直接代入
InitParams.Texture = Texture;
InitParams.Offset = FIntPoint(x * CellWidth, y * CellHeight); // 切り出し開始位置(x, y)
InitParams.Dimension = FIntPoint(CellWidth, CellHeight); // 切り出しサイズ(width, height)
// 3. 構造体を渡してスプライトを初期化
NewSprite->InitializeSprite(InitParams);
// 4. エディタ側に変更を通知して表示を更新
NewSprite->PostEditChange();
}
生成されたばかりの空のスプライトに対して、FSpriteAssetInitParameters という構造体を使って設定を流し込みます。
-
Texture: 参照する元のテクスチャ -
Offset: テクスチャ上のどこから切り出すかの左上座標(ループの x, y にセルサイズを掛けて算出) -
Dimension: 切り出すサイズ
設定後、NewSprite->InitializeSprite(InitParams) を呼び出すことで、指定した領域だけを表示するスプライトとしてジオメトリが構築されます。
最後に PostEditChange() を呼び出すのを忘れないでください。これにより、UEエディタに対して「アセットの中身が変更された(サムネイルの更新や未保存マークの付与が必要)」ということを正しく通知できます。
まとめ
今回は、TextureをSprite化して自動でSpriteフォルダに生成される機能を作成しました。
UEのデフォルトの機能を作成することはできますが、一気にTextureをSprite化させたいときなどには、このコードとEditor Utility Widgetを用いてリスト化などをして対応するなどができそうですね。