This document is about: QUANTUM 1
SWITCH TO

Blackboard

はじめに

Blackboard は、Bot SDKに含まれているとても便利なツールです。
独自のカスタマイズされたデータを保持するために使用できるキー/値のセットです。
エージェントごとに保持できるActionおよびDecisionの読み取り/書き込みメモリのようなものです。

Blackboardはデータアセットを使用して完全に定義されるため、設計者にとっても使いやすいです。
つまり、UnityプロジェクトでBlackboardを保存および整理することができます。

PS: Blackboardデータアセットは単なるメモリレイアウト定義であることを理解することが重要です。
同じメモリレイアウトを持つボット/エンティティが多数ある場合、それらはすべて同じBlackboardアセットを指すことができます。
これらのボットはそれぞれ、レイアウトに基づいて 独自のメモリ を持ちます。

シンプルな例

マップ上のいくつかのアイテムを集めるキャラクターがあるとします。
ActionとDecisionで特定のDecisionを下すために、キャラクターがすでに収集したアイテムの量を読む必要があります。
また、キャラクターが新しいアイテムを収集した際、アイテム数を増やす必要があります。
このために、整数型のItemsAmountという名前のエントリをBlackboardに作成し、ActionおよびDecisionコードで管理します。

Blackboardはエンティティごとに使用されるため、他のエンティティも別のエンティティのBlackboardに含まれるデータを読み取ることができます。

Blackboardアセットの作成

UnityのProjectタブのCreate->Quantum->Assets->AIBlackboardの下の右クリックメニューから新しいBlackboardアセットを作成できます。
こちらが初期のアセット状態です:

New Blackboard

あとは独自のエントリを作成し、その型と名前を設定するだけです。

Blackboard entries

現在Blackboardで対応されている型は次のとおりです:

  1. Boolean;
  2. Byte;
  3. Integer;
  4. FP;
  5. Vector2;
  6. Vector3;
  7. Entity Ref.

Blackboardを使用する

quantum_codeソリューションで、既にエンティティのコンポーネントとしてBlackboardを使用することができます。

C#

entity Hero[8] {
    use Transform2D;
    use DynamicBody;
    use Prefab;
    use AIBlackboardComponent;
    ...
}

重要!

Blackboardを実際にプロジェクトに使用する前に、コードで実行する必須設定があります。
特定のエンティティからBlackboardを返すメソッドがあり、Blackboard全体の設定が機能します。

もちろん、各ゲームにはBlackboardを使用する独自のエンティティがあり、これはゲームごとに異なります。
そのため、このメソッドをゲームに実装する必要があります。

これは非常に簡単なタスクです。quantum_codeソリューションで、 Entity.Blackboard.csという名前のファイルを開き、そこで宣言された一意のメソッドで、サンプルコードのコメントを外し、すべての" YourEntityType "をAIBlackboardComponentを持つ独自のエンティティタイプで置き換えます。
最終的には次のようになります:

C#

public unsafe partial struct Entity {
    public static AIBlackboardComponent*GetBlackboardComponent(Entity* entity) {
      switch (entity->_ref._type)
      {
        case EntityTypes.Hero:
          return &((Hero*)entity)->AIBlackboardComponent;
      }
      return null;
    }
  }

Blackboardアセットを参照する

AIBlackboardComponentには、Boardという名前のフィールドが含まれています。このフィールドはUnityで作成したBlackboardデータアセットを指します。
参照を作成するには、DBを使用するか、アセットリンクを使用します。

C#

hero->AIBlackboardComponent.Board = playerData.BlackboardLink;
// Or
hero->AIBlackboardComponent.Board = DB.FindAllAssets<AIBlackboard>()[0];

エンティティの個人的なBlackboardからデータを読み書きするのにはこれで十分です。
しかし、読み取り/書き込みスニペットの説明に進む前にもう一つ便利なアセットをご紹介します。

Initializing Blackboardコンポーネントメモリ

静的コンテキストではBlackboardに保管された異なるデータの種類と量が作成されない(コード生成などによってなど)ため、各コンポーネントのメモリはフレームにUserDataとして保管する必要があります(その他のエンティティやコンポーネントのようにゲームステート上に保管するのとは逆です)。

すべてのBlackboardコンポーネント用に、エンティティが作成された後で、 Frame.CreateBlackboardMemory()を呼び出しメモリを確保する必要があります。後になってFrame.DestroyBlackboardMemory()を呼び出しエンティティが破壊される前にメモリを解放しておくことが大切です。"Failed to allocate memory(メモリ割り当てに失敗しました)"というメッセージが表示された場合は、blackboardメモリの解放を忘れている可能性が高いでしょう。

C#

var hero = f.CreateHero();

// Call CreateBlackboardMemory before further configuration of the blackboard component.
f.CreateBlackboardMemory(&hero->AIBlackboardComponent);

// Continue the initialization.
var data = f.GetPlayerData(player);
if (data.BlackboardInitializer.Instance != null) {
    AIBlackboardInitializer.InitializeBlackboard(f, &hero->AIBlackboardComponent, data.BlackboardInitializer.Instance, null );
}

// Call DestroyBlackboardMemory before destroying the hero
f.DestroyBlackboardMemory(&hero->AIBlackboardComponent);
f.DestroyHero(hero);

Blackboard Initializerアセット

このアセットは、Blackboardの初期化を支援することを目的としています。
Blackboardのエントリをミラーリングし、エントリごとに初期化中にBlackboardに適用されるデフォルト値を定義できます。

Blackboard Initializerアセットを作成するには、[プロジェクト]タブを右クリックして、Create->Quantum->Assets->AIBlackboardInitializerの順に選択します。
初期状態は次のようになります:

Blackboard Initializer Default

次に、このアセットによって初期化されるBlackboardアセットを選択する必要があります。
Blackboardのエントリは初期化子アセットに表示されます。
そのエントリの初期値を定義する場合は、Add Fieldボタンをクリックします。
次のようになります:

Blackboard Default Entry

これで、シミュレーションコードで、初期化アセットからAIBlackboardComponentを初期化することを選択できます。
そのために必要なコードスニペットはこちらです:

C#

AIBlackboardInitializer.InitializeBlackboard(f, &guy->AIBlackboardComponent, data.BlackboardInitializer.Instance, null);

data.BlackboardInitializerは、BlackboardInitializerアセットへの単なるアセットリンクであることに注意してください。
DB.FindAsset<AIBlackboardInitializer>("Guid");を使用して読み込むこともできます。

PS: 必要に応じて、Blackboard Initializer Assetに基づいてBlackboardコンポーネントを初期化するサンプルコードを含むBot SDK Sample をご覧ください。

エンティティのリスポーン時にBlackboardを再初期化する

一部のエンティティを破棄し、再スポーン中などに同じエンティティを再度作成すると、そのエンティティのBlackboardには以前のライフサイクルに関連する値が既に含まれています。

そのため、生成されたエンティティのBlackboardデータを再初期化して、古いデータが使用されないようにすることをお勧めします。
このケースでは BlackboardInitializerアセットが非常に便利です。
生成されたすべてのエンティティに対してAIBlackboardInitializer.InitializeBlackboard()を実行するだけです。

Blackboardの読み取りと書き込み

エンティティのBlackboardにデータを 読み書き するには:

C#

var bb = Entity.GetBlackboardComponent(e);

// 読む
var value = bb->Board.GetValue("someKey");

// 書く
bb->Board.Set("someKey", value, frame, bb);

PS.: バージョン1.0 RC2以降のボットSDKには、Blackboardに対して読み取り/書き込みを行うサンプルのAction/Decisionが既に付属しています。
quantum_codeソリューションで、BotSDK/Samplesフォルダーに移動し、IncreaseBlackboardInt.csSetBlackboardInt.csHFSM.CheckBlackboardInt.csファイルを確認してください。

Visual EditorのBlackboard

Visual EditorにはすでにBlackboardのサブメニューが付属しています。
Blackboard Variablesという名前で、左側のパネルからアクセスできます。

Blackboard Visual Editor Empty

+ ボタンを押して、新しいBlackboardエントリを作成することができます。
新しいエントリを作成するときは、その名前、型を通知し、デフォルト値で初期化するかどうかを指定する必要があります。デフォルト値で初期化する場合は、値を指定します。

Blackboard Visual Editor Creating Entry

多くの型を持つ沢山のエントリを持つことができます。それらのすべてがデフォルト値を持つ必要はありません:

Blackboard Visual Editor Many Entries

これで、プロジェクトをコンパイルするたびに、フォルダAssets/Resources/DB/CircuitExport/Blackboard_Assetsに2つの追加のデータアセットが自動的に作成されます。

  • コンパイル済みHFSMによって定義されたエントリをもつBlackboardアセット。
  • コンパイル済みHFSMによって定義されたエントリとデフォルト値をもつBlackboard Initializerアセット。
Blackboard Assets

これらはすでに情報の読み取り/書き込みに使用できます。

Blackboardノード

変数がある場合、グラフにドラッグ&ドロップしてノードを作成できます。これらのノードには常にKeyValueという、2つのアウトバウンドスロットがあります。

Blackboard Assets

KeyスロットはAIBlackboardValueKey種類のフィールドにリンクするため使用されます。このフィールドは、取得・設定する変数のキーを通知するときにハードコードされた文字列を置き換えるのに使用します。そしてもちろん、ハードコードされたキーを削除することで、リライアブルでのコードの柔軟性が上がります。Before/Afterの取得・設定を分析してみましょう。

C#

var bb = Entity.GetBlackboardComponent(e);

// -- BEFORE --
// To read
var value = bb->Board.GetValue("someKey");

// To write
bb->Board.Set("someKey", value, frame, bb);


// -- AFTER --
public AIBlackboardValueKey PickupsKey;
// To read
var value = bb->Board.GetValue(PickupsKey.Key);

// To write
bb->Board.Set(PickupsKey.Key, value, frame, bb);

この新しいフィールドが宣言されると、Visual Editorへ移動して、Blackboard Nodeの Keyスロットをつなげることができます。例:

Blackboard Assets

Keyスロットをこのように使用する他、Valueスロットをつなげて同じ種類のフィールドを定義することも可能です。左のパネルで定義されているDefault値はアセットにベイクする値となります。

Blackboard Assets

Blackboardおよびそのノードを使用するタイミング

いつでも好きな時に使用して、ランタイム中に変更する可能性のあるデータのプレエンティティを保管します。変わることのない値を定義する必要がある場合は、代わりにコンスタントパネルを使用してみてください。

Blackboardのフレームメソッド

Blackboardは、次のFrameの部分メソッドを実装しています: InitUser FreeUserAllocUser、および CopyFromUser

ゲームにこれらのメソッドの実装が既に含まれている場合、競合が発生します。
これを回避するには、コンパイラシンボル USE_BLACKBOARD_FRAME_METHODSを設定します。
これにより、Blackboardが部分メソッドを実装できなくなります。
これらのメソッドごとに、代替メソッドがあります。
それらは、InitBlackboardFreeBlackboardAllocBlackboardCopyFromUserBlackboardSerializeBlackboardDeserializeBlackboardです。

これらすべてのメソッドが、フレームの同等のメソッドによって呼び出されることを確認する必要があります。

Back to top