関連ブログ
- [UE4][UE5]開発環境の容量を少しでも減らす 2024.08.14UE
- [UE5] PushModel型のReplicationを使い、ネットワーク最適化を図る 2024.05.29UE
- [UE5]マテリアルでメッシュをスケールする方法 2024.01.17UE
CATEGORY
2015.01.23UE4UE/ C++
※ 2019/10/31追記:この記事は古くなっており、最新のバージョンでは動作しない可能性があります。
前回の記事で、独自のアセットを外部ファイルからインポートする方法をご紹介しました。
しかし、実際のゲームプロジェクトで使用する場合、一度のインポートで完結することは殆ど無いと思います。
やはり、繰り返しの調整に対応するため、再インポート機能に対応したいところです。
今回は、その実装方法をご紹介したいと思います。
1 2 3 4 5 6 7 8 9 |
UCLASS() class UMyAsset : public UObject { //~中略~// #if WITH_EDITORONLY_DATA UPROPERTY(EditAnywhere, Instanced, Category = Reimport) class UAssetImportData* AssetImportData; #endif }; |
1 2 3 4 5 6 7 8 |
UCLASS() class UMyAssetFactory : public UFactory, public FReimportHandler { //~中略~// virtual bool CanReimport(UObject* Obj, TArray& amp; OutFilenames) override; virtual void SetReimportPaths(UObject* Obj, const TArray& amp; NewReimportPaths) override; virtual EReimportResult::Type Reimport(UObject* Obj) override; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
//~中略~ UObject* UMyAssetFactory::FactoryCreateText( UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const TCHAR*& Buffer, const TCHAR* BuferEnd, FFeedbackContext* Warn ) { TArray Values; FString(Buffer).ParseIntoArray(&Values, TEXT(“, ”), true); UMyAsset* NewMyAsset = CastChecked(StaticConstructObject(InClass, InParent, InName, Flags)); if (NewMyAsset && (3 <= Values.Num())) { NewMyAsset->ValueA = FCString::Atoi(*Values[0]); NewMyAsset->ValueB = FCString::Atoi(*Values[1]); NewMyAsset->ValueC = FCString::Atoi(*Values[2]); if (!NewMyAsset->AssetImportData) { NewMyAsset->AssetImportData = ConstructObject(UAssetImportData::StaticClass(), NewMyAsset); } NewMyAsset->AssetImportData->SourceFilePath = FReimportManager::SanitizeImportFilename(CurrentFilename, NewMyAsset); NewMyAsset->AssetImportData->SourceFileTimestamp = IFileManager::Get().GetTimeStamp(*CurrentFilename).ToString(); NewMyAsset->AssetImportData->bDirty = false; } return NewMyAsset; } bool UMyAssetFactory::CanReimport(UObject* Obj, TArray& OutFilenames) { UMyAsset* MyAsset = Cast(Obj); if (MyAsset && MyAsset->AssetImportData) { OutFilenames.Add(FReimportManager::ResolveImportFilename( MyAsset->AssetImportData->SourceFilePath, MyAsset)); return true; } return false; } void UMyAssetFactory::SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) { UMyAsset* MyAsset = Cast(Obj); if (MyAsset && ensure(NewReimportPaths.Num() == 1)) { MyAsset->AssetImportData->SourceFilePath = FReimportManager::ResolveImportFilename(NewReimportPaths[0], MyAsset); } } EReimportResult::Type UMyAssetFactory::Reimport(UObject* Obj) { UMyAsset* MyAsset = Cast(Obj); if (!MyAsset) { return EReimportResult::Failed; } const FString Filename = FReimportManager::ResolveImportFilename( MyAsset->AssetImportData->SourceFilePath, MyAsset); if (!Filename.Len() || IFileManager::Get().FileSize(*Filename) == INDEX_NONE) { return EReimportResult::Failed; } EReimportResult::Type Result = EReimportResult::Failed; if (UFactory::StaticImportObject( MyAsset->GetClass(), MyAsset->GetOuter(), *MyAsset->GetName(), RF_Public | RF_Standalone, *Filename, NULL, this)) { if (MyAsset->GetOuter()) { MyAsset->GetOuter()->MarkPackageDirty(); } else { MyAsset->MarkPackageDirty(); } return EReimportResult::Succeeded; } return EReimportResult::Failed; } |
まずは、MyAssetに再インポート用の情報を保存出来るように、UAssetImportDataを追加します。
このメンバはエディタでしか使用しませんので、#if WITH_EDITORONLY_DATAで囲っておきます。
次に、UMyAssetFactory::FactoryCreateText()でファイルをインポートした再に、ファイルパスとタイムスタンプを保存しておきます。
そして、このアセットが再インポート可能である、ということを示すため、UMyAssetFactoryの基底クラスにFReimportHandlerを追加し、CanReimport(),SetReimportPaths(),Reimport()の3つの関数を追加します。
ここから、先ほど追加したAssetImportDataを参照し、再インポート可能かどうかの判断が行われ、UFactory::StaticImportObject()の呼び出しを経由して再インポートが実行されます。
これで再インポートの準備は整いましたが、このままではアセットを右クリックしたメニューに、再インポートのための項目が登録されていません。
次はContentBrowserに、このアセットが再インポート可能だということを通知する必要があります。
ContentBrowser上でアセットを右クリックした際に出てくるメニューを拡張するには、アセットに対応した AssetTypeActions クラスを実装する必要があります。
参照先モジュールに “AssetTools” を追加し、AssetTypeActionsクラスを実装します。
1 2 |
// ↓に"AssetTools"を追加 PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UnrealEd" , "AssetTools"}); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#pragma once #include "UnrealEd.h" #include "AssetTypeActions_Base.h" class FAssetTypeActions_MyAsset : public FAssetTypeActions_Base { public: virtual FText GetName() const override { return NSLOCTEXT(“AssetTypeActions”, “AssetTypeActions_MyAsset”, “MyAsset”); } virtual FColor GetTypeColor() const override { return FColor::White; } virtual uint32 GetCategories() override { return EAssetTypeCategories::Misc; } virtual UClass* GetSupportedClass() const override; virtual bool IsImportedAsset() const override { return true; } }; |
1 2 3 4 5 6 7 8 9 10 11 |
#include "Blog_MyAsset.h" #include "AssetTypeActions_MyAsset.h" #include "MyAsset.h" #define LOCTEXT_NAMESPACE “AssetTypeActions” UClass * FAssetTypeActions_MyAsset::GetSupportedClass() const { return UMyAsset::StaticClass(); } #undef LOCTEXT_NAMESPACE |
FAssetTypeActions_Baseクラスを継承し、自分のアセットの情報を登録します。
特に重要なのは、GetSupportedClass()で、ココでアセットの型情報を指定します。
今回は独自のアクションを追加する訳ではないので、 IsImportedAsset() をオーバーライドすれば、”Reimport”メニューが実装されます。
AssetTypeActionsクラスは、クラスを定義しただけでは動作してくれません。
AssetToolsモジュールへの登録を行う必要があります。
この登録は、通常、AssetTypeActionsを実装したモジュールのロード時に行います。
モジュールの初期化コードが書かれている、プロジェクトと同じ名前が付いているcppを↓のように編集します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include "Blog_MyAsset.h" #include "MyAsset/AssetTypeActions_MyAsset.h" class FBlog_MyAsset : public FDefaultGameModuleImpl { virtual void StartupModule() override; virtual void ShutdownModule() override; TSharedPtr MyAsset_AssetTypeActions; }; IMPLEMENT_PRIMARY_GAME_MODULE(FBlog_MyAsset, Blog_MyAsset, “Blog_MyAsset”); void FBlog_MyAsset::StartupModule() { MyAsset_AssetTypeActions = MakeShareable(new FAssetTypeActions_MyAsset); FModuleManager::LoadModuleChecked(“AssetTools”).Get() .RegisterAssetTypeActions(MyAsset_AssetTypeActions.ToSharedRef()); } void FBlog_MyAsset::ShutdownModule() { if (MyAsset_AssetTypeActions.IsValid()) { if (FModuleManager::Get().IsModuleLoaded(“AssetTools”)) { FModuleManager::GetModuleChecked(“AssetTools”).Get() .UnregisterAssetTypeActions(MyAsset_AssetTypeActions.ToSharedRef()); } MyAsset_AssetTypeActions.Reset(); } } |
これで、モジュールのロード時に、AssetTypeActions_MyAssetが登録され、実際にContentBrowser上から再インポートが呼び出せるようになります。
実際に ImportSample.myasset の中身を書き換えて実行し、値が再読み込みされているのを確認してみて下さい。
コードばかりで長くなってしまいましたが、最後まで読んで頂き、ありがとうございました。
次回は、今回追加したAssetTypeActionsを利用して、アセットに対する独自のアクションを実装してみます。
[UE4] 独自のアセットを実装する方法(1) アセットクラスの実装 [UE4] 独自のアセットを実装する方法(2) インポートの実装 [UE4] 独自のアセットを実装する方法(3) 再インポートの実装 [UE4] 独自のアセットを実装する方法(4) アセットにアクションを追加する [UE4] 独自のアセットを実装する方法(5) アセットエディタを実装する [UE4] 独自のアセットを実装する方法(6) 独自のシリアライズを実装する