男の子なら誰しも
「Load Game from Slotをした時に
・成功したら特定の型にCastした状態のインスタンスを返す
・失敗したら特定の型でnewしたインスタンスを返す
という機能」
を実装したくなるものだと思う。
ここで言う特定の型とは、SaveGameを継承したクラスのこと。
しかし、ロードした結果は基底クラスであるSaveGameの型で返ってくる。
つまり、ロードした結果として返ってくるインスタンスは、ほぼ確実に派生クラスにキャストすることになる。
要するに、Load Game from Slotを呼び出す度に毎回BPでキャストを書くのが非常にだるい。
Load Game from Slotを呼び出した時点で取得すべきインスタンスの型は分かっているのに、キャストの為に型を指定するのが冗長で気分が悪くなる。
また、ロード出来なかった場合の分岐文にConstruct Object from Classを毎回書くのがとにかくだるい。
なので、C++側でそういうの全部やってほしいと思った。
ということで実装。
UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "SaveGame", DynamicOutputParam = "OutSaveGame")) static void LoadOrCreateSaveGame(TSubclassOf<USaveGame> SaveGame, const FString& SlotName, int32 UserIndex, USaveGame*& OutSaveGame);
void USaveGameHelper::LoadOrCreateSaveGame(TSubclassOf<USaveGame> SaveGame, const FString& SlotName, int32 UserIndex, USaveGame*& OutSaveGame) { if (UGameplayStatics::DoesSaveGameExist(SlotName, UserIndex)) { OutSaveGame = UGameplayStatics::LoadGameFromSlot(SlotName, UserIndex); } else { OutSaveGame = UGameplayStatics::CreateSaveGameObject(SaveGame); } }
BPで呼び出した時の感じが以下。
見えてないけど、Load Or Create SaveGameのSaveGameというパラメーターで型を指定する。
TSubClassOfによって引数の型を限定するのはよくある手法なので、特に説明はいらないと思う。
僕が着想を得たのは、みんな大好きヒストリアさんの記事。
出力するインスタンスの型が入力のパラメータの型に依存するという、UE上では解決するのが若干面倒な要件だったが、何とか実装出来た。
しかしまぁ、作っておいて何だけど僕は使わないかも。
TSubClassOfの振る舞いが微妙に理解しきれてないので、これでいいのか若干不安がある。
それと、出力したインスタンスがUEエディター上のBPでは正しく見えているけど、メタ指定子のDeterminesOutputTypeとDynamicOutputParamが具体的に何をしてるのか理解できてないので、もっと型の安全性について確証を得られないと不安が残ってしまう。
まぁ、単にDynamicOutputParamで指定したパラメータをDeterminesOutputTypeの型にキャストしてるだけ、ということであれば気兼ねなく使えるのだけども。
理解度が高い人ー!
(ΦωΦ)