在遊戲開發過程中,遊戲中的部分對象可能會根據不同的情況做出不同的行為,我們把這種對象稱為有狀态的對象,而把影響對象行為的一個或多個動态變化的屬性稱為狀态。當有狀态的對象與外部事件産生互動時,其内部狀态就會發生改變,進而使其行為也發生改變。如人都有高興和傷心的時候,不同的情緒有不同的行為,當然外界也會影響其情緒變化。
對這種有狀态的對象程式設計,傳統的解決方案是:将這些所有可能發生的情況全都考慮到,然後使用 if-else 或 switch-case 語句來做狀态判斷,再進行不同情況的處理。但是顯然這種做法對複雜的狀态判斷存在天然弊端,條件判斷語句會過于臃腫,可讀性差,且不具備擴充性,維護難度也大。且增加新的狀态時要添加新的 if-else 語句,這違背了“開閉原則”,不利于程式的擴充。
優點
1、封裝了轉換規則。
2、枚舉可能的狀态,在枚舉狀态之前需要确定狀态種類。
3、将所有與某個狀态有關的行為放到一個類中,并且可以友善地增加新的狀态,隻需要改變對象狀态即可改變對象的行為。
4、允許狀态轉換邏輯與狀态對象合成一體,而不是某一個巨大的條件語句塊。
5、可以讓多個環境對象共享一個狀态對象,進而減少系統中對象的個數。
缺點
1、狀态模式的使用必然會增加系統類和對象的個數。
2、狀态模式的結構與實作都較為複雜,如果使用不當将導緻程式結構和代碼的混亂。
3、狀态模式對"開閉原則"的支援并不太好,對于可以切換狀态的狀态模式,增加新的狀态類需要修改那些負責狀态轉換的源代碼,否則無法切換到新增狀态,而且修改某個狀态類的行為也需修改對應類的源代碼。
結構圖
代碼示例
抽象狀态類
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AI.Enemy
{
public abstract class EnemyState
{
public abstract void Handle(EnemyContext enemyBase);
}
}
具體狀态類
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AI.Enemy
{
public class EnemyStateWalk : EnemyState
{
public override void Handle(EnemyContext enemyBase)
{
Debug.Log("我叫:"+enemyBase.EnemyName + " 目前是Walk狀态");
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AI.Enemy
{
public class EnemyStateDeath : EnemyState
{
public override void Handle(EnemyContext enemyBase)
{
Debug.Log("我叫:" + enemyBase.EnemyName + " 目前是Death狀态");
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AI.Enemy
{
public class EnemyStateAttack : EnemyState
{
public override void Handle(EnemyContext enemyBase)
{
Debug.Log("我叫:" + enemyBase.EnemyName + " 目前是Attack狀态");
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AI.Enemy
{
public class EnemyStateIdle : EnemyState
{
public override void Handle(EnemyContext enemyBase)
{
Debug.Log("我叫:" + enemyBase.EnemyName + " 目前是Idle狀态");
}
}
}
環境類(Context 類)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AI.Enemy
{
public class EnemyContext
{
private string enemyName;
public string EnemyName
{
get { return enemyName; }
}
/// <summary>
/// 狀态
/// </summary>
private EnemyState enemyState;
/// <summary>
/// 定義初始狀态
/// </summary>
/// <param name="enemyState"></param>
/// <param name="enemyName"></param>
public EnemyContext(EnemyState enemyState,string enemyName)
{
this.enemyState = enemyState;
this.enemyName = enemyName;
}
/// <summary>
/// 對請求做處理,進入下一個狀态
/// </summary>
public void Request()
{
enemyState.Handle(this);
}
}
}
Test類
通過枚舉改變目标狀态
using AI.Enemy;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum EnemyStates
{
Idle,
Walk,
Attack,
Death
}
public class EnemyTest : MonoBehaviour
{
private EnemyContext enemyBase;
public EnemyStates enemyStates;
void Start()
{
}
private void Update()
{
switch (enemyStates)
{
case EnemyStates.Idle:
enemyBase = new EnemyContext(new EnemyStateIdle(),transform.name);
enemyBase.Request();
break;
case EnemyStates.Walk:
enemyBase = new EnemyContext(new EnemyStateWalk(), transform.name);
enemyBase.Request();
break;
case EnemyStates.Attack:
enemyBase = new EnemyContext(new EnemyStateAttack(), transform.name);
enemyBase.Request();
break;
case EnemyStates.Death:
enemyBase = new EnemyContext(new EnemyStateDeath(), transform.name);
enemyBase.Request();
break;
default:
break;
}
}
}
最後陳述
當然在這裡主要介紹一下狀态模式的核心思想,這在遊戲開發中是遠遠不夠用的,在遊戲中我們一般用有限狀态機來實作人物或者NPC的狀态管理,有限狀态機的核心思想就是使用狀态模式編寫,這裡就不做過多介紹了,在之後的文章裡再介紹有限狀态機的具體使用和邏輯處理吧。