[UE4] 独自のアセットを実装する方法(3) 再インポートの実装

01.23

[UE4] 独自のアセットを実装する方法(3) 再インポートの実装 はコメントを受け付けていません。

cb

前回の記事で、独自のアセットを外部ファイルからインポートする方法をご紹介しました。

しかし、実際のゲームプロジェクトで使用する場合、一度のインポートで完結することは殆ど無いと思います。

 

やはり、繰り返しの調整に対応するため、再インポート機能に対応したいところです。

今回は、その実装方法をご紹介したいと思います。

 

1.Asset及びFactoryをReimportに対応する

MyAsset.h
UCLASS()
class UMyAsset : public UObject
{
//~中略~//
#if WITH_EDITORONLY_DATA
    UPROPERTY(EditAnywhere, Instanced, Category = Reimport)
    class UAssetImportData* AssetImportData;
#endif
};
MyAssetFactory.h
UCLASS()
class UMyAssetFactory : public UFactory, public FReimportHandler
{
//~中略~//
    virtual bool CanReimport(UObject* Obj, TArray<FString>& OutFilenames) override;
    virtual void SetReimportPaths(UObject* Obj, const TArray<FString>& NewReimportPaths) override;
    virtual EReimportResult::Type Reimport(UObject* Obj) override;
};
MyAssetFactory.cpp
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<FString> Values;
    FString(Buffer).ParseIntoArray(&Values, TEXT(","), true);
 
    UMyAsset* NewMyAsset =
        CastChecked<UMyAsset>(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>(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<FString>& OutFilenames)
{
    UMyAsset* MyAsset = Cast<UMyAsset>(Obj);
    if(MyAsset && MyAsset->AssetImportData)
    {
        OutFilenames.Add(FReimportManager::ResolveImportFilename(
            MyAsset->AssetImportData->SourceFilePath, MyAsset));
        return true;
    }
    return false;
}
void UMyAssetFactory::SetReimportPaths(UObject* Obj, const TArray<FString>& NewReimportPaths)
{
    UMyAsset* MyAsset = Cast<UMyAsset>(Obj);
    if(MyAsset && ensure(NewReimportPaths.Num() == 1))
    {
        MyAsset->AssetImportData->SourceFilePath =
            FReimportManager::ResolveImportFilename(NewReimportPaths[0], MyAsset);
    }
}
EReimportResult::Type UMyAssetFactory::Reimport(UObject* Obj)
{
    UMyAsset* MyAsset = Cast<UMyAsset>(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に、このアセットが再インポート可能だということを通知する必要があります。

 

2.AssetTypeActionsの追加

ContentBrowser上でアセットを右クリックした際に出てくるメニューを拡張するには、アセットに対応した AssetTypeActions クラスを実装する必要があります。

参照先モジュールに “AssetTools” を追加し、AssetTypeActionsクラスを実装します。

Blog_MyAsset.Build.cs
// ↓に"AssetTools"を追加
PublicDependencyModuleNames.AddRange(new string[] {
    "Core", "CoreUObject", "Engine", "InputCore", "UnrealEd", "AssetTools"
    });

AssetTypeActions_MyAsset.h
#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; }
};
AssetTypeActions_MyAsset.cpp
#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”メニューが実装されます。

 

3.AssetTypeActionsをAssetToolsモジュールに登録

AssetTypeActionsクラスは、クラスを定義しただけでは動作してくれません。

AssetToolsモジュールへの登録を行う必要があります。

 

この登録は、通常、AssetTypeActionsを実装したモジュールのロード時に行います。

モジュールの初期化コードが書かれている、プロジェクトと同じ名前が付いているcppを↓のように編集します。

 

Blog_MyAsset.cpp
#include "Blog_MyAsset.h"
#include "MyAsset/AssetTypeActions_MyAsset.h"
 
class FBlog_MyAsset : public FDefaultGameModuleImpl
{
    virtual void StartupModule() override;
    virtual void ShutdownModule() override;
    TSharedPtr<FAssetTypeActions_MyAsset> MyAsset_AssetTypeActions;
};
IMPLEMENT_PRIMARY_GAME_MODULE( FBlog_MyAsset, Blog_MyAsset, "Blog_MyAsset" );
 
void FBlog_MyAsset::StartupModule()
{
    MyAsset_AssetTypeActions = MakeShareable(new FAssetTypeActions_MyAsset);
    FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get()
        .RegisterAssetTypeActions(MyAsset_AssetTypeActions.ToSharedRef());
}
void FBlog_MyAsset::ShutdownModule()
{
    if (MyAsset_AssetTypeActions.IsValid())
    {
        if (FModuleManager::Get().IsModuleLoaded("AssetTools"))
        {
            FModuleManager::GetModuleChecked<FAssetToolsModule>("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) 独自のシリアライズを実装する
  • このエントリーをはてなブックマークに追加

関連記事

コメントは利用できません。

カテゴリー

ページ上部へ戻る