Page 1 of 1

TaskGraphAsset内で使用したNodeComponentが初期化されるタイミング

Posted: 2025/04/05 20:47
by duckmealGame
お世話になっております。

NodeComponentをTaskGraphAsset内で使用した場合に、メンバ変数がシーン切り替え後も初期化されない現象を確認しました。
同じNodeComponentをLogicBehaviorやLogicAsset内で直接使用した場合には発生しませんでした。

作成したグラフの一部を後からTaskGraphAsset化した場合、元と異なる動作になる可能性があります。
そのため、もし可能でしたらLogicBehavior(LogicAsset)内のNodeComponentが初期化されるタイミングで
そのLogicBehavior(LogicAsset)が参照しているTaskGraphAssetも初期化されるような仕組みをご検討いただけますと幸いです。
ただ、運用やスクリプティングで対処できる内容でもありますので、
他のユーザーの方々への影響が大きいようでしたら現状のままでも問題ございません。


■環境
Unity:6000.0.40f1
LogicToolkit:1.8.1


■再現方法
再現用のコードは以下になります。
Unityエディタからシーンを再生し、その後同じシーンをSceneManager.LoadSceneAsync()でロードすると、
TaskGraphAsset内で使用したServiceComponentのみメンバ変数valueが初期化されずtrueが出力されました。

メンバ変数valueの初期値を出力するServiceComponent

Code: Select all

using LogicToolkit;
using UnityEngine;

internal class TestServiceComponent : ServiceComponent
{
    [SerializeField]
    LogicToolkit.InputField<string> name;
    private bool value = false;
    protected override void OnActivated()
    {
        base.OnActivated();
        Debug.Log($"OnActivated({name.Value}):{value}");
        value = true;
    }
}
実行結果(下3件がシーン再読み込み後の結果)
onactivated-test.png
onactivated-test.png (56.29 KiB) Viewed 432 times
再現に使用したノードグラフ
onactivated-test2.png
onactivated-test2.png (35.74 KiB) Viewed 432 times
再現に使用したTaskGraphAsset(InvokeTest)
onactivated-test3.png
onactivated-test3.png (33.1 KiB) Viewed 432 times

私の場合はステージのリトライ機能をシーン再読み込みで実現しており、
再読み込み後にComponentNodeのメンバ変数をリセットしたかっただけなので、
以下のようにLogicPlayerが削除されたらリセットすることで対処できました。

LogicPlayerが削除されたらメンバ変数を初期化するServiceComponent

Code: Select all

using LogicToolkit;
using UnityEngine;

internal class TestServiceComponent : ServiceComponent
{
    [SerializeField]
    InputField<string> name;
    private bool value = false;

    MonoBehaviour _parent;
    protected override void OnActivated()
    {
        base.OnActivated();
        if (_parent == null)
        {
            Initialize();
            _parent = this.Player;
        }
        Debug.Log($"OnActivated({name.Value}):{value}");
        value = true;
    }

    private void Initialize()
    {
        value = false;
    }
}

Re: TaskGraphAsset内で使用したNodeComponentが初期化されるタイミング

Posted: 2025/04/05 23:33
by caitsithware
ご意見ありがとうございます。

まず現在の仕様をまとめますと以下の通りとなっております。
  1. 関数グラフを実行するタイミングで関数グラフのインスタンス化を行っている。
  2. 同じ関数グラフインスタンスは一度作成されると使いまわされる(オブジェクトプール)。
  3. 各フィールドの初期値を内部保持しておらず、使いまわす際に自動的には初期値に戻せない。
この2.と3.により2回目以降の実行時に前回の値が残ってしまっているということになります。

確かに、グラフのインスタンスの有効時(インスタンスを作成した時やプールから取り出した時)と無効時(プールにしまう時)に初期化&解放できる仕組みが必要ですね。
今後の更新で対応するように検討いたします。

Re: TaskGraphAsset内で使用したNodeComponentが初期化されるタイミング

Posted: 2025/04/06 11:04
by duckmealGame
ご回答ありがとうございます。
初期化&解放の仕組みにつきまして、ご検討のほど何卒よろしくお願いいたします。

重ねての質問となり恐縮ですが、関数グラフがシーン内の複数のLogicPlayerから参照されている場合、
それらのLogicPlayerは同じプールを共有しているのでしょうか。
現在実装中のコードでは初期化処理でLogicPlayerの親のオブジェクトのコンポーネントを取得しキャッシュしております。
そのため、シーンのロードを挟まない場合でもプールされたインスタンスが他のLogicPlayerで使われるようでしたら、
別のLogicPlayerから実行された(=親オブジェクトが変わった)ときにコンポーネントをキャッシュし直すようコードの見直しが必要と考えております。
以上の点についてご教示いただけますと幸いです。

Code: Select all

using LogicToolkit;
using UnityEngine;

internal class TestServiceComponent : ServiceComponent
{
    [SerializeField]
    InputField<string> name;
    private Rigidbody2D _body;

    MonoBehaviour _parent;
    protected override void OnActivated()
    {
        base.OnActivated();
        // LogicPlayerが削除されていたら新しいシーンとみなし再度初期化する
        // もし別のLogicPlayerとインスタンスが共有されるなら_parent != this.Playerとする必要がある
        if (_parent == null)
        {
            Initialize();
            _parent = this.Player;
        }
        // ここでRigidbody2Dを操作するスレッドを起動(省略)
    }

    private void Initialize()
    {
        // LogicPlayerの親オブジェクトのRigidBody2Dをキャッシュする
        _body = this.Player.GetComponentInParent<Rigidbody2D>();
    }
}

Re: TaskGraphAsset内で使用したNodeComponentが初期化されるタイミング

Posted: 2025/04/06 12:12
by caitsithware
はい。
どこで使用するかに関わらずプールは共有され、同じ関数グラフアセットのインスタンスは使いまわされます。
行いたいことがコンポーネントのキャッシュでしたらthis.Playerが変わっている場合にコンポーネントを再取得していただければ問題ないかと思います。

Re: TaskGraphAsset内で使用したNodeComponentが初期化されるタイミング

Posted: 2025/04/07 11:59
by duckmealGame
やはりプールは共有されているのですね。メモリ効率の観点からそのような仕様かと想像しておりました。
Playerが変わるケースも考慮してコンポーネントの取得処理を調整するようにいたします。
おかげさまで疑問点が解消されました。
ご丁寧なご回答、誠にありがとうございました。