This document is about: QUANTUM 2
SWITCH TO

Shared Concepts

有些概念對於超過一個的機器人SDK AI模型而言是相同的,在這裡可以找到這些概念。

定義動作

在HFSM上,每個狀態有一個子圖表在其中。在GOAP上,每個工作也有一個子圖表。可以透過按兩下狀態/工作來存取它們。 在這些子圖表上,您可以 建立動作

當存取圖表時,您將在頂端列看見一個階層連結列,其指出您目前所在的階層的深度。 您可以使用這些按鈕,以導航到先前的階層層級。

breadcrumb

在子圖表上已經定義了一個重要的節點:動作根節點。

subgraph actions

在HFSM上

在這裡我們有三個需要執行的動作清單:

  1. 進入時 清單定義了當HFSM進入到這個狀態時,需要執行的動作;
  2. 更新時 清單定義了每次更新HFSM時(一般而言是每一幀時),需要執行的動作;
  3. 結束時 清單定義了當HFSM離開這個狀態時,需要執行的動作。

在GOAP上

在這裡我們有一個需要執行的動作清單:

  1. 更新時 清單定義了每次更新GOAP時(一般而言是每一幀時),需要執行的動作;

為了定義這些動作清單,按一下動作根節點的右側的箭頭,然後按一下任何動作的輸入槽,或是按一下任何空的空間,以立刻建立一個新的動作:

actions sample 1

在這裡,一件重要的事情是,您可以定義您需要的數量的已連接動作,並且它們將在相同的幀上按順序被執行。 使用箭頭按鈕來持續連接它們:

actions sample 2

您可以依照您想要的方式來重新排序動作。 如果您不希望刪除動作,您也不希望執行它們,您之後也可以使用分離的動作集:

actions sample 3

請注意,您可以按一下輸入槽來定義動作欄位值。

按Enter 以應用更改,或 按Esc 以放棄這些更改。

action fields

如同我們已經看到的,編輯器已經附有一些預先定義的動作及決策。 這些是為了開發人員而建立,以作為一個開始。

在您的專案上,您將需要執行您的特定的動作及決策。 讓我們來看一下應該如何做到這點。

編碼動作

為了建立一個新的動作,您需要開啟 quantum_code 解決方案。

quantum.code 專案上,您可以建立繼承於AIAction抽象層級的任何層級。 當這樣做的時候,您將需要執行Update方法,每當您更新您的代理(HFSM或GOAP)時,都會調用它。

對於HFSM,這就是在您定義的動作清單上執行的事情:進入時更新時結束時。所以舉例而言,當進入某些HFSM狀態時,也將針對列於初始狀態上的動作,來執行這個程式碼。

重要事項:您需要標記您的新的層級為[Serializable]partial

C#

namespace Quantum
{
    [Serializable]
    public partial class IdleAction : AIAction
    {
        public override unsafe void Update(Frame f, EntityRef e)
        {
            // insert the action code here
        }
    }
}

定義欄位值

在機器人SDK上,可以在程式碼上宣告公共欄位,這樣這些欄位會顯示在視覺編輯器上。對於HFSM,可以針對動作及決策程式碼來做這個,而對於GOAP,可以針對動作。在編輯器上,使用者可以定義該欄位的值。

設定這些值的更簡單的方式是按一下欄位,並且立刻指派值。但是還是有其他選項:

  • 使用黑板節點;
  • 使用常數節點;
  • 使用設定節點;
  • 使用AI功能節點。

因為在黑板文檔中已經說明過黑板節點,讓我們深入了解常數/設定/AI功能節點。

常數面板

可以從左側面板來定義及使用常數。

在定義常數之後,可以擷取常數節點並且連接它們到動作及決策的輸入槽(在HFSM上)。一個單一的常數節點可多次作為輸入,這使得以相同的值來定義許多欄位變得更容易。

另一個關於常數的重要的角度是,在設定選單上不論何時改變其值,所有來自於該常數的節點將相應地被更新,這讓定義在HFSM的不同部分中的值,並且隨後改變這些值,變得更加容易。

使用左側面板以定義一個新的常數,在常數索引標籤上按一下(+)符號。

hfsm asset

然後選擇其NameTypeDefault Value並且儲存它。現在,有了已定義的常數,您可以拖放它到圖表檢視,並且已經可以連接它到輸入槽。

hfsm asset

設定面板

對於許多使用相同的HFSM/GOAP的代理,可以有不同的常數值,這很有用,舉例而言,在您希望針對不同的困難度有不同機器人,而它們有相同的行為邏輯的情形。在簡易模式下的一個射擊手機器人可以有一個「2秒」的值作為其反應時間,同時在困難模式下該值為「0.5秒」。使用設定面板可以輕易地完成這件事情。

使用在左側的面板,以建立設定值,其之後被編譯為類型AIConfigAsset的一個新的資料資產。在編譯之後,您可以在AIConfig_Assets資料夾中找到名為<DocumentName>DefaultConfig的資產。

這些資產之後可以用於模擬,以擷取常數值。

讓我們建立一個非常簡單的設定配置:

createconfig

除了從頭建立新的設定欄位,也可以轉換一個常數成為一個設定,或是反過來。

converttoconfigurable

之後,在編譯文件之後,這是結果資產:

defaultconfig

最後,為了有來自這個資產的變量,這樣您可以定義不同的常數值,請使用Unity的專案索引標籤上的右鍵選單,並且前往Create/Quantum/Assets/AIConfig之下。

這將建立一個非常簡單的設定資產,其將尋找另一個設定資產來作為其基礎。填入預設設定欄位,並且按一下更新設定以鏡像預設設定資產。您隨後可以按照需要來更改值。同時,如果您希望還原值為原始設定資產值,請按一下重新設定為預設。

configvariation

現在,為了使用這些設定,這裡是一些替代方案:

  1. 直接從設定資產讀取它,告知設定索引鍵,並且根據設定類型來相應地擷取值:

    var myBoolean = myConfig.Get("Key").Value.Boolean;

    類型可以是:整數、布林值、位元組、FP、FP向量2、FP向量3及字串

  2. 或者與AI參數類型一起使用它。

    舉例而言:

    首先,在任何動作/決策上建立一個AI參數FP欄位。編譯它,這樣它就會在視覺編輯器上顯示;

    然後,從左側面板拖放一些設定值,並且連接它到AI參數欄位;

confignode

​ 然後編譯文件。在程式碼上,使用AI參數API來擷取值。這將透過作為參數來傳送的設定資產,來相應地擷取設定的值:

Unknown

// Considering that the variable "AttackRange" is of type AIParamFP
FP rangeValue = AttackRange.Resolve(f, blackboard, myConfig);

所以,基本上,關鍵在於基於您在視覺編輯器上擁有的東西,使用設定面板,來建立預設設定資產,然後建立它的變數,並且使用資產參照來相應地連接它們到您的需求。

HFSM代理和GOAP代理已經附有一個欄位,來參照特定代理/實體的設定資產,這僅是為了方便性。所以您可以使用它來完成您自己的參照:

Unknown

// The config to be set can come from any source that you prefer. Some custom asset, RuntimeConfig, RuntimePlayer...it's up to you. Just set it to the component:
hfsmAgent->Config = config;
// or
goapAgent->Config = config;

// Then, when you need to GET the Config:
hfsmAgent.GetConfig(frame);
//or
goapAgent.GetConfig(frame);

AI參數

有不同的方法來定義欄位上的值的來源。目前可以使用黑板、常數及設定來完成它。因此,也有不同的方法來讀取程式碼上的這些值。因此,如果開發人員需要將來源類型從一種更改為另一種(也就是從黑板節點改為常數節點),那麼也需要更改來源程式碼。

但是,為什麼更改來源類型是有用的呢?

  • 簡單來說,可以僅僅是以手動定義值;
  • 如果值在運行階段可以改變,它可以被儲存在黑板上,所以請使用一個黑板節點來定義它;
  • 如果值不改變,但是您希望它來自節點,以讓圖表變得更有彈性,那麼請使用常數節點來定義它;
  • 如果上述說明適用,但是您需要它在各個代理之間而有所不同,請使用一個設定節點。

AIParam是一個類型,其被建立以協助突然改變來源的情況。但是,在學習使用它之前,讓我們快速地分析一下在讀取值時,各個程式碼之間的不同點。

如果某些欄位曾在視覺編輯器上被手動地定義,或是曾經使用一個常數節點來定義它,那麼讀取它的程式碼是簡單直白的:

C#

// In this case, the value is directly stored on the field itself, with no need for any extra code
public Int32 MyInteger;

現在,如果該值來自於一個黑板節點:

C#

// Read the value from the blackboard asset
var value = blackboardComponent->Board.GetValue("someKey");

對於一個設定節點:

C#

// Read the value from the config asset
var myBoolean = myConfig.Get("Key").Value.Boolean;

所以,為了在視覺編輯器上的值來源改變時,不需要更改程式碼,請針對您的欄位來使用AIParam類型。其主要的特性是:

  • 它有Resolve方法,其接收黑板及設定資產。透過了解欄位的值的來源,這個方法已經傳回正確的值,其可以是直接傳回正確的值(當欄位被手動地定義時),或是從黑板/設定傳回值。所以您可以根據需要來多次更改值來源類型,並且這將是讀取它的程式碼:

C#

public AIParamInt MyAIParam;
var value = MyAIParam.ResolveResolve(frame, blackboard, aiConfig);
  • 它目前有8個可能的類型:

C#

AIParamInt, AIParamBool, AIParamByte, AIParamFP, AIParamFPVector2, AIParamFPVector3, AIParamString, AIParamEntityRef
  • 內部而言,它已經檢查在視覺編輯器上定義AI參數的方式。它是否是手動定義,或是來自任何特製化節點。

AI功能節點

有了AI功能節點,可以預先定義各種類型的「取得器」節點。這樣做的主要目的是,可以建立特定節點,其將根據您的遊戲特定需要來傳回值。

針對AI功能節點的基礎類型是:

  • AI功能位元組;
  • AI功能布林值;
  • AI功能整數;
  • AI功能FP;
  • AI功能FP向量2;
  • AI功能FP向量3;
  • AI功能實體參照

為了建立您自己的AI功能節點,只需繼承上述任何層級,並且執行抽象Exectue()方法。這裡是一個範例AI功能節點,其將傳回某些儲存在一個自訂元件中的實體的位置:

C#

namespace Quantum
{
  [System.Serializable]
  public unsafe class GetEntityPosition : AIFunctionFPVector3
  {
    public override FPVector3 Execute(Frame frame, EntityRef entity = default)
    {
      MyComponent myComponent = frame.Unsafe.GetPointer<MyComponent>(entity);
      Transform3D* targetTransform = frame.Unsafe.GetPointer<Transform3D>(myComponent->TargetEntity);
      return targetTransform->Position;
    }
  }
}

當您編譯Quantum解決方案時,AI功能現在將在內容選單中可用。也可以在AI功能層級上宣告公共欄位,您隨後可以在視覺編輯器中直接填入該欄位。

對於連接AI功能節點,必須使用上面解釋的AIParam類型來完成。所以,如果我有一個HFSM動作,其需要基於上述的AI功能層級來取得一個實體的位置,則需要一個AIParamFP欄位:

Unknown

namespace Quantum
{
  [System.Serializable]
  public unsafe partial class SampleAction : AIAction
  {
    public AIParamFP TargetPosition;

    public override void Update(Frame f, EntityRef e)
    {
      // If you are not sure if your AIParam's source is a Blackboard/Config/AIFunction node, then use the general Resolve method
      var position = TargetPosition.Resolve(/*args*/);

      // If you are sure that the source is an AIFunction node, then you can use the specific Resolve method
      var position = TargetPosition.ResolveFunction(frame, entity);

      // Now, do something with the position
    }
  }
}

正如上述示例所顯示,您可以使用一般性解析方法,其取決於在視覺編輯器(黑板、設定、AI功能節點)上定義的來源,而傳回一個值。但是如果已知AI參數是由一個AI功能節點所定義,那麼使用特定解析方法可能是一個更好的選項,因為它不需要許多參數,並且稍微快一點。

一個AI功能節點也可以有一個AIParam欄位,其允許建立巢狀AI功能。

在視覺編輯器上

當在動作及決策上宣告一個公共AIParam時,它將顯示在視覺編輯器上,並且您將能夠手動或從特製化節點來定義其值。舉例而言,設想一個public AIParamInt IncreaseAmount

aiparam sample
### 針對自訂列舉來建立AI參數

除了前述提到的類型之外,也可以針對列舉來建立AI參數。為此,您需要針對您所需的特定列舉來建立您自己的AI參數類型。這裡是針對它的程式碼片段:

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(BlackboardValue value)
    {
      int enumValue = *value.IntegerValue;
      return (BotType)enumValue;
    }

    protected override BotType GetConfigValue(AIConfig.KeyValuePair config)
    {
      return (BotType)config.Value.Integer;
    }
  }

AI內容

機器人SDK附有一個資料容器的執行方式,其可用於在代理更新常式上傳送其內容特定的資料。

並不是強制使用這類內容的容器,但是可用於輔助從使用者端點取得資料,比如一個HFSM的AIAction.Update()、一個BT的Leaf.OnUpdate()等等。

使用它的主要理由是,提供額外的資料到幀或實體參照以外的使用者節點。因此,可以避免大量的重複使用的程式碼,比如frame.Get<MyComponent>(entityRef),其有時候對於一個代理的一個單一的更新,需要完成許多次。

有了AI內容,可以在更新常式的最初的時候,放資料到它之中(比如,在調用HFSMManager.Update之前,這也適用於BT、GOAP及UT的情況)。
所以舉例而言,這裡的內容的目的在於,以有關於一個特定的代理的內容的資料來填入它。可能儲存它的AI黑板元件。可能是一些其他的自訂元件。或者可能是代表代理更新邏輯的某個整數。
總的來說,這裡是一些讓它運行所需的程式碼片段。這裡是一些HFSM的示例,不過同樣的情形也適用於BT、GOAP,及UT:

擴展AI內容架構

  • 建立一個新的檔案,依您的需要來選擇名稱及位置。像是一個AIContext.User.cs
  • 在它之中,以所需的欄位,建立一個AI內容架構的partial宣告:

C#

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

        public AIContext(HFSMAgent* hfsmAgent, AIBlackboardComponent* blackboard)
        {
            this.HfsmAgent = hfsmAgent;
            this.Blackboard = blackboard;
        }
    }
}
  • 容器的配置已經準備完成。現在在每次更新代理時建立內容物件,並且以一個ref parameter來傳送它:

C#

AIContext aiContext = new AIContext(hfsmAgentComponent, blackboardComponent);
HFSMManager.Update(frame, frame.DeltaTime, hfsmData, entityRef, ref aiContext);
  • 這樣做,端點將可以存取內容,比如在一個AI動作的更新之中:

C#

namespace Quantum
{
    [System.Serializable]
    public unsafe class SampleAction : AIAction
    {
        public override void Update(Frame frame, EntityRef entity, ref AIContext aiContext)
        {
            // either cash the data in local variables
            var agent = aiContext.HfsmAgent;
            var blackboard = aiContext.Blackboard;

            // or use it right away where needed
        }
    }
}

重要考量

您需要謹慎地處理AIContext。雖然有其他的使用方式,但是在每一幀從頭開始建立它,並且填入它的資料 是最安全地使用它的方式

同時,內容的主要目的是,提供一個好的方式來讀取關於內容的資料,以協助決策。組建它的目的不是支援在內容中儲存資料,並且在動態的情況下改變它,即便這樣做是可能的。按需要使用它,並請確保謹慎處理,以避免難以追蹤的問題。

停用

針對各個AI模型,有一些特定的節點可以被 停用。這基本上從編譯流程中停用了該部分的邏輯,且不需要刪除/中斷連接任何東西。讓我們分析在不同的AI模型上有哪些東西可以被停用:

HFSM的特定情況

停用狀態

忽略了到已停用狀態的轉換。同時,在該狀態中的任何動作將不會被執行。

附註: 如果您以一個已停用的預設狀態來編譯一個HFSM,您將得到一個錯誤。

為了停用一個狀態節點,以右鍵按一下狀態,然後選擇「停用/取消停用狀態」。 當它被停用時,它將顯示為透明。

mute state

停用轉換

您可以透過以右鍵按一下轉換的線,並且選擇「停用/取消停用轉換」,來停用一個轉換。

已停用的轉換將在編譯時被忽略。 可以停用任何類型的轉換:一般轉換、任何轉換、轉換集以及轉換到入口網站。

mute transition

GOAP的特定情況

已停用的工作不會被新增到GOAP用來制定計劃的可能的工作之中。

為了停用一個工作節點,以右鍵按一下狀態,然後選擇「停用/取消停用工作」。 當它被停用時,它將顯示為透明。

mute task

對於HFSM及GOAP

停用動作

可以停用任何動作,不論它在動作清單上的位置。 已停用的動作將在編譯時被忽略,並且只會執行鏈中的下一個動作,前提是已停用的動作含有任何下一個動作的話。

也可以以右鍵按一下任何動作節點,來存取它。

mute action

機器人SDK系統

機器人SDK的套件預設附有兩個層級:

  • BotSDKSystem:用於自動化一些流程,比如解除配置黑板記憶體、以實體原型含有的資料來初始化HFSM/BT代理等等;
  • BotSDKDebuggerSystem:用於在Unity側,針對偵錯工具來收集重要資訊;

為了使用它們,只需在您的SystemSetup層級上新增new BotSDKSystem(),和/或new BotSDKDebuggerSystem()

附註:不是強制 使用這些系統。這裡完成的任何事情都可以在您自己的層級中完成;

附註2: 請注意有些事情可能已經在您自己的程式碼裡面的其他部分完成了,所以請謹慎處理,透過新增這個系統來引進問題。在新增系統之前,看一下它的功能,然後立即使用它,或是將它的一些邏輯放到您自己的程式碼中。

視覺編輯器評論

將評論新增到視覺編輯器中是非常方便的。為了做到這點,選取任何節點(狀態/工作/動作/決策/常數,等等),並且 按「G」 以新增一個評論區域。然後,按一下「評論」標題文字,並且根據您的需要來改變它。也可以新增評論到超過一個節點。為了選取超過一個節點,在Windows作業系統上持續按著「Ctrl」按鈕,或是在Mac作業系統上持續按著「Command」按鈕。

commented state
commented actions

更改編譯輸出資料夾

預設下,機器人SDK的編譯生成的資產,將被放到Assets/Resources/DB/CircuitExport資料夾。可以透過選取位於Assets/Photon/BotSDK/VisualEditor/CircuitScriptables資料夾的名為SettingsDatabase的資產,來改變這個。然後,尋找名為Bot SDK OutputFolder的欄位,並且按照您的需要來更改它。請確保目標資料夾已經被建立。

同時,總是建立一個名為CircuitExport的上層資料夾,並且所有子資料夾將被建立在其中。

change output folder

選擇已儲存的歷史大小

機器人SDK預設儲存5個歷史的輸入項目到視覺編輯器檔案。如果當您關閉/開啟您的AI文件時,您希望在工作階段之間維持歷史,這相當有用。但是AI檔案大小將增加,因為AI迴路變得更大,這取決於您儲存的歷史輸入項目有多少。

所以您可以選擇您希望儲存的歷史輸入項目的數量,其中如果您不需要儲存歷史,您甚至可以設定為零,歷史將只在您重新開啟您的AI檔案時重新載入,其也發生於您關閉/開啟Unity的時候。

為了更改歷史輸入項目的數量,選取位於Assets/Photon/BotSDK/VisualEditor/CircuitScriptables資料夾的名為SettingsDatabase的資產。然後,尋找名為Save History Count的欄位。

history count
Back to top