天天看點

Unity 設計模式——狀态模式

在遊戲開發過程中,遊戲中的部分對象可能會根據不同的情況做出不同的行為,我們把這種對象稱為有狀态的對象,而把影響對象行為的一個或多個動态變化的屬性稱為狀态。當有狀态的對象與外部事件産生互動時,其内部狀态就會發生改變,進而使其行為也發生改變。如人都有高興和傷心的時候,不同的情緒有不同的行為,當然外界也會影響其情緒變化。

對這種有狀态的對象程式設計,傳統的解決方案是:将這些所有可能發生的情況全都考慮到,然後使用 if-else 或 switch-case 語句來做狀态判斷,再進行不同情況的處理。但是顯然這種做法對複雜的狀态判斷存在天然弊端,條件判斷語句會過于臃腫,可讀性差,且不具備擴充性,維護難度也大。且增加新的狀态時要添加新的 if-else 語句,這違背了“開閉原則”,不利于程式的擴充。

優點

1、封裝了轉換規則。

2、枚舉可能的狀态,在枚舉狀态之前需要确定狀态種類。

3、将所有與某個狀态有關的行為放到一個類中,并且可以友善地增加新的狀态,隻需要改變對象狀态即可改變對象的行為。

4、允許狀态轉換邏輯與狀态對象合成一體,而不是某一個巨大的條件語句塊。

5、可以讓多個環境對象共享一個狀态對象,進而減少系統中對象的個數。

缺點

 1、狀态模式的使用必然會增加系統類和對象的個數。

2、狀态模式的結構與實作都較為複雜,如果使用不當将導緻程式結構和代碼的混亂。

3、狀态模式對"開閉原則"的支援并不太好,對于可以切換狀态的狀态模式,增加新的狀态類需要修改那些負責狀态轉換的源代碼,否則無法切換到新增狀态,而且修改某個狀态類的行為也需修改對應類的源代碼。

結構圖

Unity 設計模式——狀态模式

 代碼示例

 抽象狀态類

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的狀态管理,有限狀态機的核心思想就是使用狀态模式編寫,這裡就不做過多介紹了,在之後的文章裡再介紹有限狀态機的具體使用和邏輯處理吧。

繼續閱讀