コンテンツアセットタイプとはテクスチャやスタティックメッシュなど、エディタ上で作成できるオブジェクトで、必ずしもレベルに配置せずともよく、エディター内で静的なインスタンスをつくることができるというものです。
ここでは、独自のコンテンツアセットタイプを作成する最小構成のみの手順だけまとめます。Pluginとして実装する前提ですが、プロジェクト内でも同様のはずです。
本記事は、Gerke Max Preussner氏の Adding New Asset Types to UE4 を参考にしています。実際のコードも氏のGithubレポを参考にするとより実用の理解が進むはずです。
執筆時のバージョンは 4.27.0
です。
1. RuntimeモジュールとEditorモジュールを用意する
Runtimeモジュールに加えてEditorモジュールも用意します。モジュールを分けなくても動作しますが、慣例に従いEditorモジュールを作成したほうが良いでしょう。配布時の容量や動作の効率性など恩恵が多いはずです。
最初に、Plugins > New Plugin から空のプラグイン(ここでは便宜上 "TestPlugin" と命名)を作成します。
エクスプローラー上でプラグインモジュール( Plugins / TestPlugin / Source 配下のディレクトリ)をコピペしつつ、設定ファイルやビルドスクリプトを書き換えます。
/TestPlugin
...
/Source
/TestPlugin
/Private
TestPlugin.cpp
/Public
TestPlugin.cpp
TestPlugin.Build.cs
/TestPluginEditor <---- TestPluginをコピペしてディレクトリ名とファイル名をリネーム
/Private
TestPluginEditor.cpp
/Public
TestPluginEditor.h
TestPlugin.Build.cs
TestPlugin.uplugin
.uplugin
ファイルのモジュールにEditorモジュールを追加します。
{
...
"Modules" :
[
{
"Name" : "TestPlugin",
"Type" : "Runtime",
"LoadingPhase" : "Default"
},
{
"Name" : "TestPluginEditor",
"Type" : "Editor",
"LoadingPhase" : "Default"
}
]
}
namespace UnrealBuildTool.Rules
{
public class TestPlugin : ModuleRules
{
public TestPlugin(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(
new string[] {
"Core",
"CoreUObject",
});
}
}
}
Editorモジュール用のビルドスクリプトのネームスペースやクラス名、依存先を書き換えます。TestPlugin
をTestPluginEditor
と書き換えます。
using UnrealBuildTool;
public class TestPluginEditor : ModuleRules
{
public TestPluginEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"TestPlugin", // アセットを定義するRuntimeモジュールを依存先に追加
"UnrealEd", // Editor用のモジュールを依存先に追加
});
}
}
デフォルトで配置されているモジュール自体の定義クラスもリネームしておきます。TestPlugin
をTestPluginEditor
と書き換えます。
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FTestPluginEditorModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
#include "TestPluginEditor.h"
#define LOCTEXT_NAMESPACE "FMTestluginEditorModule"
void FTestPluginEditorModule::StartupModule()
{}
void FTestPluginEditorModule::ShutdownModule()
{}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FTestPluginEditorModule, TestPluginEditor)
2. UObject派生のクラスを定義する
AssetTypeとなる UObject
派生クラスを、Runtimeモジュール配下に、Public
として定義します。
パラメータは任意です。確認用に複数作って置くと良いでしょう。
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "UserDefinedAsset.generated.h"
UCLASS(BlueprintType)
class TESTPLUGIN_API UUserDefinedAsset : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
int32 IntParam;
UPROPERTY(EditAnywhere)
FText TextParam;
};
この時点でビルドしてみると、C++クラスはつくられていることが確認できますが、AssetTypeとしてエディター内インスタンスを作成することはできません。
3. AssetFactoryクラスを実装する
UFactory
を継承するオブジェクトをEditorモジュール配下にPrivate
で実装します。
ファクトリとは、インスタンス生成時の処理をテンプレートをもとにして切り分けることで、柔軟なクラス定義を可能にするというデザインパターンに則った概念です。つまり、当該アセットのインスタンス生成時の振る舞いをお作法に則って定義するということです。
#pragma once
#include "CoreMinimal.h"
#include "Factories/Factory.h"
#include "UserDefinedAssetFactory.generated.h"
UCLASS()
class UUserDefinedAssetFactory : public UFactory
{
GENERATED_UCLASS_BODY()
public:
virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
virtual bool ShouldShowInNewMenu() const override;
};
ファクトリの実装内容としては、
- コンストラクター内でメンバー変数である
SupportedClass
に、先程作ったUObject派生クラスのStaticClass
を代入します。 -
FactoryCreateNew
関数をオーバーライドして、先程作ったUObject派生クラスのインスタンスを返すようにします。 -
ShouldShowInMenu
をオーバーライドして、True
を返すようにすることで、アセットメニューの表示を許可します。
#include "UserDefinedAssetFactory.h"
#include "UserDefinedAsset.h"
UUserDefinedAssetFactory::UUserDefinedAssetFactory(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
SupportedClass = UUserDefinedAsset::StaticClass();
bCreateNew = true;
bEditAfterNew = true;
}
UObject* UUserDefinedAssetFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
return NewObject<UUserDefinedAsset>(InParent, InClass, InName, Flags);
}
bool UUserDefinedAssetFactory::ShouldShowInNewMenu() const
{
return true;
}
これでもまだアセットタイプの実装は完了していません。
4. AssetTypeActionsをつくり、登録する
FAssetTypeActions_Base
を継承するクラスをオブジェクトを作成します。エディター内でインスタンス生成される際のエディターの挙動をより具体的に定義することができます。
エディター内インスタンスのアイコンや色、アセットタイプのカテゴリーなどを指定します。
OpenAssetEditor
やGetActions
といった関数をコメントアウトしていますが、これらをオーバーライドすると、アセットエディターのインターフェースや、ファイルをドラッグするインスタンス生成など高度な定義ができますが、非常に長くなりそうなので今回はそれらを扱いません。代わりに、デフォルトの(スーパークラス)の関数をそのまま使います。
#pragma once
#include "AssetTypeActions_Base.h"
class FTestPluginActions : public FAssetTypeActions_Base
{
public:
virtual FText GetName() const override;
virtual FColor GetTypeColor() const override;
virtual UClass* GetSupportedClass() const override;
virtual uint32 GetCategories() override;
//virtual void OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<class IToolkitHost> EditWithinLevelEditor = TSharedPtr<IToolkitHost>()) override;
//virtual void GetActions(const TArray<UObject*>&InObjects, FMenuBuilder & MenuBuilder) override;
//virtual bool HasActions(const TArray<UObject*>& InObjects) const override;
private:
};
ここでは、
- AssetActionの名称と、適用対象のクラス(
UUserDefinedAsset
)を設定 - アセットタイプのアイコンの色を白
- カテゴリーは
Media
配下
としています。
#include "TestPluginActions.h"
#include "UserDefinedAsset.h"
FText FTestPluginActions::GetName() const
{
return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_TestPlugin", "Test Plugin");
}
FColor FTestPluginActions::GetTypeColor() const
{
return FColor::White;
}
UClass* FTestPluginActions::GetSupportedClass() const
{
return UUserDefinedAsset::StaticClass();
}
uint32 FTestPluginActions::GetCategories()
{
return EAssetTypeCategories::Media;
}
最後にAssetTypeActionsを登録します。UFactory
などUObject
派生クラスのエンジンへの登録は暗黙的におこなれますが、それ以外は、明示的にコードを書かないといけなかったりする罠があったりします。AssetTypeActions
もAssetTools
に登録してあげる必要があるので、モジュールの定義クラスにコードを追加します。
#include "TestPluginEditor.h"
#include "Modules/ModuleInterface.h"
#include "TestPluginActions.h"
#define LOCTEXT_NAMESPACE "FTestPluginEditorModule"
void FTestPluginEditorModule::StartupModule()
{
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
AssetTools.RegisterAssetTypeActions(MakeShareable(new FTestPluginActions()));
}
void FTestPluginEditorModule::ShutdownModule()
{}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FTestPluginEditorModule, TestPluginEditor)
以上で、最小構成でのアセットタイプの作成が完了しました。
AssetTypeActions
のOpenAssetEditor
をオーバーライドをしていないので、デフォルト表示のアセットエディタが開かれます。アセットエディタのカスタマイズ自体はまた別に扱いたいと思います。