共通コンセプト
複数のBot SDK AIモデルに共通のコンセプトについてはこちらを参照してください。
Actionを定義する
HFSMでは、すべてのステートの中にサブグラフがあります。GOAPでは、すべてのTaskにもサブグラフがあります。これらはstate/taskをダブルクリックでアクセス可能です。
これらのサブグラフで Actionを作成 できます。
サブグラフにアクセスする際、上部のバーに現在いる階層がどれだけ深いかを示すブレッドクラムが見られます。
これらのボタンを使用して階層の前段階に移動できます。
 
サブグラフに定義されている重要な「Actions Root」ノードがあります。
 
HFSMで
以下は実行するアクションのリストです:
- On Enter リストはHFSMがこのステートに入った際に実行されるべきアクションを定義します。;
- On Update リストはHFSMのアップデート時に毎回実行されるべきアクションを定義します。(通常、毎フレーム);
- On Exit リストはHFSMがこのステートを離れる際に実行されるべきアクションを定義します。
GOAPで
以下は実行するアクションのリストです:
- On Update リストはGOAPのアップデート時に毎回実行されるべきアクションを定義します。(通常、毎フレーム);
これらのアクションリストを定義するには、Actions Rootノードの右側にある矢印をクリックします。そして、いずれかのActionのインバウンドスロットをクリックするか、空白のスペースのどこかをクリックすることですぐに新しいActionを作成できます。以下を参照してください。
 
ここで大事なことは、リンクされたアクションをいくつでも必要なだけ定義できるということです。そしてその定義は同一のフレーム上で連続して実行されます。
矢印ボタンを使用してアクションをリンクしましょう。以下を参照してください。:
 
アクションの順番は好きなように入れ替えることができます。
アクションを削除したくお実行したくもない場合、切り離したアクションのセットにしておいて後で使用することもできます。
 
Actionフィールド値はインバウンドスロットをクリックすることで定義できます。
Enterを押して 変更を適用、Escを押して 変更を破棄します。
 
既にみた通り、Editorには事前に定義されたActionとDecisionが搭載されています。
これらはデベロッパが何か始める時のために作成されたものです。
自分のプロジェクトで特定のActionおよびDecisionを実装する必要があります。
その方法について確認しましょう。
Actionをコーディングする
新しいActionを作成するには、 quantum_code ソリューションを開く必要があります。
quantum.state プロジェクトで、AIAction抽象クラスから継承する全てのクラスを作成することができます。
これを行う際、Updateメソッドの実装が必要になります。これは自分のエージェント(HFSMでもGOSPでも)をアップデートする際は毎回呼ばれるメソッドです。
HFSMでは、定義した OnEnter、OnUpdate、OnExit のアクションリストで実行されるものです。例えば、HFSM Stateに入るときに、このコードもまた初期ステートでリストされたアクションのために実行されます。
新しいクラスを[Serializable]としてマークする必要があります。
C#
namespace Quantum
{
    [Serializable]
    public class IdleAction : AIAction
    {
        public override unsafe void Update(Frame f, Entity* e)
        {
            // insert the action code here
        }
    }
}
フィールド値を定義する
Bot SDKでは、コードでパブリックフィールドを宣言できます。そのため、これらのフィールドがVisual Editorに表示されています。HFSMではActionとDecisionコードで宣言でき、GOAPではActionで宣言できます。エディターではそのフィールドの値をユーザーが定義することができます。
これらの値の簡単な設定方法は、フィールドをクリックしてすぐに値をアサインすることです。ただし、そのほかにもオプションはあります。以下を参照してください。:
- Blackboardノードを使用する;
- Constantノードを使用する;
- Configノードを使用する
Blackboardノードについては既に Blackboard documentationで説明してありますので、 ConstantノードとConfigノードに飛びましょう。
Constantパネル
Constantは、左側のパネルから定義して使用できます。
Constantを定義した後、ConstantノードをとらえActionとDecisionのインバウンドスロットにリンクさせることができるようになります(HFSMで)。単一のConstantノードは多くの回数インプットとして使用でき、多くのフィールドを同じ値で簡単に定義できるようになります。
Constantのもう一つの大事な側面は、設定メニューでその値が変更されるといつでもそのConstantから派生したすべてのノードがそれに従ってアップデートされます。これにより、HFSMの様々なたくさんの部分の値を定義したり、後から変更することが簡単にできるようになります。
新しいConstantを定義するには、左側のメニューを使用し、Constantsタブの(+)マークをクリックします。
 
その後、Name、Type、Default Valueを選択して保存します。これでConstantが定義され、グラフビューにドラッグアンドドロップできるようになりました。インバウンドスロットには既にリンクされています。
 
Configパネル
同じHFSM/GOAPを使用する多くのエージェントに異なるconstant値を持たせることは可能です。これはたとえばBotをさまざまな難所に、ただし同じ挙動ロジックを持たせるように置きたいときなどに便利です。イージーモードでのシューターBotはリアクションタイムに「2秒」の値を持たせ、一方でハードモードではその値を「0.5秒」にします。Configパネルを使用すると、そのようなことが簡単にできるようになります。
Config値を作成するには、左側のパネルを使用します。この値はその後AIConfigAssetタイプの新しいデータアセットにコンパイルされます。コンパイルの後、<DocumentName>DefaultConfigという名前のアセットがAIConfig_Assetsフォルダ内のフォルダにできます。
これらのアセットは、constant値を検索するのにシミュレーションで使用します。
それでは簡単なConfigレイアウトを作成してみましょう:
 
スクラッチから新しいconfigフィールドを作成するほか、Configに変換したりまたその逆も行うことができます。
 
ドキュメントのコンパイル後、以下が結果としてできるアセットです。:
 
最終的にこちらのアセットからバリエーションをだして異なるconstant値を定義できるようにするには、Unityのプロジェクトタブで右クリックメニューを使用してCreate/Quantum/Assets/AIConfig配下に移動します。
これで、他のconfigアセットを検索してベースにしようとするとても簡単なconfigアセットが作成されます。その後、好きな値に変更できます。また、値を元のconfigアセット値に戻すには、ResetをクリックしてDefaultにします。
 
これらのconfigを使用するにあたり、方法がいくつかあります:
- configキーを通知しconfigタイプに対応する値を検索しつつconfigアセットから直接読み込む: - var myBoolean = myConfig.Get("Key").Value.Boolean;- タイプ: Integer、Boolean、Byte、FP、FPVector2、FPVector3、String 
- または、AIParam typeと一緒に使用する - 例: - まず、いずれかのAction/DecisionでAIParamFPフィールドを作成します。それをコンパイルし、Visual Editorで表示されるようにします; - その後、左側のパネルからConfig値をドラッグアンドドロップしてAIParamフィールドにリンクさせます; 
 
ドキュメントをコンパイルします。コード上で、AIParam APIを用いて値を検索します。これにより、パラメータとしてパスされたConfigアセットに対応したconfigの値がすでに検索されます。:
// Considering that the variable "AttackRange" is of type AIParamFP
FP rangeValue = AttackRange.Resolve(f, blackboard, myConfig);
ここでのカギは、Visual Editorにあるものに応じてコンフィグパネルを使用しデフォルトのConfigアセットを作成すること、そしてそのバリエーションを作成してAssetRefsを使用して自分のニーズに合わせてそれらをリンクすることです。
HFSMAgentには利便性のために特定のエージェント・エンティティに対してConfigアセットを参照するフィールドが既に搭載されています。自分のリファレンスを作成するのに、其方を利用いただけます。:
myEntity->HFSMAgent.Config = DB.FindAsset<AIConfig>("EasyLevelConfig");
AIParam
フィールド上の値のソースを定義する方法はいくつかあります。現在は、Blackboard、Constants、Configを使用することで完了できます。そのため、コード上でこれらの値を読む方法もいくつかあります。デベロッパがソースのタイプを別のもの(例:BlackboardノードからConstantノードへ)に変更する必要がある場合、ソースコード上の変更もまた必要となります。
ソースタイプの変更が便利なのはなぜでしょうか?
- 簡潔にするため、値を手動で定義することも可能です。;
- ランタイム中に値を変更することが可能な場合、Blackboardに保存できます。Blackboardノードを使用して定義してください。;
- 値を変更はせず、グラフの柔軟性を上げるためノードから値がでるようにする場合はConstantノードを使用して定義します。;
- 上記を適用するが、エージェントごとに変化を持たせる場合はConfigノードを使用します。
AIParamはソースが突然変更されるような状況で助けになるため作成されるタイプです。この使用方法について学ぶ前に値を読むためのコード間の違いを簡単に分析してみましょう。
フィールドの値をVisual Editorで手動で定義した場合、またはConstantノードで定義した場合、読むためのコードはとても単純です。:
C#
// In this case, the value is directly stored on the field itself, with no need for any extra code
public Int32 MyInteger;
Blackboardノードから値をとる場合:
C#
// Read the value from the blackboard asset
var value = blackboardComponent->Board.GetValue("someKey");
As for a Configノードの場合:
C#
// Read the value from the config asset
var myBoolean = myConfig.Get("Key").Value.Boolean;
Visual Editorで値ソースを変更した際にコードの変更を必要としないためには、自分のフィールドにAIParamタイプを使用します。その主な特徴を以下に記載します。:
- blackboardアセットとconfigアセットを受け取るResolveメソッドがあります。フィールドの値のソースが何かを把握することで、このメソッドは正しい値を直接返すか(手動でフィールドが定義された場合)、Blackboard/Configsから返します。値のソースタイプは必要な回数だけ変更でき、これが読むためのコードとなります。:
C#
public AIParamInt MyAIParam;
var value = MyAIParam.ResolveResolve(frame, blackboard, aiConfig);
- 現在のところ、8つタイプがあります。:
C#
AIParamInt, AIParamBool, AIParamByte, AIParamFP, AIParamFPVector2, AIParamFPVector3, AIParamString, AIParamEntityRef
- 内部的に、Visual EditorAIParamがどのように定義されたか、手で定義されているか、特定のノードから持ってこられたものなのか、既にチェックされています。
Visual Editor上で
ActionsとDecisionsでパブリックAIParamが宣言されると、Visual Editorで表示されこの値を手動もしくは特定のノードで定義することができるようになります。例として、public AIParamInt IncreaseAmountを考えてみます。:
 
カスタムEmum用AIParamを作成する
前述したタイプ以外に、Enums用のAIParamsを作成することも有用です。そのためには、必要とする特定のEnum用のAIParamタイプを作成しておきます。以下はコードスニペットです。
C#
// Considering this enum:
public enum BotType { None, HFSM, GOAP };
// Create a new AIParam class based on that enum:
[System.Serializable]
  public unsafe sealed class AIParamBotType : AIParam<BotType>
  {
    public static implicit operator AIParamBotType(BotType value) { return new AIParamBotType() { DefaultValue = value }; }
    protected override BotType GetBlackboardValue(IAIBlackboardMemory memory, AIBlackboardComponent* blackboardComponent, AIBlackboardValue value)
    {
      return (BotType)value.GetInteger(memory, blackboardComponent);
    }
    protected override BotType GetConfigValue(AIConfig.KeyValuePair config)
    {
      return (BotType)config.Value.Integer;
    }
  }
// Now, on your Actions/Decisions, you can just use your new type and it will already show up on the Visual Editor
public AIParamBotType BotType;
それから、以下の方法でエディタ上でEnumを設定することができるようになります:
- ドロップダウンメニューからEnumを選択する;
- または特定のノードでリンクする
ミュートする
各AIモデルには、ミュート が可能な特定のノードがあります。
ミュートは、コンピレーションプロセスのロジック部分を、何かを削除したりリンク解除したりすることなく無効にします。様々なAIモデルでミュートできる解消について分析していきましょう。:
HFSM固有
ミュート状態
ミュート状態へのトランジションは無視されます。また、この状態内のActionはすべて実行されません。
注意: ミュートされたデフォルトの状態でHFSMをコンパイルした場合、エラーが出ます。
Stateノードをミュートするには、Stateを右クリックし「Mute/Unmute State」を選択します。
ミュートされている間、トランスペアレントにレンダリングされます。
 
ミュートのトランジション
トランジションの行を右クリックし、「Mute/Unmute Transition」を選択するとトランジションをミュートできます。
トランジションはミュートされるとコンパイルの間認識されなくなります。
どのようなタイプのトランジションでもミュートできます。:共通のトランジション、 Any transition、Transition Set、Portalへのトランジション。
 
GOAPに固有
ミュートされたタスクは計画を立てるため可能なGOAPに追加されません。
Taskノードをミュートするには、Stateを右クリックして「Mute/Unmute Task」を選択します。
ミュートされている間トランスペアレントにレンダリングされます。
 
HFSMとGOAPの両用
Actionsをミュートする
Actionはすべて、その位置とアクションリストに関わらずミュートできます。
ミュートされたアクションはコンパイル時に認識されず、ミュートされたアクションに次のアクションがあった場合、続くアクションのみ実行されます。
いずれかのActionノードを右クリックすることでも操作は可能です。
 
Visual Editor Comments
Visual Editorへコメントの追加を行うのに便利な機能です。コメントを追加するには、いずれかのノード(States/Tasks/Actions/Decisions/Constantsなど)を選択し、コメントエリアを追加するにはGを押します。それから、"Comment"ヘッダーテキストをクリックして好きなように変更します。複数のノードにコメントを追加することもできます。複数のノードを選択するには、Windows OSでは「Ctrl」ボタンを、Mac OSでは「Command」ボタンを押したまま操作します。
 
   
  デフォルトで、Bot SDKにより生成されたアセットはAssets/Resources/DB/CircuitExportフォルダ内に置かれます。これは、Assets/Plugins/Circuit/CircuitScriptablesフォルダ内にあるSettingsDatabaseという名のアセットを選択することで変更できます。それから、Bot SDK OutputFolderフィールドを探し、好きなように変更します。ターゲットフォルダが既に作成済みであることを確認してください。
また、CircuitExportという親フォルダも常に作成され、この中にすべてのサブフォルダが作成されます。
 
  保存した履歴サイズを選択する
Bot SDKはデフォルトでvisual editorファイルに5つ履歴のエントリを保存します。AIドキュメントを開いたり閉じたりする場合、セッション間の履歴を残したいときには便利です。ただし、AIファイルのサイズは、AI circuitが大きくなるごとに保存している理例のエントリの量に応じて増大します。
保存する履歴エントリの数は、選ぶことができます。保存の必要がなければ0でも可能です。履歴が再読み込みされるのはAIファイルを再度開いたときのみです。これは、Unityを閉じる・開くときにも行われます。
履歴エントリの数を変更するには、Assets/Plugins/Circuit/CircuitScriptablesフォルダ内のSettingsDatabaseアセットを選択します。それから、Save History Countフィールドを探します。
