探秘Unity游戲開發(fā)中的狀態(tài)設(shè)計(jì)模式
一、狀態(tài)模式定義
狀態(tài)模式(state)在GoF中的解釋:
讓一個(gè)對象的行為隨著內(nèi)部狀態(tài)的改變而改變,而該對象也想換了類一樣。
二、實(shí)現(xiàn)范例
結(jié)構(gòu)圖:

Context(狀態(tài)擁有者)
/// <summary>
/// Describe:狀態(tài)擁有者
/// Note:是一個(gè)具有“狀態(tài)”屬性的類,可以制定相關(guān)的接口,讓外界能夠得知狀態(tài)的改變或通過操作讓狀態(tài)改變。
/// 比如:游戲角色有潛行,攻擊、施法等狀態(tài);好友上線,脫機(jī)、忙碌等狀態(tài);
/// GoF使用TCP聯(lián)網(wǎng)為例,有已連接、等待連接、短線等狀態(tài);
/// </summary>
public class Context
{
State state = null;
public void Request(int Value)
{
state.Handle(Value);
}
public void SetState(State state)
{
this.state = state;
}
}State(狀態(tài)接口類)
/// <summary>
/// Describe:狀態(tài)接口類
/// Note:指定狀態(tài)的接口,負(fù)責(zé)規(guī)范Context(狀態(tài)擁有者)在特定狀態(tài)下要表現(xiàn)的行為。
/// </summary>
public abstract class State
{
protected Context m_Context = null;
public State(Context context)
{
m_Context = context;
}
public abstract void Handle(int Value);
}ConcreteState(具體狀態(tài)類)
/*
Describe:具體狀態(tài)類
Note:
1、繼承自State(狀態(tài)接口類)
2、實(shí)現(xiàn)Context(狀態(tài)擁有者)在特定狀態(tài)下該有的行為。
*/
//狀態(tài)A
public class ConcreteStateA : State
{
public ConcreteStateA(Context context) : base(context)
{ }
public override void Handle(int Value)
{
if (Value>10)
{
m_Context.SetState(new ConcreteStateB(m_Context));
}
}
}
//狀態(tài)A
public class ConcreteStateB : State
{
public ConcreteStateB(Context context) : base(context)
{ }
public override void Handle(int Value)
{
if (Value > 10)
{
m_Context.SetState(new ConcreteStateC(m_Context));
}
}
}
//狀態(tài)A
public class ConcreteStateC : State
{
public ConcreteStateC(Context context) : base(context)
{ }
public override void Handle(int Value)
{
if (Value > 10)
{
m_Context.SetState(new ConcreteStateA(m_Context));
}
}
}三、使用狀態(tài)模式(state)實(shí)現(xiàn)游戲場景的轉(zhuǎn)換
場景結(jié)構(gòu)示意圖:

ISceneState 場景類接口
/// <summary>
/// Describe:場景類接口
/// Note:定義場景轉(zhuǎn)換和執(zhí)行時(shí)需要調(diào)用的方法。
/// </summary>
public class ISceneState
{
//狀態(tài)名稱
private string m_StateName = "ISceneState";
public string StateName
{
get => m_StateName;
set => m_StateName = value;
}
//控制者
protected SceneStateController m_Controller = null;
//建造者
public ISceneState(SceneStateController controller)
{
m_Controller = controller;
}
//開始
public virtual void StateBegin() { }
//結(jié)束
public virtual void StateEnd() { }
//更新
public virtual void StateUpdate() { }
//結(jié)束
public override string ToString()
{
return $"[I_SceneState: StateName={StateName}]";
}
}三個(gè)可切換場景(主畫面場景,主畫面場景,戰(zhàn)斗場景)
主畫面場景
/// <summary>
/// Describe:開始場景
/// </summary>
public class StartState : ISceneState
{
public StartState(SceneStateController controller) : base(controller)
{
this.StateName="StartState";
}
//開始
public override void StateBegin()
{
//可在此進(jìn)行游戲數(shù)據(jù)加載和初始化
}
//更新
public override void StateUpdate()
{
//更換為
m_Controller.SetState(new MainMenuState(m_Controller), "MainMenuScene");
}
}主畫面場景
/// <summary>
/// Author:maki
/// Time:2021-12-2
/// Describe:主畫面場景
/// </summary>
public class MainMenuState:ISceneState
{
private Button tmpBtn;
public MainMenuState(SceneStateController controller) : base(controller)
{
this.StateName = "MainMenuState";
}
//開始
public override void StateBegin()
{
//獲取開始按鈕
// tmpBtn=UITool.GetUIComponent<Button>("StartGameBtn");
tmpBtn?.onClick.AddListener(()=>OnStartGameBtnClick(tmpBtn));
}
//開始游戲
private void OnStartGameBtnClick(Button tmpBtn)
{
m_Controller.SetState(new BattleState(m_Controller), "BattleScene");
}
}戰(zhàn)斗場景
/// <summary>
/// Author:maki
/// Time:2021-12-2
/// Describe:戰(zhàn)斗場景
/// </summary>
public class BattleState:ISceneState
{
public BattleState(SceneStateController controller) : base(controller)
{
this.StateName = "BattleState";
}
//開始
public override void StateBegin()
{
}
//結(jié)束
public override void StateEnd()
{
}
//更新
public override void StateUpdate()
{
//輸入
InputProcess();
//游戲邏輯
//游戲是否結(jié)束
m_Controller.SetState(new MainMenuState(m_Controller), "MainMenuState");
}
//輸入
private void InputProcess()
{
//玩家輸入判斷程序代碼。。。
}
}SceneStateController-場景狀態(tài)控制者
/// <summary>
/// Author:maki
/// Time:2021-12-2
/// Describe:場景狀態(tài)的擁有者(Context),保持當(dāng)前游戲場景狀態(tài),并作為與GameLoop類互動的接口,
/// 除此以外,也是U3D場景轉(zhuǎn)換的地方。
/// </summary>
public class SceneStateController
{
private ISceneState m_State;
private bool m_bRunBegin = false;
public SceneStateController() { }
//設(shè)置狀態(tài)
public void SetState(ISceneState state, string loadSceneName)
{
m_bRunBegin = false;
//載入場景
LoadScene(loadSceneName);
//通知前一個(gè)State結(jié)束
if (m_State != null)
m_State.StateEnd();
//設(shè)置
m_State = state;
}
//載入場景
private void LoadScene(string loadSceneName)
{
if (string.IsNullOrEmpty(loadSceneName))
{
return;
}
SceneManager.LoadScene(loadSceneName);
// Application.LoadLevel(loadSceneName); 過時(shí)
}
//更新
public void StateUpdate()
{
//通知新的State開始
if (m_State != null && m_bRunBegin == false)
{
m_State.StateBegin();
m_bRunBegin = true;
}
if (m_State != null)
{
m_State.StateUpdate();
}
}
}GameLoop-游戲主循環(huán)類
/// <summary>
/// Author:maki
/// Time:2021-12-2
/// Describe:游戲主循環(huán)類
/// Note:包含初始化游戲和定期調(diào)用更新操作
/// </summary>
public class GameLoop:MonoBehaviour
{
//場景狀態(tài)
SceneStateController sceneStateController = new SceneStateController();
void Awake()
{
// 轉(zhuǎn)換場景不會被刪除
GameObject.DontDestroyOnLoad(gameObject);
//隨機(jī)數(shù)種子
Random.seed = (int)System.DateTime.Now.Ticks;
}
private void Start()
{
//設(shè)置起始場景
sceneStateController.SetState(new StartState(sceneStateController), "");
}
private void Update()
{
sceneStateController.StateUpdate();
}
}四、使用狀態(tài)模式的優(yōu)點(diǎn)
優(yōu)點(diǎn)
- 減少錯(cuò)誤的發(fā)生并降低維護(hù)難度;
- 狀態(tài)執(zhí)行環(huán)境單一化;
- 項(xiàng)目之間可以共享場景;
具體來說,使用狀態(tài)模式可以清楚地了解某個(gè)場景狀態(tài)執(zhí)行時(shí)所需要配合使用的類對象,并且減少因新增狀態(tài)而需要大量修改現(xiàn)有程序代碼的維護(hù)成本。
缺點(diǎn)
在應(yīng)用有大量狀態(tài)的系統(tǒng)時(shí),會遇到“產(chǎn)生過多狀態(tài)類”的情況,此時(shí)會伴隨著類爆炸的問題。
到此這篇關(guān)于探秘Unity游戲開發(fā)中的狀態(tài)設(shè)計(jì)模式的文章就介紹到這了,更多相關(guān)Unity狀態(tài)模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C# Distinct和重寫IEqualityComparer時(shí)要知道的二三事
這篇文章主要給大家介紹了關(guān)于C# Distinct和重寫IEqualityComparer時(shí)要知道的二三事,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
關(guān)于C# 5.0 CallerMemberName CallerFilePath CallerLineNumber 在.
本篇文章,小編為大家介紹關(guān)于C# 5.0 CallerMemberName CallerFilePath CallerLineNumber 在.NET4中的使用介紹方法,有需要的朋友可以參考一下2013-04-04
C#中Dictionary<TKey,TValue>排序方式的實(shí)現(xiàn)
這篇文章主要介紹了C#中Dictionary<TKey,TValue>排序方式的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
C#基于時(shí)間輪調(diào)度實(shí)現(xiàn)延遲任務(wù)詳解
在很多.net開發(fā)體系中開發(fā)者在面對調(diào)度作業(yè)需求的時(shí)候一般會選擇三方開源成熟的作業(yè)調(diào)度框架來滿足業(yè)務(wù)需求,但是有些時(shí)候可能我們只是需要一個(gè)簡易的延遲任務(wù)。本文主要分享一個(gè)簡易的基于時(shí)間輪調(diào)度的延遲任務(wù)實(shí)現(xiàn),需要的可以參考一下2022-12-12

