This document is about: QUANTUM 2
SWITCH TO

Hierarchical Finite State Machine

建立一個新的HFSM

在編輯器的頂端列上,按一下(+)按鈕,然後選擇狀態機選項。

create hfsm document

之後將提示您儲存HFSM檔案。 在您希望的地方儲存它。 這將建立一個資料資產,用於保留您 在視覺編輯器上 已完成的工作。

hfsm file

注意事項:您現在選擇的名稱,將是您編譯您在視覺編輯器上已完成的工作時,進一步生成的另一個資料資產的名稱。 那將是實際上用於驅動您的機器人的資料資產,所以您已經可以選擇一個暗示性的名稱。

initial state

當您儲存檔案時,主要的機器人SDK視窗將被一個單一的 新狀態 填入,供您開始使用。

現在,讓我們進一步看看這個初始狀態:

state analysis
  1. 指出這是HFSM的初始狀態;
  2. 狀態的名稱;
  3. 從這個狀態而生的轉換清單。

建立一個新的狀態

為了建立一個新的狀態,以右鍵按一下在編輯器視窗上的任何空的空間,並且選擇 建立新狀態

create new state
new state created

編輯一個狀態

為了編輯一個狀態,以右鍵按一下目標狀態,並且選擇 編輯這個狀態

edit state

在這裡,您可以定義狀態的名稱及刪除/重新排序轉換。

附註:重新排序轉換將不會改變它們的優先順序。 它只用於視覺調整。在這個教學中將進一步說明優先順序主題。 它只用於視覺調整。

按下Enter 以適用更改或 按下Esc 以放棄那些更改。

最小化狀態檢視

常見的情況是,HFSM在狀態及轉換的數量方面大幅增加,這可能導致難以實際了解某些HFSM的流程。 在任何狀態節點上,您可以按一下 最小化 按鈕,以隱藏轉換槽及更改從它開始畫/畫向它的線的方式,從而啟用一個簡化的檢視。

minimized state view

讓我們分析 使用前使用後

maximized sample
minimized sample

在兩個狀態間建立一個轉換

為了開始在兩個狀態間建立一個轉換,首先按一下任何狀態的左/右邊緣的小圓圈的任一個。 然後,如果您按一下另一個狀態,將建立一個連接這兩個狀態的新的轉換。

如果您不是按一下另一個狀態,而是按一下任何空白的空的空間,如果您希望立即建立目標狀態,編輯器將彈出建立面板。

new transition

無論您何時建立一個新的轉換,它將是暗色,指出該轉換 還沒被定義

有幾種與轉換互動的方式:

  1. 當您的滑鼠滑過一個轉換,它將被醒目提示;
  2. 當您以左鍵按一下一個轉換,小圓圈將走過它,以指出轉換的方向;
  3. 當您按兩下一個轉換,您將進入它的子圖表;
  4. 從右鍵選單中,您可以:進入轉換子圖表,停用轉換或刪除它。

讓我們來分析已經在決策子圖表上建立的節點:

transition node

這是用於實際地定義轉換的節點。 這裡有四個重要的概念:

  1. 這個節點的名稱指出原始狀態及目標狀態。它可以被更改,這是使用右鍵選單完成,所以您可以選擇有意義的名稱,並且它將顯示在上層,這讓情況更容易理解;
  2. 第一個槽用於定義一個在評估這個轉換時需要被考量的 事件
  3. 第二個槽用於定義撰寫轉換的 決策
  4. 第三個槽用於定義來自 相同的節點 的所有轉換之間的執行的順序。

讓我們開始針對這個轉換來定義一個簡單的決策。 為了做到這點,以右鍵按一下任何空白空間,此時一個面板將會顯示您可以建立的決策。

initial decisions

為了簡單說明,讓我們選擇 真決策。 當然,這個決策的結果總是為真。

new decision

有一個輸出槽,其定義這個決策的結果將被驅動到的地方。

所以,以左鍵按一下 結果 槽一側上的圓圈,並且連接它到決策槽:

connected decision

這樣就完成了。 您已經定義了最簡單的轉換。 有了這個,只要您的機器人處於 新狀態 並且已更新HFSM,機器人將轉換到 新狀態1

按一下決策欄位,可以編輯一個決策欄位值

decision fields

按下 Enter 以適用更改或 按下 Esc 以放棄更改。

現在我們定義了一個非常簡單的轉換,讓我們回到頂層檢視,並且看看它看起來如何。 您可以按一下頂層列的階層連結列的 按鈕,來導航回狀態檢視:

root on breadcrumb

或者,您可以使用左側面板,來導航狀態:

root on hierarchy

現在您可以看到轉換不再是紅色。 這指出這是一個有效的轉換。

valid transition

定義轉換優先順序

對於具有多個轉換的狀態,可以定義首先要評估哪個轉換。 為了定義這個順序,使用 優先順序 槽。按一下它以設定其值。

valid transition

可以在狀態節點上看見轉換的優先順序。

valid transition

轉換將被評估的順序是:從高優先順序到低優先順序。 有了這個,您已經可以定義首先需要檢查哪個轉換。

注意事項: 針對狀態的轉換所定義的優先順序,對另一個狀態的轉換而言沒有影響。

建立新的轉換

我們目前只有在我們的兩個狀態之間的一個轉換。 有了這個,優先順序欄位是無用的,因為沒有狀態有多個轉換。

為了建立一個新的轉換,將滑鼠放在您的狀態的底部部分,並且按一下顯示的 (+) 按鈕。

new transition

之後您可以定義該新的轉換,就像您對第一個轉換所做的一樣。

two transitions

特殊轉換類型

轉換集

這些節點類型可用於群組許多的轉換。 重新使用一個轉換集特別有用,因為許多狀態可以指向一個單一的轉換集。

為了建立一個新的轉換集,以右鍵按一下任何空的空間,並且選擇 建立新的轉換集。 這將建立一個節點,其非常相似於狀態節點:它開始於一個單一的未定義的轉換,並且您可以透過將滑鼠暫留在其底部部分,來建立許多新的轉換:

transition set

之後,只需建立連結。 這裡是一個示例:

maximized transition set

也可以使用轉換集的右上角按鈕,來最小化轉換集:

minimized transition set

任何轉換

當您希望定義一些應該 從位於該同一階層層級上的任何狀態 選取的轉換時,這些轉換非常有用。 附註: 將在本文檔中進一步說明階層。

從右鍵選單來建立新的「任何」轉換。

現在,定義「任何」轉換的目標狀態。

any transition

在上述的範例圖,該階層層級上的每個狀態都將考量來自「任何」節點的轉換。

但是可以定義一個忽略「任何」轉換的狀態的清單。從 已排除清單 選單中選擇這些。

any transition excluded list

入口轉換

這個轉換類型意味著強迫HFSM從一個狀態轉到 您的階層的任何層級 中的任何其他狀態。 從右鍵選單建立新的入口轉換。

portal transition

現在您必須定義這個入口將把HFSM帶往哪個狀態。 按一下下拉式選單以選擇它。 請注意,目標狀態的名稱將基於您擁有的階層:

portal dropdown

在您定義入口的目標之後,您只需定義哪些狀態將考量該入口。

transition to portal

已撰寫的決策

除了使用單一決策來定義轉換,也可以建立一些已撰寫的決策。 機器人SDK已經帶有3個邏輯決策可以立即使用。

這裡是一些已撰寫的決策的範例,它們基於 決策:

composed decision 1
composed decision 2
composed decision 3
composed decision 4

事件

如果您希望評估一個轉換,且不在您的 quantum_code 專案上建立一個新的決策、編譯或設定決策等等,您可以使用事件。

事件的工作方式非常簡單:每當事件被觸發時,目前狀態的轉換將檢查該事件是否正在被接聽。 如果任何轉換接聽該事件,它將被選取。 就程式碼而言,這是觸發事件所需的:

在任何 quantum_code 指令碼中,請執行:

C#

HFSMManager.TriggerEvent(f, &guy->Fields.HFSMAgent.Data, (Entity*)guy, "SomeEventName");

這個方法調用可以被新增到您的動作/決策,或到您有的任何其他邏輯,比如在您的系統中。

為了建立一個新的事件,按一下事件的工作階段的左側面板上的 (+) 按鈕:

create event

當您這樣做,您將被提示來建立您的新的事件。 您也可以在事件上按兩下,來開啟選單以 編輯或刪除事件

為了在轉換子圖表上放置一個事件,您必須拖放它。 然後,類似於您對決策所做的,您可以連接事件的輸出槽到轉換的事件輸入槽:

linked event

注意事項: 與決策有所不同,這裡沒有複合事件定義,並且一個轉換不接受超過一個事件的連接。

帶有由 僅一個事件 定義的決策的轉換,被視為 有效轉換

也可以透過設定 一個事件及一個決策,來定義一個轉換。 在這種情況下,只有在同一個幀上觸發事件並且決策條件為真時,才發生轉換。

event and decision

定義動作

現在我們討論了如何建立狀態機的流程(帶有狀態及轉換),讓我們看一下如何針對一個狀態來定義動作。

更多關於這個主題的資訊,請參見:定義動作

停用

當測試您的AI時,為了暫時地停用一些邏輯而停用一些節點,可能有用。更多關於如何停用節點的資訊,請參見:停用

階層

在任何狀態的子圖表上,您可以建立新的狀態及轉換的集。 假設您在 新的狀態 子圖標上,而且您建立一個新的狀態稱為 另一個狀態。 當您這樣做的時候,將在這兩個動作之間建立一個關係: 新的狀態是上層而另一個狀態是下層

上層 下層狀態的動作集轉換都將被執行。 這樣的話,可以封裝一個狀態機到另一個狀態機之內。

假設如果您有 3個層次的行為的魔王,這可能非常方便:它一開始是簡易模式,然後當它的血量剩下一半時變得稍微難一點,最後在它總健康值只剩10%時變得更難。 您可以在頂層圖表建立3個主要狀態,並且各個狀態將有其自己的狀態機以定義魔王的難度。

改善HFSM的組織方式是非常有用的。

為了建立一個下層狀態,只需前往任何狀態的子圖表,以右鍵按一下空的空間,並且選擇 建立新的狀態 選項。 有了這個,您可以有一個複雜的階層層級,您可以在左側選單中看見它:

hfsm hierarchy

注意事項: 您可以按一下這些按鈕來導航於階層上。 也可以以右鍵按一下任何這些狀態,以在您的目前的圖表檢視上來對它建立一個新的入口。

重要事項: 也可以為HFSM上的每個階層層級來定義預設狀態。這定義了在上層狀態之間轉換時輸入的下層狀態。為了定義哪個是預設狀態,以右鍵按一下任何狀態節點並且選擇「設為預設狀態」。

編譯您的HFSM

為了實際上使用您建立的HFSM,您必須編譯您所完成的事情。

為了編譯,您有兩個選項:

compile buttons
  • 左按鈕只用於編譯目前開啟的文件;
  • 右按鈕用於編譯在您的專案上您有的每個AI文件。

您的HFSM檔案將位於:"Assets/Resources/DB/CircuitExport/HFSM_Assets"。

hfsm asset

設定將被您的機器人使用的AI

為了最終使用建立的AI,您只需參照已編譯的資產。 為了完成它,您可以基於GUID來載入資產,或您可以只是建立一個 資產連結 以指向所需的AI資產:

referencing ai

HFSM編碼

現在,就DSL而言,在建立新的HFSM代理方面有兩個主要選項:

  • 您可以將HFSM代理元件新增到您的實體中;
  • 您可以在您的架構或全域空間上宣告它,而無需擁有實體。

這一點很重要,因為可能讓HFSM執行超出實體的範圍的動作,比如建立遊戲流程、定義遊戲開始時執行哪些動作、定義何時遊戲將改變其目前狀態等等。

初始化代理

現在首先,舉例而言,這是您建立一個新的HFSM代理,並且新增它到您的實體的方式,前提是您沒有正在使用一個實體原型

C#

var hfsmAgent = new HFSMAgent();
f.Set(myEntity, hfsmAgent);

然後,在任何更適合您的應用程式之處,您必須基於您從視覺編輯器編譯的HFSMRoot資產,針對該實體來調用HFSMManager.Init方法,。

無論 您是否正在使用實體原型,下述的初始化步驟都需要完成:

C#

var hfsmRootAsset = f.FindAsset<HFSMRoot>(referenceToRoot.Id);
HFSMManager.Init(frame, myEntity, hfsmRootAsset);

// Only do this if you are not using Entity Prototypes
f.Set(myEntity, hfsmAgent);

如果HFSM初始化過程中所需的任何資訊都包含在HFSM代理本身之中,您可以從實體來取得元件,以取得必要的資料:

Unknown

var hfsmAgent = f.Get<HFSMAgent>(myEntity);

使用"OnComponentAdded"回調來初始化

也可以直接在實體原型上設定參照到HFSMRoot資產,並且使用OnComponentAdded信號以透過該資訊來初始化代理:

C#

// At any system...
  public unsafe class AISystem : SystemMainThread, ISignalOnComponentAdded<HFSMAgent>
  {
    public void OnAdded(Frame f, EntityRef entity, HFSMAgent* component)
    {
      // This is how you get the HFSMRoot from the component set on the Entity Prototype
      HFSMRoot hfsmRoot = f.FindAsset<HFSMRoot>(component->Data.Root.Id);

      // Then we just do the initialization step
      HFSMManager.Init(f, entity, hfsmRoot);
    }

  // ...
  }

更新代理

現在,已經初始化實體的HFSM代理,您只需要在您需要的時候調用更新方法:

C#

HFSMManager.Update(f, f.DeltaTime, myEntity);

這將使HFSM執行其初始狀態的資料並且執行動作及轉換到其他狀態。 有了這個,AI應該已經執行您在視覺編輯器上建立的流程。

一個範例系統,其初始化及更新代理

C#

namespace Quantum
{
  public unsafe class AISystem : SystemMainThread, ISignalOnComponentAdded<HFSMAgent>
  {
    public void OnAdded(Frame f, EntityRef entity, HFSMAgent* component)
    {
      HFSMRoot hfsmRoot = f.FindAsset<HFSMRoot>(component->Data.Root.Id);
      HFSMManager.Init(f, entity, hfsmRoot);
    }

    public override void Update(Frame f)
    {
      var allAgents = f.Filter<HFSMAgent>();
      while(allAgents.NextUnsafe(out var entity, out var agent))
      {
        HFSMManager.Update(f, f.DeltaTime, entity);
      }
    }
  }
}

編碼動作及決策

為了建立您自己的動作,遵循這些指引:編碼動作

建立您自己的決策,是非常相似的。 但是,不是建立一個其繼承於AIAction的新的層級,而是讓其繼承於HFSMDecision抽象層級。

同時,不是必須執行Update方法,而是必須執行Decide方法。 使用它來傳回true/false,這取決於您自己的需要。

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

C#

namespace Quantum
{
  [Serializable]
  public partial class TrueDecision : HFSMDecision
  {
    public override unsafe bool Decide(Frame frame, EntityRef entity)
    {
      return true;
    }
  }
}

定義欄位值

關於設定值到動作/決策欄位時,您可使用的替代方案的更多資訊,請參見這裡:定義欄位值.

AI參數

如果您希望有更彈性的欄位,並且透過不同的方式來定義這些欄位的話,這些關於使用AI參數的資訊很有用:手動設定或從黑板/常數/設定節點來設定:AI參數.

AI內容

如需取得更多關於如何將代理內容的資訊作為參數傳送的資訊,請參見這裡:AI內容.

機器人SDK系統

這是一個層級,其用於自動執行一些流程,比如解除配置黑板的記憶體。如需取得更多關於它的資訊,請參見這裡:機器人SDK系統.

偵錯工具

機器人SDK帶有其自己的偵錯工具。它使開發人員可以在運行階段選擇任何HFSM代理,並且看見在視覺編輯器上醒目提示的最新的代理的流程。這裡是一個用於機器人SDK範例專案的偵錯工具的範例:

debugger graph

如同上述GIF所顯示,可以看到代理的當前狀態,以及導致該狀態的最近三次轉換。藍色轉換是最近的一次轉換。比起先前的黑色轉換,它也有更多圓圈穿過線條。

此外,也可以檢查階層檢視上的目前狀態。帶有箭頭的狀態代表HFSM目前處於該狀態。這很有幫助,因為您不需要深入查看視覺圖表來了解代理目前在階層的深度。

debugger hierarchy

使用偵錯工具

這是在您的專案上使用偵錯工具的步驟:

  1. 在您的SystemSetup.cs檔案上啟用BotSDKDebuggerSystem。使用這個特定的系統是選擇性的,因為如果您希望在其他地方使用偵錯邏輯,您只需 在已驗證幀時 在您自己的自訂系統中調用BotSDKDebuggerSystem.OnVerifiedFrame?.Invoke(f);
  2. 在視覺編輯器上,在頂層面板上按一下臭蟲圖示。當圖示為綠色時,偵錯為 啟用
debug active

現在有兩個方式來選擇將偵錯哪個實體。它可以被關聯到一個選取的遊戲物件,或它可以在一個檢查器上被選取。您可以選擇上述的其中之一。或者兩個都選:

從一個遊戲物件來偵錯:

  1. 選取您的代表一個有HFSMAgent作為一個元件的Quantum實體的預製件/實體原型;

  2. 新增BotSDKDebugger到它;

  3. 在運行階段,開啟機器人SDK視窗時,選取已新增BotSDKDebugger的遊戲物件。這樣就行了!偵錯應該已經開始;

從一個偵錯工具檢查器視窗來偵錯:

  1. 在模擬側,您需要註冊代理實體到偵錯工具視窗。為了完成它,可以調用:

    BotSDKDebuggerSystem.AddToDebugger(entitiRef, (optional) customLabel)

    針對被偵錯的實體,預設的顯示名稱遵循這個規則:Entity XX | AIAssetName。但如果您希望針對偵錯輸入而言有一個自訂名稱,您可以使用customLabel參數。您可以自由決定它。

    也可以建立階層。只需在自訂標籤上使用分隔符號/,它將在偵錯工具視窗上建立階層,其可以被摺疊、擴展等等;

  2. 在Unity上,按一下在偵錯工具啟用按鈕右側的按鈕。它開啟一個新的視窗,其顯示所有已註冊實體。選取您希望進行偵錯的實體,這樣就完成了。

debug window
debug hierarchy

舉例而言,上述範例GIF中使用一些自訂標籤:Monster 1, Monster 2, Blue Team/Commander, Blue Team/Warriors/Foo, Blue Team/Warriors/Fuz and Blue Team/Wizards/Bar

重要事項: 當啟用偵錯工具,它將配置記憶體來儲存偵錯所需的資料,如果您正在 從編輯器遊玩,其 可能減慢 遊戲。所以,如果您正在從Unity之內來分析應用程式,部分分析可能被關聯到偵測工具,所以請考慮在分析時停用它。

附註:偵錯工具視窗也將顯示 沒有實體檢視 的實體,所以這就是您如何找到它來偵錯它們的HFSM的方式;

附註2:目前,不可以偵錯沒有連接到一個實體的代理,比如仰賴DSL全域的代理。這將只在以後的版本中新增。

視覺編輯器註解

如需取得更多如何在視覺編輯器上建立註解的資訊,請參見這裡:視覺編輯器註解.

更改編譯匯出資料夾

預設下,機器人SDK編譯生成的資產將放在Assets/Resources/DB/CircuitExport資料夾。您更改匯出資料夾的方式,請參見這裡:更改編譯匯出資料夾.

選擇被儲存的歷史大小

可以更改儲存在機器人SDK檔案的歷史輸入的數量。如需取得更多資訊,請參見這裡:更改歷史儲存計數.

在幀中發生的事

在機器人SDK上,主要輸入點為:

  • HFSMManager.Update,其被持續地調用,以更新您的代理;
  • HFSMManager.Init,其用於初始化代理;
  • HFSMManager.TriggerEvent,其考量一個已觸發事件以強制進行轉換檢查。

為了協助更好地視覺化當調用這些方法時,在一個幀發生的事情,這裡是一個流程圖表:

commented actions
Back to top