関連ブログ
- [UE4][UE5]開発環境の容量を少しでも減らす 2024.08.14UE
- [UE5] PushModel型のReplicationを使い、ネットワーク最適化を図る 2024.05.29UE
- [UE5]マテリアルでメッシュをスケールする方法 2024.01.17UE
CATEGORY
2015.02.02UE4UE/ C++
改訂バージョン: Unreal Engine 4.21 |
今回は、アセット実装の連続記事の最終回です。
独自のシリアライズを実装することで、UPROPERTYに対応していない型のデータをセーブ/ロードする方法をご紹介したいと思います。
第1回の記事でご紹介したように、UObject派生クラスでUPROPERTYにしたメンバ変数は、自動的にセーブ/ロードに対応されます。
しかし、時にはUPROPERTYに対応していない型のデータや、外部ライブラリで定義されたデータ型でUSTRUCT化出来ないものを保存したいケースも出てくると思います。
そういった場合には、UObject::Serialize()関数をオーバーライドすることで対応することが出来ます。
UPROPERTY化出来ない型に、、、と言っておいてイキナリですが、UPROPERTY化しないとエディタ上で値を確認するのが難しくなります・・・
なので、今回はサンプルと割りきって、シリアライズ対象外に指定したUPROPERTYを自前でシリアライズする、という、現実的には非常に無意味なコードを書いてみたいと思います。
1 2 3 4 5 6 7 8 9 10 |
class BLOG_MYASSET_API UMyAsset : public UObject { //~中略~ UPROPERTY(EditAnywhere, Transient) int32 ValueX; //~中略~ }; |
UMyAssetクラスにメンバを追加します。
UPRPPERTYのオプションに「Transient」と追加することで、このメンバはシリアライズ対象外となります。
この状態で実行し、エディタ上でValueXの値を書き換えてセーブし、エディタを再起動すると、ValueXの値が保存されずに0に戻っていることを確認してみて下さい。
今回は、このValueXを自前のコードでセーブ/ロードに対応していきます。
まずは、UMyAssetクラスでSerialize()関数をオーバーライドして、ValueXのセーブ/ロードを実装してみます。
1 2 3 4 5 6 7 8 9 10 |
UCLASS() class BLOG_MYASSET_API UMyAsset : public UObject { GENERATED_UCLASS_BODY() virtual void Serialize(FArchive& amp; Ar) override; //追加 //~中略~ }; |
1 2 3 4 5 6 7 8 9 10 |
//~中略~ void UMyAsset::Serialize(FArchive& amp; Ar) { Super::Serialize(Ar); if (Ar.IsSaving() || Ar.IsLoading()) { Ar& lt; < ValueX; } } |
Serialize()関数は、セーブ/ロード以外にも呼び出される場合があります。
セーブ/ロードのために呼び出されたかどうかは、引数で渡されたFArchiveから取得出来ます。
また、ちょっと分かりにくいのですが、セーブ/ロードの際の手順は同じで、FArchiveに対する<<演算子で実装されています。
構造体など、直接<<演算子が対応されていない型を保存したい場合は、対応したデータ型にバラして順次<<を呼び出していきます。
で、このままのコードで実行すると、新規にインポートしたアセットは正常にセーブ/ロード出来るのですが、ValueXのシリアライズを実装する以前にセーブしていたアセットをロードしようとすると、エディタがクラッシュしてしまいます。
これは、以前セーブされた際にはアセット内にValueXが保存されていないため、存在しないデータを読み出そうとしてしまうからです。
次は、アセットにバージョン情報を埋め込んで、古いバージョンのデータからは、ValueXの読み出しを行わないための対応を実装します。
まずはソースコードです。UMyAsset::Serialize()関数を、以下のように書き換えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//~中略~ void UMyAsset::Serialize(FArchive& Ar) { Super::Serialize(Ar); static const int32 MyVersion = 1; static const FGuid MyGUID(111, 222, 333, 444); static FCustomVersionRegistration RegisterMyAssetCustomVersion(MyGUID, MyVersion, TEXT(“MyAssetVersion”)); Ar.UsingCustomVersion(MyGUID); if (Ar.IsSaving() || (Ar.IsLoading() && (MyVersion <= Ar.CustomVer(MyGUID)))) { Ar << ValueX; } } |
3つのstatic変数は、必要に応じてクラスのstaticメンバにするなり、グローバル変数にするなりして下さい。
MyVersionが、自分で定義するアセットのバージョン番号です。
アセットのデータ形式が変わった場合は、この値を書き換えていきます。
MyGUIDには、他と被らない適当な値を入れておき、予めGUIDとバージョン番号を組み合わせて登録しておきます。
シリアライズを行う際には、UsingCustomVersion()で、GUIDに紐付けられたバージョン番号を使用することを宣言します。
これで、セーブ時にはアセットにバージョン情報が追加されるようになります。
ロード時には、CustomVer()を使って保存されたバージョン情報を取得し、現在のバージョンより古ければValueXのシリアライズを行わない、という処理を追加しました。
これで、古いアセットをロードしてもValueXはデフォルト値の状態で正常に処理され、改めてセーブすることで最新バージョンに更新される、という形を実装出来ました。
プロジェクトの規模によっては、旧バージョンのデータの保守を続けるのは大変になりますが、最低でも1つ前のバージョンをサポートするようにして、バージョンが変わった際にはコンソールコマンドで一括変換、というようなワークフローが構築出来ればよいのではないでしょうか。
(もちろん、標準のシリアライズだけで対応出来れば一番簡単なのですが・・・)
今回はサンプルコードが長くなってしまいましたので、この記事で使用したソースコードを公開したいと思います。
こちらからダウンロードして解凍して下さい。対応バージョンは UE4.6.1 になります。
全6回に渡って、独自のアセットを実装する方法をご紹介してきましたが、以上で本連続記事は終了になります。
最後まで読んで頂き、ありがとうございました。
[UE4] 独自のアセットを実装する方法(1) アセットクラスの実装 [UE4] 独自のアセットを実装する方法(2) インポートの実装 [UE4] 独自のアセットを実装する方法(3) 再インポートの実装 [UE4] 独自のアセットを実装する方法(4) アセットにアクションを追加する [UE4] 独自のアセットを実装する方法(5) アセットエディタを実装する [UE4] 独自のアセットを実装する方法(6) 独自のシリアライズを実装する