跳至主要内容
版本:v3

GSIManagerBase

重要 注意 提醒

Coding Style wiki


GSIManagerBase<T>GSIFrame(Game Stage Integration)的遊戲階段管理器抽象基類(FSM 概念),統一管理 GSIBase 階段的註冊、查詢、切換與更新驅動。繼承後即可透過靜態 Default API 直接以子類類名操作,通常一個遊戲只需要一個管理器子類。

命名空間OxGFrame.GSIFrame
類型public abstract class
原始碼GSIManagerBase.cs
using OxGFrame.GSIFrame;

提醒 若專案有熱更新工程,建議區分兩個管理器分別管理(如 AotGameStageManagerHotfixGameStageManager)。

宣告

public abstract class GSIManagerBase<T> where T : GSIManagerBase<T>, new()

泛型參數

泛型參數說明
T管理器子類自身型別(CRTP 自我參照約束),需具備無參數建構子(new())。

繼承時將子類自身作為泛型參數傳入:

public class GSIManagerExample : GSIManagerBase<GSIManagerExample> { /* ... */ }

運作方式

單例與驅動

GSIManagerBase<T> 內建執行緒安全的延遲單例(Double-Checked Locking)。首次呼叫任一靜態方法時,自動建立 T 實例並執行其建構子(基類建構子會初始化階段快取),所有 Default API 皆透過該單例轉呼叫對應的實例成員方法。

重要
  • GSIManagerBase<T> 為純 C# 類(非 MonoBehaviour),不會自行更新,必須由主入口的 MonoBehaviour 呼叫 DriveStart(於 Start())與 DriveUpdate(於 Update())驅動運作。
  • 不可於子類建構子中呼叫任何 Default API 靜態方法,會導致 GetInstance() 遞迴而 StackOverflow(死循環);建構子中請改用實例成員方法(如 AddGameStage)。

階段註冊與 ID

  • 每個階段以唯一的 int id 註冊至快取。泛型多載未指定 id 時,以 typeof(U).GetHashCode() 作為 id(同型別僅能註冊一個實例)。
  • 指定 id 的多載可自訂 id,同型別即可註冊多個實例。
  • 相同 id 重複註冊會輸出警告日誌並略過,不會覆蓋既有階段。
  • 註冊時管理器會自動呼叫 gameStage.SetId(id) 設定階段辨識碼。

切換流程

模式生效時機行為
一般切換force = false下一次驅動更新僅記錄目標 id,於下一次 DriveUpdate 偵測到 id 變更時,依序執行「舊階段 OnExit → 更新當前 id → 新階段初始流程」。不允許切換至當前階段(輸出警告且不切換)。
強制切換force = true立即立即依序執行「舊階段 OnExit → 更新當前 id → 新階段初始流程」。不檢查是否為同一階段,可用於重新進入當前階段。
注意
  • 新階段的初始流程(OnCreate / OnEnter / 開啟刷新)詳見 GSIBase 生命週期與呼叫時序
  • 切換至未註冊的 id 時,舊階段仍會執行 OnExit,接著輸出錯誤日誌,且當前階段會變為空。

繼承實作範例

using OxGFrame.GSIFrame;
using UnityEngine;

// 自訂遊戲階段管理器:以子類自身作為泛型參數(CRTP)
public class GSIManagerExample : GSIManagerBase<GSIManagerExample>
{
public GSIManagerExample()
{
// 建構子中註冊各遊戲階段(僅能使用實例成員方法,不可呼叫 Default API)
this.AddGameStage<StartupStageExample>();
this.AddGameStage<LogoStageExample>();
this.AddGameStage<PatchStageExample>();
this.AddGameStage<LoginStageExample>();
this.AddGameStage<EnterStageExample>();
}

public override void OnStart()
{
// 啟動第一個遊戲階段
this.ChangeGameStage<StartupStageExample>();
}

public override void OnUpdate(float dt = 0.0f)
{
base.OnUpdate(dt); // 必須呼叫 base,否則階段不會切換與更新
}
}

// 主入口 MonoBehaviour,驅動管理器運作
public class Main : MonoBehaviour
{
private void Start()
{
GSIManagerExample.DriveStart();
}

private void Update()
{
GSIManagerExample.DriveUpdate(Time.deltaTime);
}
}

之後即可於任意處透過 Default API 操作階段切換:

// 一般切換(下一次驅動更新時執行)
GSIManagerExample.ChangeStage<LoginStageExample>();

// 強制切換(立即執行)
GSIManagerExample.ChangeStage<LoginStageExample>(true);

提醒 可透過 Project 視窗右鍵範本快速建立管理器子類:Create → OxGFrame → GSI Frame → Template Scripts → Template GSIManager.cs (Game Stage Manager)

成員總覽

靜態方法(Default API)

繼承後直接以子類類名呼叫(如 GSIManagerExample.ChangeStage<U>())。

方法說明
GetCurrentId取得當前階段 id。
GetStage<U>取得已註冊的階段實例。
AddStage建立並註冊遊戲階段。
DeleteStage從快取移除已註冊的階段。
ChangeStage切換遊戲階段(一般/強制)。
DriveStart驅動啟動(於主入口 Start() 呼叫)。
DriveUpdate驅動更新(於主入口 Update() 呼叫)。
Start / Update已棄用的舊版驅動介面。

可覆寫方法(virtual)

方法說明
OnStartDriveStart 呼叫,覆寫後於此啟動第一個階段。
OnUpdateDriveUpdate 每幀呼叫,預設執行階段切換偵測與刷新。

一般方法(實例成員)

供子類內部使用(如建構子註冊、OnStart 啟動階段)。

方法說明
GetCurrentGameStageId取得當前階段 id(成員版本)。
GetGameStage<U>取得已註冊的階段實例(成員版本)。
AddGameStage註冊遊戲階段(成員版本)。
DeleteGameStage移除已註冊的階段(成員版本)。
ChangeGameStage一般切換(成員版本)。
ChangeGameStageForce立即強制切換(成員版本)。

受保護成員(繼承後可用)

成員型別說明
_dictGameStageDictionary<int, GSIBase>階段快取。
_incomingIdint待切換的目標階段 id。
_currentIdint當前階段 id(唯讀屬性)。
_currentGameStageGSIBase當前階段實例(唯讀屬性)。
GetInstance()static T取得管理器單例(延遲建立、執行緒安全)。
UpdateGameStage(float dt = 0.0f)void偵測階段切換並刷新當前階段(由 OnUpdate 呼叫)。
InitGameStage()void以當前 id 取出階段並啟動其初始流程。
ReleaseGameStage()void呼叫當前階段的 OnExit

GetCurrentId

public static int GetCurrentId()

回傳值

int — 當前階段 id;尚未切換過任何階段時為 0

說明

取得當前正在執行的階段 id。


GetStage<U>

public static U GetStage<U>() where U : GSIBase
public static U GetStage<U>(int id) where U : GSIBase

參數

參數型別說明
idint階段 id。未指定時,以 typeof(U).GetHashCode() 查找。

回傳值

U — 對應的階段實例;查無時回傳 null

說明

從快取取得已註冊的階段實例。無參數多載以型別 hash 查找,僅適用於未指定 id 註冊的階段;以自訂 id 註冊者請使用 GetStage<U>(int id)

範例

var loginStage = GSIManagerExample.GetStage<LoginStageExample>();

AddStage

public static void AddStage<U>() where U : GSIBase, new()
public static void AddStage<U>(int id) where U : GSIBase, new()
public static void AddStage(int id, GSIBase gameStage)

參數

參數型別說明
idint階段 id。未指定時,以 typeof(U).GetHashCode() 作為 id。
gameStageGSIBase已建立的階段實例(以指定 id 註冊)。

說明

建立(new U())並註冊遊戲階段至管理器快取,或以指定 id 直接註冊既有實例。註冊時自動呼叫 SetId(id) 設定階段辨識碼。

注意 相同 id 重複註冊會輸出警告並略過,不會覆蓋既有階段。

範例

// 以型別 hash 作為 id 註冊
GSIManagerExample.AddStage<FightStageExample>();

// 以自訂 id 註冊既有實例
GSIManagerExample.AddStage(0x01, new FightStageExample());

DeleteStage

public static void DeleteStage<U>() where U : GSIBase
public static void DeleteStage(int id)

參數

參數型別說明
idint階段 id。未指定時,以 typeof(U).GetHashCode() 查找。

說明

從快取移除已註冊的階段;查無時不動作。

注意 僅從快取移除,不會觸發該階段的 OnExit


ChangeStage

public static void ChangeStage<U>(bool force = false) where U : GSIBase
public static void ChangeStage(int id, bool force = false)

參數

參數型別說明
idint目標階段 id。泛型多載以 typeof(U).GetHashCode() 作為目標 id。
forcebool是否立即強制切換。
預設值false(一般切換)

說明

切換遊戲階段:

  • 一般切換force = false):記錄目標 id,於下一次驅動更新時執行切換;不允許切換至當前階段(輸出警告且不切換)。
  • 強制切換force = true):立即依序執行「舊階段 OnExit → 更新當前 id → 新階段初始流程」;不檢查是否為同一階段,可用於重新進入當前階段。

切換細節請參考切換流程GSIBase 生命週期與呼叫時序

範例

// 一般切換(下一次驅動更新時執行)
GSIManagerExample.ChangeStage<EnterStageExample>();

// 強制切換(立即執行,亦可重新進入當前階段)
GSIManagerExample.ChangeStage<EnterStageExample>(true);

DriveStart

public static void DriveStart()

說明

驅動啟動,內部轉呼叫單例的 OnStart。於主入口 MonoBehaviourStart() 中呼叫一次;首次呼叫會自動建立管理器單例(執行建構子完成階段註冊)。

範例

private void Start()
{
GSIManagerExample.DriveStart();
}

DriveUpdate

public static void DriveUpdate(float dt = 0.0f)

參數

參數型別說明
dtfloat幀間隔時間(一般傳入 Time.deltaTime)。
預設值0.0f

說明

驅動更新,內部轉呼叫單例的 OnUpdate,執行階段切換偵測與當前階段的 OnUpdate 刷新。於主入口 MonoBehaviourUpdate() 中每幀呼叫。

範例

private void Update()
{
GSIManagerExample.DriveUpdate(Time.deltaTime);
}

Start / Update(已棄用)

[Obsolete("Use DriveStart instead.")]
public static void Start()

[Obsolete("Use DriveUpdate instead.")]
public static void Update(float dt = 0.0f)

說明

舊版驅動介面,行為分別同 DriveStartDriveUpdate

注意 已標記 [Obsolete],請改用 DriveStart / DriveUpdate


OnStart

public virtual void OnStart()

說明

DriveStart 呼叫。基類為空實作,覆寫後通常於此呼叫 ChangeGameStage 啟動第一個遊戲階段。


OnUpdate

public virtual void OnUpdate(float dt)

參數

參數型別說明
dtfloat幀間隔時間,由 DriveUpdate 傳入。

說明

DriveUpdate 每幀呼叫。基類預設實作執行 UpdateGameStage(dt):偵測階段切換(一般切換於此時生效),並於當前階段的 runUpdate 開啟時呼叫其 OnUpdate

重要 覆寫時必須呼叫 base.OnUpdate(dt),否則階段將不會切換與更新。


GetCurrentGameStageId

public int GetCurrentGameStageId()

回傳值

int — 當前階段 id;尚未切換過任何階段時為 0

說明

GetCurrentId 的實例成員版本,供子類內部使用。


GetGameStage<U>

public U GetGameStage<U>() where U : GSIBase
public U GetGameStage<U>(int id) where U : GSIBase

參數

參數型別說明
idint階段 id。未指定時,以 typeof(U).GetHashCode() 查找。

回傳值

U — 對應的階段實例;查無時回傳 null

說明

GetStage<U> 的實例成員版本,供子類內部使用。


AddGameStage

public void AddGameStage<U>() where U : GSIBase, new()
public void AddGameStage<U>(int id) where U : GSIBase, new()
public void AddGameStage(int id, GSIBase gameStage)

參數

參數型別說明
idint階段 id。未指定時,以 typeof(U).GetHashCode() 作為 id。
gameStageGSIBase已建立的階段實例(以指定 id 註冊)。

說明

AddStage 的實例成員版本,於子類建構子中註冊各階段時使用(建構子中不可呼叫靜態 Default API)。註冊時自動呼叫 gameStage.SetId(id);相同 id 重複註冊會輸出警告並略過;傳入 null 實例會輸出錯誤並略過。

範例

public GSIManagerExample()
{
this.AddGameStage<StartupStageExample>();
}

DeleteGameStage

public void DeleteGameStage<U>() where U : GSIBase
public void DeleteGameStage(int id)

參數

參數型別說明
idint階段 id。未指定時,以 typeof(U).GetHashCode() 查找。

說明

DeleteStage 的實例成員版本;查無時不動作。不允許刪除當前運行中的階段(會輸出警告並略過)。


ChangeGameStage

public void ChangeGameStage<U>() where U : GSIBase
public void ChangeGameStage(int id)

參數

參數型別說明
idint目標階段 id。泛型多載以 typeof(U).GetHashCode() 作為目標 id。

說明

一般切換的實例成員版本:記錄目標 id,於下一次驅動更新時執行切換;不允許切換至當前階段;目標階段不存在時,會輸出錯誤並取消切換。通常於 OnStart 中呼叫以啟動第一個階段。


ChangeGameStageForce

public void ChangeGameStageForce<U>() where U : GSIBase
public void ChangeGameStageForce(int id)

參數

參數型別說明
idint目標階段 id。泛型多載以 typeof(U).GetHashCode() 作為目標 id。

說明

強制切換的實例成員版本:立即依序執行「舊階段 OnExit → 更新當前 id → 新階段初始流程」;不檢查是否為同一階段,可用於重新進入當前階段。