ぼっちプログラマのメモ

Unreal Engineについて書いたりしてます

【UE5, C++】 AnimSequence から AnimComposite, Montage を少し便利に作る方法

はじめに

AnimSequenceアセットの右クリックメニューから AnimComposite, Montageアセットを作れます。しかし、アセット生成先のフォルダを指定できなかったり、アセット名末尾に _Composite/Montage が強制的につけられます。

それぞれ手動でアセットを移動・リネームすればいい話ではありますが…それが数十、数百回となると流石にストレスですし、ヒューマンエラーも時々発生してしまいそうです。

ということで、この処理をC++で自作して、プロジェクトのルールに沿った場所・命名規則AnimComposite, Montageアセットを作成できるようにします。

続きを読む

【UE5】StateTreeで文字列から関数・イベントを呼び出すタスクの作り方

はじめに

StateTreeから制御対象のActorの関数・イベントを呼びたい、というのは多々あるかと思いますが、そのたびに専用のStateTree Taskを作るのは少々めんどうです。

基底クラスで関数を用意したり、(BP)Interfaceなどを用いることで1つのTaskを流用することはできますが、プロト制作時などスピード感が必要な段階では…その準備もサボりたい所…

ということで、文字列から関数・イベントを呼び出せる CallFunctionByNameWithArguments を使ったStateTree Taskを作ってみました!

事前準備不要でStateTreeからActor(UObject)が持つ処理を呼び出せますし、

呼び出す関数名・引数をStateTreeのParameterと紐づけておけば、Actor側のStateTree Component で設定することが可能になります。

その結果、実装が多少違うBP・Actorであっても、StateTreeを超汎用的に使用することが可能になります!これは便利!(なはず)

ということで、今回はそんなStateTree Taskを実現する C++コードを紹介します。なお、もしBPのみで実現したい場合はKEコマンドを使用することになるはずです。

【UE4】ConsoleCommand「CE,KE」について【★★☆】 | キンアジのブログ

続きを読む

【UE5】StateTreeのParameterを実行中に変更する方法(C++版)

はじめに

StateTreeのParameterStateTreeComponentで初期値を設定することができ、

TaskやConditionなどで紐づけ(Binding)して利用することが可能です。

また、StateTree全体だけでなく、State毎にParameterを追加・設定することが可能です。

しかし、UE5.5時点では、「実行中にParameterの値変更すること」は C++側でしかできません。

今後のアップデートでBPからも変更可能になる可能性は高いとは思いますが、ひとまず本記事では C++ を使って Parameterを変更する方法について紹介します!

続きを読む

【UE5】StateTreeでドアの開閉状態の管理を実装してみる (BPのみ編)

はじめに

StateTreeはいいぞ!

StateTreeはUE5から追加された「汎用的な階層型ステートマシン」機能です。UEユーザの中で「ステートマシン」と言えばAnimationBPのAnimGraphにおけるステートマシンがまず思い浮かぶかと思います。基本的な概念・考え方はStateTreeでも同様で、「State(状態)を条件・処理に応じてTransition(遷移)していくことでロジックを実装できる」というものです。

ただStateTreeはAnimGraphと比べると遥かに高機能かつ柔軟な機能なので、まさに汎用的に様々なロジック実装に使用することができます!

  • NPCの振る舞い制御
  • 敵キャラの戦闘行動選択
  • オブジェクトの状態管理
  • アニメーション選択
  • エストロジック
  • などなど

そして最近は海外のUnreal Festで講演されることが増えてきたり、UE5.4, 5.5で大きく改善されたりと盛り上がりつつある機能です。

ということで、まずは「ドアの開閉制御・管理」というシンプルな処理をStateTree かつ BPのみで実装してみました!

まあこれだけだと「BPのみでええやん」となりますが、開閉時にSEなどの演出をつけたり、特定のカギを持ってないと開かない、などの制御追加をStateTree側でできるのが便利なはず…!

なお、StateTreeの基本的な操作・機能に関しては既に素晴らしい記事が沢山あるので割愛します(本当にありがとうございます!めちゃ勉強になりました!)

Unreal Engine の StateTree | Unreal Engine 5.5 ドキュメンテーション | Epic Developer Community

[UE5] StateTreeで状態管理|株式会社ヒストリア

StateTreeの構成について #ue5 - Qiita

State Tree(5.5) まとめ #ue5 - Qiita

State Treeを使ってクエストの進行管理をしてみた | ドクセル

Your First 60 Minutes with StateTree | Tutorial

StateTree | UE5攻略リンク

検証環境はUE5.5.1 です。また、ドアのBPは機能別サンプル(ContentExample)のBP_Doorを用いています。なお、今回はBPオンリーで実装していきますが、C++を使って更にいい感じに実装する方法を後日公開予定です。

続きを読む

2024年に(ある程度)プレイした中でのおすすめゲームの一言感想メモ

はじめに

そういえばここは個人ブログ(便所の落書き)なので、UnrealEngine以外の記事も全然書いて良いのだ!ということで、各ストアの購入履歴を頼りにしつつ、2024年にプレイしたゲームをまとめていく(買ったけど、プレイ時間がミリで積んでるものは割愛)

リスト

  • Switch

    • CrossCode
    • Hookah Haze
    • SCHiM - スキム -
    • ゼルダの伝説 知恵のかりもの
    • 大神 絶景版
  • PlayStation

    • SILENT HILL: The Short Message
    • ザ・パスレス
    • FINAL FANTASY VII REBIRTH
    • Stellar Blade
    • ヒア・ゼイ・ライ™ -眠りし者たち-
    • プラネット ズー
    • TUNIC
    • ゼンレスゾーンゼロ
    • アストロボット
    • Faaast Penguin
    • Neva
    • 野狗子: Slitterhead
    • インフィニティニキ
  • Steam

    • 未解決事件は終わらせないといけないから
    • Legal Dungeon
    • 8番のりば
    • Leap Year
    • 違う冬のぼくら
    • おおむねリバーシ
    • MiSide : ミサイド
    • Inverted Angel
続きを読む

【UE5】C++でChooser Table・Proxy Tableを使う方法について

はじめに

先日から続いてる Chooser 関連の記事シリーズ 第3弾!

pafuhana1213.hatenablog.com pafuhana1213.hatenablog.com

Chooserの設定方法やBPで使う方法については書きましたが、実際にプロジェクトで使い祭は C++ で Chooser Table, Proxy Tableを使う方法を知りたいところですよね!

早速ググってみると…出てこないいいいいいい! なんだ、この記事たちは!参考にならん!!!

ということで、世の中に情報がなかったのでエンジンコードを頼りに「Evaluate Chooser / Proxy を C++から呼ぶ方法について調べてみました…!


Evaluate Chooser を C++ で実行する方法

色々調べたところ、C++ で Chooser を使う際はUChooserFunctionLibraryで用意された各関数を使うのが良さそうです!

UChooserFunctionLibrary

EvaluateChooser

最もシンプル。ContextObjectにはChooserTableの入出力パラメータにおけるUObjectを渡し、ObjectClassにはChooserTableResultClassを設定

UFUNCTION(BlueprintPure, meta = (BlueprintThreadSafe, BlueprintInternalUseOnly = "true", DeterminesOutputType = "ObjectClass"))
static UObject* EvaluateChooser(const UObject* ContextObject, const UChooserTable* ChooserTable, TSubclassOf<UObject> ObjectClass);

EvaluateChooserMulti

EvaluateChooserの複数結果を返す版

 UFUNCTION(BlueprintPure, meta = (BlueprintThreadSafe, BlueprintInternalUseOnly = "true", DeterminesOutputType = "ObjectClass"))
 static TArray<UObject*> EvaluateChooserMulti(const UObject* ContextObject, const UChooserTable* ChooserTable, TSubclassOf<UObject> ObjectClass);

EvaluateObjectChooserBase

UObjectではなく、Chooser専用のFChooserEvaluationContextを渡す関数

FChooserEvaluationContextにはChooserTableの入出力パラメータを複数設定することが可能(後述)

UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe, BlueprintInternalUseOnly = "true", DeterminesOutputType = "ObjectClass"))
static UObject* EvaluateObjectChooserBase(UPARAM(Ref) FChooserEvaluationContext& Context,UPARAM(Ref) const FInstancedStruct& ObjectChooser, TSubclassOf<UObject> ObjectClass, bool bResultIsClass = false);

EvaluateObjectChooserBaseMulti

EvaluateObjectChooserBaseの複数結果を返す版

UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe, BlueprintInternalUseOnly = "true", DeterminesOutputType = "ObjectClass"))
static TArray<UObject*> EvaluateObjectChooserBaseMulti(UPARAM(Ref) FChooserEvaluationContext& Context,UPARAM(Ref) const FInstancedStruct& ObjectChooser, TSubclassOf<UObject> ObjectClass, bool bResultIsClass = false);

AddChooserObjectInput

FChooserEvaluationContextに 入出力パラメータである UObjectを設定する関数。C++のみで完結する場合は FChooserEvaluationContextAddObjectParam関数を使ったほうがシンプル

UFUNCTION(BlueprintCallable, meta = (BlueprintThreadSafe, BlueprintInternalUseOnly = "true"))
static void AddChooserObjectInput(UPARAM(Ref) FChooserEvaluationContext& Context, UObject* Object);

AddChooserStructInput

FChooserEvaluationContextに 入出力パラメータである Structを設定する関数。ただC++のみで完結する場合は FChooserEvaluationContextAddStructParam関数を使ったほうがシンプル。

UFUNCTION(BlueprintCallable, CustomThunk, meta = (BlueprintThreadSafe, BlueprintInternalUseOnly = "true", CustomStructureParam = "Value"))
static void AddChooserStructInput(UPARAM(Ref) FChooserEvaluationContext& Context, int32 Value);
    
DECLARE_FUNCTION(execAddChooserStructInput);

GetChooserStructOutput

FChooserEvaluationContextに 入出力パラメータである Structを取得する関数。ただこれはEvaluate Chooserノードが動的にピンを生成するのに使われているものであり、C++のみで完結する場合はあまり使用する機会はないかも

UFUNCTION(BlueprintPure, CustomThunk, meta = (BlueprintThreadSafe, BlueprintInternalUseOnly = "true", CustomStructureParam = "Value"))
static void GetChooserStructOutput(UPARAM(Ref) FChooserEvaluationContext& Context, int Index, int32& Value);
        
DECLARE_FUNCTION(execGetChooserStructOutput);

MakeEvaluateChooser

ChooserTableからInstancedStruct/Script/Chooser.ObjectChooserBase)を生成する関数。EvaluateObjectChooserBase(Multi)を使う際はこのInstancedStructが必要になる

UFUNCTION(BlueprintPure, Category = "Animation", meta = (BlueprintThreadSafe, BlueprintInternalUseOnly="true", NativeMakeFunc))
static FInstancedStruct MakeEvaluateChooser(UChooserTable* Chooser);

MakeChooserEvaluationContext

空のFChooserEvaluationContext を作成する関数。ただ FChooserEvaluationContext Context; で十分(内部実装もそうだし)

UFUNCTION(BlueprintCallable, Category = "Animation", meta = (BlueprintThreadSafe, BlueprintInternalUseOnly="true"))
static FChooserEvaluationContext MakeChooserEvaluationContext();

サンプルコード

で、これらを使うことでEvaluateChooserと同様に、ChooserTableを使った選定処理を独自のC++コードで実装することができます!

UFUNCTION(BlueprintCallable)
    UObject* GetObjectFromChooserTable( UChooserTable* ChooserTable, const TSubclassOf<UObject> ObjectClass,
        UPARAM(Ref) FXXXInput& Input,FXXXOutput& Output);
UObject* AXXX::GetObjectFromChooserTable(UChooserTable* ChooserTable, const TSubclassOf<UObject> ObjectClass,
    FXXXInput& Input,FXXXOutput& Output)
{
    if(ChooserTable)
    {
        // シンプル版
        //return UChooserFunctionLibrary::EvaluateChooser(this, ChooserTable, ObjectClass);
    
      // パラメータ複数版
        // ChooserTableのパラメータを設定
        FChooserEvaluationContext Context;
        Context.AddObjectParam(this);
        Context.AddStructParam(Input);
        Context.AddStructParam(Output);

        // ChooserTableを使って選定処理を実行
        FInstancedStruct ChooserStruct = UChooserFunctionLibrary::MakeEvaluateChooser(ChooserTable);
        return UChooserFunctionLibrary::EvaluateObjectChooserBase(
            Context, ChooserStruct, ObjectClass, false);
    }

    return nullptr;
}

(表示するStaticMeshアセットをパラメータに応じて変化、というもの)

EvaluateChooserノードと違い「基底クラスで処理を書き、派生クラスで使用するChooserTableを設定する」ことができるため、前回の記事にて書いた欠点を解決することができます!

ただChooserTableのResult Class・入出力パラメータの変化には弱く、クラス設定やパラメータの数が変化すると問題が起きることが EvaluateChooserノードに比べると多そうです。

ちなみに、Chooserに関する処理は各クラスがオススメです!

  • FAnimNode_ChooserPlayer(Chooser Playerノード)
    • シンプルでわかりやすい!
  • UK2Node_EvaluateChooser2( Evaluate Chooserノード)
    • 動的にピンを生成したりしてるので少し複雑
    • UK2Node_EvaluateChooserというのもありますが、こちらは古いコードの模様

ちなみに、↑のChooser Playerノードはこちら。ABP(AnimInstance)を内部で渡してくれるので、Animation Chooser設定で作ったChooser TableをAnimGraph 内で使う際に便利です!

Evaluate Proxyを C++ で実行する方法

こちらは UProxyTableFunctionLibrary を使うのがオススメです。ただProxyAssetがまだExperimentalであったり、一時的な後方互換のために用意された関数があったりします。

そのため、実際に使うのはMakeLookupProxyWithOverrideTableだけです。

UProxyTableFunctionLibrary

MakeLookupProxyWithOverrideTable

ProxyTableの処理で使う InstnacedStruct(FLookupProxyWithOverrideTable)を生成する関数。

UFUNCTION(BlueprintPure, Category = "Animation", meta = (BlueprintThreadSafe, BlueprintInternalUseOnly="true"))
static FInstancedStruct MakeLookupProxyWithOverrideTable(UProxyAsset* Proxy, UProxyTable* ProxyTable);
FInstancedStruct UProxyTableFunctionLibrary::MakeLookupProxyWithOverrideTable(UProxyAsset* Proxy, UProxyTable* ProxyTable)
{
    FInstancedStruct Struct;
    Struct.InitializeAs(FLookupProxyWithOverrideTable::StaticStruct());
    FLookupProxyWithOverrideTable& Data = Struct.GetMutable<FLookupProxyWithOverrideTable>();
    Data.Proxy = Proxy;
    Data.OverrideProxyTable = ProxyTable;
    return Struct;
}

サンプルコード

で、これを使うことでEvaluateProxyと同様に、ProxyAsset, ProxyTableを使った選定処理を独自のC++コードで実装することができます!

UObject* AXXX::GetObjectFromProxyTable(UProxyTable* ProxyTable, UProxyAsset* ProxyAsset)
{
    if(ProxyTable && ProxyAsset)
    {
        FInstancedStruct ProxyStruct =
            UProxyTableFunctionLibrary::MakeLookupProxyWithOverrideTable(ProxyAsset, ProxyTable);
        FLookupProxyWithOverrideTable& LookupProxy = ProxyStruct.GetMutable<FLookupProxyWithOverrideTable>();

        FChooserEvaluationContext Context;
        return LookupProxy.ChooseObject(Context);      
    }

    return nullptr;
}

ProxyTableChooserTableと比べると設定パラメータが少ないので、C++で実装する際もシンプル!(その分できることも少ないですが…)

前回の記事にも書いた通り、ChooserTableの長所である条件判定処理が不要で、ID(ProxyAsset)が確定している場合は今回のようなコードでプロジェクト独自のアセット管理・取得処理を書いておくと便利だと思います。

さいごに

ChooserをC++で扱うには色々複雑になるのかなぁ…と思っていたら、案外シンプルに実装できることがわかって一安心です!

Chooserは多機能で便利ではありますが、その反面プロジェクトにおける運用・管理フローを整備しないとカオスなことになりそうです。そして、Evaluate Chooser / Proxyでは物足りない部分をC++で独自実装しておくとフローがスッキリしたり、設定ミスなどによるヒューマンエラーも防げる気がしました!

そんなこんなで少しでも参考になれば幸いです。(次はChooserTableアセットの自動生成・設定を調べていきたいところ)

おしまい

【UE5】猫でも分かるChooser(動的アセットの選択)ことはじめ Part2 - Proxy Asset , Table編 -

はじめに

本記事は「【UE5】猫でも分かるChooser(動的アセットの選択)ことはじめ Part 1 - Chooserとは?編 -」の続きです!もしまだ見ていない方は Part 1 からどうぞ! pafuhana1213.hatenablog.com

Part 1 ではChooserの用途例や基本機能について説明し、「なんかChooserすごそう!便利そう!」と少し感じていただいたのかなぁと思います!(感じて!!!)

ただ実際に使おうとすると…Evaluate Chooser ノードの仕様に頭を抱えることになります。 Evaluate Chooser ノードは使用するChooser Tableアセットを動的に設定・変更することができないため、使用する箇所(キャラ・機能など)事にEvaluate Chooserノードを新たに配置する必要があるからです。

例えば「Template Animation BP(以降 TABP)を使ってアニメーション制御を共通化 ▶ 派生ABPで再生するアニメーションアセットを指定」というのはよくある実装フローです。ここにChooserを活用する場合、「TABPに Evaluate Chooser ノードを配置 ▶ 派生ABP で 使用する Chooser Tableアセットを設定」としたい所です。

が、残念ながら Evaluate Chooser ノードは動的にChooser Table アセットを変更することができないため、派生ABP毎にEvaluate Chooser ノードを配置する必要があります。これではTABPによる処理共通化・Chooserによるデータドリブン化のウマミが半減してしまします。

これはABPに限った話ではありません。例えば ダメージ処理を行う汎用BPを作る場合、Chooserを含む処理を共通化したい所ですが上述の理由によりできません。その結果、GA_Grayman_Damage, GA_Manny_Damage, GA_Dragon_Damage, GA_HogeHoge_Damage…といった感じで増えていってしまいます。これではChooserを使うのが嫌になってしまいます…

これを解決するのが、ProxyAsset / Table 機能です。これを利用することで Evaluate Chooserノードは共通・汎用処理に書くだけでOKになります!

と長くなりましたが、本記事ではProxy Asset / Table について書いていきます! (なお、アニメーション制御にChooserを使った例が続きますが、Chooserはそれ以外にも使えます!)

微妙に長くなったので、AIに要約してもらいました(クリックで展開)

記事の要約

概要

  • Unreal Engine 5(UE5)の「Chooser(動的アセット選択)」をより使いやすくするためのテクニックとして
    「Proxy Asset / Table」 を活用する方法を紹介している。
  • 前回(Part1)で挙げた「Evaluate Chooser ノードでは Chooser Table を動的に切り替えられない」問題を回避し、
    共通処理(テンプレートABPなど)に 1 つだけノードを置いてデータ差分を外部で管理する構成を目指す。

Proxy Asset / Table とは?

  • Proxy Asset
    • “ID”のようなもので、動的に切り替えたいアニメーションやアセットを特定するための名前(ID)の役割。
  • Proxy Table
    • “ID”ごとに実際のアセット(AnimSequence など)を紐づけるためのリスト。
  • これらを組み合わせることで、共通処理に Evaluate Chooser 相当のノードを増やさずに
    キャラクターごとの差分管理を実現できる。

実装フローの例

  1. Proxy Asset の作成
    • 例)ID として “PA_Idle” “PA_Walk” の 2 つを作り、紐づけるアセットの型(AnimSequence など)を設定。
  2. Proxy Table の作成
    • キャラクターごとに 1 つ用意し、
      PA_Idle” “PA_Walk” に対して実際のアニメーションを割り当てる。
    • 例)Manny 用/Dragon 用といった形で分ける。
  3. 共通ABP(Template ABP)と派生ABP の設定
    • Template ABP(TABP)に Evaluate Proxy ノードを配置し、必要な Proxy Asset(ID)を呼ぶ処理を組む。
    • Manny や Dragon の派生ABPでは “どの Proxy Table を使うか” を設定するだけで、
      それぞれのアニメーションが再生される。

Chooser Table との併用

  • Proxy Asset / Table は “アセットの動的切り替え” が得意だが、
    “複数条件に応じたトリガー” の実装はノードを増やす必要がある。
  • そこで Chooser Table を組み合わせると、
    • 条件分岐(たとえば Speed が 0 のとき Idle、1~300 のとき Walk など)
    • キャラごとのアニメ差し替え(Proxy Table)
      を同時に外部データ化できる。
  • さらに Proxy Asset に Chooser Table を紐づける ことで、キャラクターごとに異なるロジックを足すことも可能になる。

メリット

  • 共通ロジックの ABP は最小限のノード構成のまま拡張でき、
    キャラクターや動作ごとの差分は 外部アセット(Proxy Table / Chooser Table) で管理できる。
  • ABP 内部を複雑にせずに済むので、規模が大きくなるほど管理がラクになる
  • アーティストやゲームデザイナーが “データ(テーブル)を編集” して実装を進めやすい、
    いわゆるデータドリブンなワークフローを実現できる。

まとめ

  • Proxy Asset / Table は、UE5 の “Chooser” 機能の弱点(Evaluate Chooser の動的切り替え不可)を補完し、
    汎用・共通処理に対してキャラごとのデータ差分を外部管理できる強力な仕組み。
  • Chooser Table と組み合わせれば、条件分岐とキャラ差分をまとめて外部化することも可能。
  • 記事の最後では、さらに運用上の知見や Tips(Part3)に続くとしており、
    大規模プロジェクトでのアニメーション・イベント処理を楽にする方法として注目されている。

続きを読む