This document is about: QUANTUM 3
SWITCH TO

スニペット

Bot SDKで使用できる便利なコードスニペットを紹介します。

Compound Agents(複合エージェント)

同じエージェント内で複数のAIエージェントコンポーネントを動作させると便利なこともあります。
例えば、あるHFSMはエンティティの移動のみを担当し、別のHFSMはターゲットに対する攻撃のみを担当する場合などです。
同じエージェント内で異なるAIモデルを混在させる(例:動作の一部をHFSMで処理し、別の部分をBTで処理する)ことも可能です。

こういった構成が必要な場合、エンティティに複数のメモリストレージを用意すると良いでしょう。Bot SDKでは、AIBlackboardコンポーネントを使用するのが一般的(必須ではない)です。

以下のコードスニペットは、単一のエンティティに複数のコンポーネントを追加して、「複合エージェント」を持つエンティティを作成する方法を示します。

複合エージェントコンポーネントの作成

DSL(任意の.qtnファイル)に、以下のコンポーネントを追加してください。
コンポーネントは、ゲーム固有の意味のある名前を付けるか、汎用的な方法でリストに追加してください。
今回の例では、エンティティの移動のみを担当するHFSMと、攻撃を担当するHFSMを用意します。

Qtn

component CompoundAgents
{
    HFSMAgent MovementHFSMAgent;
    HFSMAgent AttackHFSMAgent;

    AIBlackboardComponent MovementBlackboard;
    AIBlackboardComponent AttackBlackboard;

    AssetRefAIBlackboardInitializer MovementBBInitializer;
    AssetRefAIBlackboardInitializer AttackBBInitializer;
}

AIContextの拡張

AIContextUserの部分実装を作成します。今回の例におけるコンテキストは「更新されたエンティティが与えられた時、どのHFSMが更新されるのか?どのブラックボードを使用するのか?」などになります。

C#

namespace Quantum
{
  public unsafe partial struct AIContextUser
  {
    public readonly AIBlackboardComponent* Blackboard;
    public readonly HFSMAgent* HFSMAgent;

    public AIContextUser(AIBlackboardComponent* blackboard, HFSMAgent* hfsmAgent)
    {
      Blackboard = blackboard;
      HFSMAgent = hfsmAgent;
    }
  }
}

エージェントとブラックボードの初期化

今回の例ではISignalOnComponentAdded<CompoundAgents>内で初期化していますが、どこで行うかは開発者の自由です。

C#

public void OnAdded(Frame frame, EntityRef entity, CompoundAgents* compoundAgents)
{
    // エージェントの初期化
    HFSMRoot hfsmRoot = frame.FindAsset<HFSMRoot>(compoundAgents->MovementHFSMAgent.Data.Root.Id);
    HFSMManager.Init(frame, &compoundAgents->MovementHFSMAgent.Data, entity, hfsmRoot);

    hfsmRoot = frame.FindAsset<HFSMRoot>(compoundAgents->AttackHFSMAgent.Data.Root.Id);
    HFSMManager.Init(frame, &compoundAgents->AttackHFSMAgent.Data, entity, hfsmRoot);

    // ブラックボードの初期化
    AIBlackboardInitializer initializer = frame.FindAsset<AIBlackboardInitializer>(compoundAgents->MovementBBInitializer.Id);
    AIBlackboardInitializer.InitializeBlackboard(frame, &compoundAgents->MovementBlackboard, initializer);

    initializer = frame.FindAsset<AIBlackboardInitializer>(compoundAgents->AttackBBInitializer.Id);
    AIBlackboardInitializer.InitializeBlackboard(frame, &compoundAgents->AttackBlackboard, initializer);
}

各エージェントの更新

AIContextを作成し、HFSMとブラックボードをそれぞれ設定したら、更新を実行します。

C#

public override void Update(Frame frame, ref Filter filter)
{
    HFSMData* movementData = &filter.CompoundAgents->MovementHFSMAgent.Data;
    HFSMData* rotationData = &filter.CompoundAgents->RotationHFSMAgent.Data;
    HFSMData* attackData = &filter.CompoundAgents->AttackHFSMAgent.Data;

    AIContext aiContext = new AIContext();

    AIContextUser movementContext = new AIContextUser(&filter.CompoundAgents->MovementHFSMAgent, &filter.CompoundAgents->MovementBlackboard);
    aiContext.UserData = &userData;
    HFSMManager.Update(frame, frame.DeltaTime, movementData, filter.EntityRef, ref aiContext);

    AIContextUser rotationContext = new AIContextUser(&filter.CompoundAgents->RotationHFSMAgent, &filter.CompoundAgents->RotationBlackboard);
    aiContext.UserData = &userData;
    HFSMManager.Update(frame, frame.DeltaTime, rotationData, filter.EntityRef, ref aiContext);

    AIContextUser attackContext = new AIContextUser(&filter.CompoundAgents->AttackHFSMAgent, &filter.CompoundAgents->AttackBlackboard);
    aiContext.UserData = &userData;
    HFSMManager.Update(frame, frame.DeltaTime, attackData, filter.EntityRef, ref aiContext);
}

AIContextからHFSM固有のブラックボードを取得

以下は、コンテキスト固有のブラックボードに書き込みを行うアクションのスニペットです。どのブラックボードでも動作するため、疎結合なコードになっています。そのため、どのブラックボードを使用するかを判断するためのボイラープレートコードを、アクションに追加する必要はありません。

ここでは、独自のコンテキスト型に変換する拡張メソッドを使用しています。

C#

namespace Quantum
{
    [System.Serializable]
    public unsafe class WriteBlackboardCompound : AIAction
    {
        public int Value;

        public override void Update(Frame frame, EntityRef entity, ref AIContext aiContext)
        {
            aiContext.UserData().Blackboard->Set(frame, "TestInteger", Value);
        }
    }
}
Back to top