This document is about: QUANTUM 2
SWITCH TO

Utility Theory(Beta)


Available in the Gaming Circle and Industries Circle
Circle

はじめに

Bot SDKのUtility Theoryはまだベータバージョンです。予定通り製品化し、将来のバージョンではAPIとパフォーマンスが改善されます。

概要

Utility Theory AIは、数学的モデリングに強く基づいた意思決定のためのアーキテクチャを提供します。独自のUTエージェントを作成する際には、UnityのAnimationCurvesを利用して定義された応答曲線を常に作成、調整することになります。

すべてのレスポンスカーブには、ゲームロジックで定義された入力値が与えられ、その出力はここではスコアと呼ばれています。このプロセスを経て、UTエージェントは、その目盛りでより役に立つ選択肢を知り、それに応じて行動を開始します(そのため、Utility Theoryと呼ばれています)。

これに比べて、Utility Theoryは、Finite State MachinesやBehaviour Treesとは大きく異なり、Goal Oriented Action Planningに近いものがあります。このAI技術は、FSMのTransitionやBTのLinksのように、すべてのものの間に非常に「硬い」関係を作ることをユーザーに義務付けていないため、没入型の行動を作り出すことができます。

UTのAIの最大の利点は、レスポンスカーブによって、非常に柔軟な意思決定空間を定義することができることです。例:

  • 例えば、HFSMの判断に距離チェックがあったとすると、「敵との距離が5より小さい場合、エージェントは逃げなければならない」となります。これは「見えない壁」のようなもので、意思決定が極端に変化する「二値のしきい値」(「逃げない」から「逃げる」へ)を作り出しています。
  • UTのレスポンスカーブでは、カーブの本体で意思決定をスムーズに行うことができます。それは、効用値の最大が5であるような直線的な曲線になります。距離の値が5に近づくにつれて、逃げるという選択肢が直線的に重要になることを意味します。あるいは、指数関数的に成長する曲線で、逃げるという決断がより早く有用になるようにすることもできます。

Utility Theory AIの新規作成

エディタのトップバーにある(+)ボタンをクリックして、オプションの「Utility Theory」を選択します。

create bt document
その後、BTファイルを保存するかどうか確認されます。 お好きな場所に保存してください。 これにより、**ビジュアルエディターで行った**作業を保存するためのデータアセットが作成されます。
ut file

:ここで決めた名前は、ビジュアルエディターで行った作業をコンパイルする際に、さらに生成される別のデータアセットの名前になります。 これは、Quantumシミュレーションで実際にBotを動かすためのデータアセットになりますので、すでに示唆に富んだ名前を選ぶことができます。これは、Quantumシミュレーションで実際にBotsを動かすためのデータアセットになりますので、すでに示唆に富んだ名前を選ぶことができます。

initial node

ファイルを保存すると、Bot SDKのメインウィンドウには、Consideration Nodeという1つのノードが表示され、作業を開始することができます。

Consideration Node

これはメインのノードタイプで、UT Agentにとって重要なデータをすべて含んでいます。

Considerationの中では、特定のConsiderationが分析するResponse Curvesを定義することができます。これらの曲線は、0~n個のスコア値からなり、それらが掛け合わされ、その結果が「その検討を実行することの効用」と呼ばれるものになります。

まず、すべての検討事項が評価されます。これは、入力が曲線に入れられ、結果としてスコアが得られることを意味します。そして、最大のScoreを持つ検討事項が、その特定のフレームで実行される検討事項として選択されます。

つまり、ユーザーは、頻繁に評価される多くのConsiderationノードを作成し、その実行がエージェントを行動に移すことになるということです。

Considerationのフィールドを1つずつ分析してみましょう。

ut consideration nodes
## Base Values
consideration base values
  • **Base Score (FP):**応答曲線の結果を合計したもの。これを使って、与えられた考慮事項に対する固定の効用値を与えます。

  • Cooldown (FP): Considerationが選択されて実行されたとき、CooldownはそのConsiderationが可能な選択肢から何秒後に破棄されるかを定義します。

  • **Momentum Amount (Int):**Considerationが選択された場合、Momentumはそのランクをこのフィールドで定義された値まで増加させます。このフィールドを使用して、どのConsiderationがより多くのフレームで実行され続けるべきかを定義してください(絶対的な有用性の値が増加します)。詳細については、以下のランキングのトピックを参照してください。

  • **Momentum Decay (Int):**ConsiderationがMomentumに乗っている(つまり、Momentumによってランクが上がっている)場合、そのランク値は毎秒、このフィールドで定義された量だけ減少します。勢いをつけたいときの速さを表すのに使います。

これらの基本値は必ず使用しなければならないものではありませんが、いくつかの機能が追加されており、非常に便利です。

アクション

actions

これは、ユーザーがアクターを動かすために、ゲームの状態に変化を与えることができるノードタイプです。アクターをポイントAからポイントBに移動させたり、攻撃したり、スキャンしたりすることは、すべてActionsで行うことができます。

UTのActionsは、HFSMやGOAPで使われているものと同じです。そのため、共有のドキュメントがあります。こちらをご確認ください。

Considerationのアクションを編集するには、「アクション」のエリアをダブルクリックしてください。その中で、以下の場面のアクションをリンクすることができます。

  • On Enter:そのConsiderationが最も有用なものとして選択され、実行され始めたときに実行されます。
  • On Update:そのConsiderationがまだ最も有用なものとして選択されている間、毎フレーム実行されます。
  • On Exit:そのConsiderationが前のフレームで選択されたが、現在のフレームで再び選択されなかった場合に実行されます。

ランキング

ranking

There are two main ways of choosing the Considerations. Here defined as:

  • Absolute Utility: 最高のランク値を持つConsiderationは、低いランク値を持つConsiderationよりも絶対的に優先されます。つまり、スコアを計算する際に、低いランク値のConsiderationは完全に無視されます。
  • Relative Utility: これはすでに説明したもので、応答曲線から評価されたスコアそのものです。

ランクの値は整数として計算されます。例として、5つの検討事項(A、B、C、D、E)があるとします。それらに対するランク値を仮定してみましょう。

  • A = 0;
  • B = 1;
  • C = 1;
  • D = 2;
  • E = 2.

つまり、そのフレームでは、ConsiderationDとEが絶対的な優先順位を持ち、それらを選択するために応答曲線を評価・比較することになります。ConsiderationA、B、Cは無視されます。

ランク値を変更するには、主に2つの方法があります。ランク値は常にランタイムに定義され、フレームごとに変更することができます。この動的なランク値は、ユーザーが自分のゲームに特有のランキングロジックを事前に定義し、必要に応じてConsiderationのサブセットを優先させるために役立ちます。

例を挙げると、シューティングゲームで、戦闘をするか、回復のために走るかを選択できるとします。それぞれの可能性に応じて、多くのConsiderationがあるかもしれません。ある瞬間に何がより重要であるかをフィルタリングするためには、ランクの値を増やしてUTエージェントにそれを表現することが有用であるかもしれません。

Rank値を定義する方法は2つしかありません。

独自のRankロジックを定義する: Quantumソリューションでは、整数値を返すロジックを実装するために、AIFunctionIntクラスを継承することができます。以下はそのコード例です。

C#

namespace Quantum
{
  [System.Serializable]
  public unsafe class SampleRank : AIFunctionInt
  {
    public override int Execute(Frame frame, EntityRef entity = default)
    {
      // Add here any logic to calculate the desired Rank
      return 0;
    }
  }
}

例えば、ゲームの状態やブラックボードの値を読み込んで、エージェントが現在危険な状態にあるかどうか、つまり敵が近くにいるか、LoSがあるかどうかを確認するIsDangerRankがあるとします。危険が検出された場合は、Rank値を10にして返します。つまり、このRankを持つConsiderationの絶対的な優先度が非常に高くなります。危険がない場合は、単に 0 を返します。

Quantumコードがコンパイルされると、Functionノードがコンテキストメニューに表示されるようになります。Rankノードにアクセスするには、ConsiderationノードのRankエリアをダブルクリックします。

また,以下のトピックで説明するように,Momentumの概念に基づいてRankの値を変更することも可能です。

モーメンタム

Considerationの基本値を使用することで、モーメンタムを指定することができます。これは、Considerationが選択されたときに、自動的にそのConsiderationのランクを上げるものです(On Enterロジックに似ています)。

モーメンタムを持たせる目的は、エージェントが頻繁に考えを変えないようにするためです。Considerationは常に再評価されるため、エージェントがあるタスク(ターゲットを追いかけるなど)をやり始めても、すぐに別のタスク(基地を守るために戻るなど)に切り替えてしまい、エージェントが何もしないまま終わってしまう可能性があるからです。

モーメンタムをつけることで、Considerationのランクを上げるように指定することができます。これにより、選択したタスクに対する「コミットメント」と呼ばれるものが生まれます。モーメンタムによって生成されたランク値は、動的に計算されたものよりも高い優先度を持ちます。

Momentumをつけましたが、いつまたリセットされるのでしょうか?Momentumをゼロに戻すには、主に2つの方法があります。

  • Momentum Decayという基本値を使って、Momentumのランクを1秒ごとに下げるための値を指定することができます。したがって、運動量の値をどのように設定するかによって、1秒間だけコミットすることも、何秒間もコミットすることもできます。それはあなた次第です。
  • Commitmentチェッカータイプでは、Momentumのランクをキャンセルすることも可能です。このタイプはブール値を返し、Momentumをキャンセルするタイミングを指定することができます。Commitmentチェッカーを作成するには、BoolFuncを継承し、そのExecuteメソッドを実装します。値が FALSE を返す場合は、モーメンタムがキャンセルされるべきであることを意味します。

C#

namespace Quantum
{
  [System.Serializable]
  public unsafe class SampleCommitment : BoolFunc
  {
    public override bool Execute(Frame frame, EntityRef entity = default)
    {
      // Implement your checking logic here
      return false;
    }
  }
}

Quantumコードがコンパイルされると、Func ノードがコンテキストメニューに表示されるようになります。Commitmentノードにアクセスするには、Commitmentエリアでダブルクリックします。

これは、ある条件が満たされるまで、高いRank値を何度も維持したいという場合に使用します。例えば、他のキャラクターに追従するエージェントがいる場合、キャラクターが追従を開始した時点で大きなランク値を持つようにし、高いランク値を維持するためにMomentum Decayに0を設定し、エージェントが本当に目標に到達できるかどうかをチェックするCommitment を追加します(単純に距離をチェックするだけでもよいでしょう)。ターゲットが遠すぎる場合は、Falseを返すことでConsiderationのRankが再び0になり、他のものがエージェントにとってより有用である可能性が高まります。

とはいえ、これらのテクニックのどちらも使わなければならないわけではありませんし、それらを排他的に使う必要もありません。ConsiderationにMomentumを追加して、自然なMomentum DecayとCommitment Checkerの両方を持つことができます。すべてはあなた次第なのです。

レスポンスカーブ

response curves

これが、このAI技術の核心です。意思決定は、カーブを定義し、スコアを付け、それらを掛け合わせ、そのフレームでより有用なものを得るために結果を組み合わせることに基づいています。

カーブを定義するために、UnityのAnimationCurveシステムを再利用していて、それを決定論的バージョンにコンパイルしてFPAnimationCurveとしています。

独自のカーブを作成する場合、ここで最も重要なのは、あるconsiderationをどのように評価したいかを正しく表現するカーブを使用することです。特定の値に非常に近い場合にのみ重要となる動作しますか?重要性は直線的に増加しますか?指数関数的に増加するのでしょうか?または特定の範囲内ではゼロで、ある時点を境に直線的に増加し始めますか?

独自のカーブを作成したり、プリセットから選択したり、新しいプリセットを作成したりすることができます。

非常に重要な概念 曲線のY軸(結果のスコア)は正規化(つまり0~1の間)されるべきです。曲線の結果は乗算されるため、比率を維持する必要があります。そうしないと、曲線の結果はお互いに比較できなくなり、UTの原則を破ってしまうことになります。

ここでは、プリセットとして保存されたレスポンスカーブをSpellcaster Sampleで使用した場合のサンプル画像をいくつか紹介します。

response curves sample

1つのConsiderationに対してより多くのレスポンスカーブを定義するには、カーブエリアをダブルクリックします。曲線コンテナが表示されます。

マウスの右ボタンを使って、新しい応答曲線を作成します。

create curve

曲線をクリックすると、エディタウィンドウが表示されます。

edit curve

どのカーブを使用するかは、ゲーム特有のニーズに完全に依存しています。そのカーブにどのような入力を入れるかと、その入力値の変化をどのように「ユーティリティー」の値に反映させるかによって決まります。

例を挙げてみましょう。

  • 回復を考慮:最大体力が10で、体力が5以下になると回復を望むようになるエージェントがいるとします。つまり5以上の値に対しては、効用曲線の結果は0になるはずです。そして、ゼロ以下の値に対しては、回復の効用が非常に速く増加するようにします。これを正しく表現した曲線と言えるでしょう。

    heal curve
  • 攻撃を考慮:シナリオ上に少なくとも1体の敵がいる場合にのみ攻撃を希望するエージェントがいるとします。敵が1人でも2人でも10人でも構いません。これは、ゼロからすぐに1になる「二項閾」曲線です。曲線の持つ表現力の一部が失われていますが、それでもある種の分析には有効です。曲線は次のようになります。

    attack curve

    そして、1つのConsiderationには複数のレスポンスカーブがあるのが普通です(必須ではありません)。必要に応じて新しいカーブを追加していきましょう。カーブで使用されている入力を読み取るために追加されるオーバーヘッドに注意してください(詳細は次のトピックで説明します)。

    ルートビューからレスポンスカーブを見て編集することができます。

curves root view

レスポンスカーブの入力

入力値は、ゲーム特有のものであるため、カスタムユーザーロジックによって定義されます。エンティティのコンポーネントから得られる体力値であったり、ブラックボードに保存されているデータであったり、センサーシステムから収集されたものであったりします。

独自の入力タイプを作成するには、FPFuncを継承した新しいクラスを作成し、そのExecuteメソッドを実装します。

C#

using Photon.Deterministic;

namespace Quantum
{
  [System.Serializable]
  public unsafe partial class InputEntityHealth : FPFunc
  {
    public override FP Execute(Frame frame, EntityRef entity = default)
    {
      // Read the current health from the component which is in the Agent's entity
      if(frame.TryGet<Health>(entity, out var health) == true)
      {
        return health.Current;
      }
      else
      {
        return 0;
      }
    }
  }
}

Quantumコードがコンパイルされると、コンテキストメニューにFuncノードが表示されます。レスポンスカーブノードにアクセスするには、ConsiderationノードのResponse Curvesエリアをダブルクリックします。

Considerationのリンク

1つのConsiderationを他のConsiderationとリンクさせ、親子関係を作ることができます。

この場合、子のConsiderationは、特定のフレームで親のConsiderationが最も有用なものとして選択された場合にのみ評価されます。このような場合、子のConsiderationは、兄弟とのみ競合します。

これは主に2つの理由で役に立ちます。

  • パフォーマンスの最適化に役立ちます。戦うことが有用かどうかを分析する親のConsiderationを想像してみてください。子のConsiderationは、実際にどのような戦いの選択をするかを評価するものです。これらは、戦うことが有用になるまで計算されません。
  • また、パフォーマンスに関しては、親のConsiderationに含まれるカーブは、その子のすべてに対して暗黙に計算されないので、それらのカーブを何度も計算し直す必要がなくなります。
  • 親のConsiderationのカーブが子に複製される必要がないため、「文脈」の問題でConsiderationを整理するのに役立ちます。

Considerationをリンクするには、Considerationノードの出力スロットをクリックして、他の入力スロットにリンクするだけです。

linked considerations

Utility Theory AIをコンパイルする

作成したUTを実際に使用するためには、コンパイルを行う必要があります。

コンパイルには2つの方法があります。

compile buttons
  • 左のボタンは、現在開いているドキュメントのみをコンパイルするために使用します。
  • 右のボタンは、プロジェクトにあるすべてのAIドキュメントをコンパイルするために使用します。

UTファイルは、"Assets/Resources/DB/CircuitExport/UT_Assets "にあります。

ut asset

Botが使用するAIの設定

作成したAIを最終的に使用するには、コンパイルしたアセットを参照する必要があります。 GUIDに基づいてアセットを読み込むこともできますし、AssetRefUTRootを作成して目的のAIアセットを指すようにすることもできます。

referencing ai

Utility Reasoner

Utility ReasonerはQuantum側のメイン構造体で、UTのすべてのアーキテクチャを担当しています。Considerationをスコアリングして選択するための関連データをすべて保持しています。

Reasonerを特定のエンティティに結びつけて、自分のエージェントとみなしたい場合は、そのエンティティにUTAgentというコンポーネントを追加します。UTAgentにはすでにUtility Reasonerが含まれていますが、これはQuantumコードを使っても、Entity Prototype上で直接行っても構いません。

Prototype上で直接行う利点は、Unityから直接UTRootアセットを参照できるようになることで、これは便利なことです。

Reasonerは通常のQuantum構造体です。この構造体は、次のトピックで説明する UTAgent という名前のコンポーネントで使用されますが、Reasoner は DSL の global データの一部として宣言することもできます。UTのAPI全体では、EntityRef entityというパラメータがオプションとして用意されています。これは、RTSゲームでクリーチャーをどこで、いつ、どのようにスポーンさせるかを決定する「架空のプレイヤー」のような、より抽象的な概念に役立つ可能性があるということを意味しています。

グローバルデータの一部としてReasonerを使用するには、次のように宣言して参照します。

Unknown

// In any DSL file
global
{
    UtilityReasoner UtilityReasoner;
}

// In any other logic file, such as inside a System
f.Global->UtilityReasoner

初期化と更新

ここでは、UTAgentコンポーネントを使用して、Utility Reasonerを初期化する例を紹介します。

C#

UTManager.Init(f, &utAgent->UtilityReasoner, utAgent->UtilityReasoner.UTRoot, entity);

Utility Reasonerのアップデートについて。

C#

UTManager.Update(f, &utAgent->UtilityReasoner, entity);

フィールドの値の定義

Considerations、Inputs、Ranks、Commitmentsのフィールドに値を設定する際の選択肢については、こちらをご覧ください:フィールドの値の定義

AIParam

AIParam型の使用方法についてはこちらをご覧ください。これは、様々な方法で定義できるより柔軟なフィールドを持ちたい場合に便利です。設定は、手動またはBlackboard/Constant/Config Nodesでおこないます:AIParam

AIContext

エージェントコンテキストの情報をパラメータとして渡す方法の詳細については、以下を参照してください:AIContext

BotSDKSystem

Blackboardのメモリ解放など、一部の処理を自動化するためのクラスがあります。詳細は以下を参照してください:BotSDKSystem

Visual Editorコメント

Visual Editorにコメントを作成する方法についての詳細はこちらをご覧ください。Visual Editorのコメント

コンパイルのエクスポートフォルダの変更

デフォルトでは、Bot SDKのコンパイルによって生成されたアセットは、Assets/Resources/DB/CircuitExportフォルダに置かれます。エクスポートフォルダを変更するには、こちらをご覧ください。エクスポートフォルダの変更

保存される履歴のサイズの選択

Bot SDKファイルに保存される履歴エントリの量を変更することができます。この件についての詳細はこちらをご覧ください。 履歴保存数の変更

Back to top