1. 程式人生 > >C# 設計模式(十)狀態模式(unity演示)

C# 設計模式(十)狀態模式(unity演示)

1、引言

  在我們平常的遊戲開發中,我們常常遇到遊戲物體狀態切換的問題,比如:人物的運動狀態,有待機,行走、奔跑、攻擊和死亡狀態等。今天我們將用狀態模式來演示遊戲物體狀態切換辦法。下面我們一起來了解下吧。

2、狀態模式詳細介紹

2.1、定義

  • 狀態模式(State Pattern)
      允許一個物件在其內部狀態改變時自動改變其行為,物件看起來就像是改變了它的類。

2.2、解決的問題

  主要解決的是當控制一個物件狀態轉換的條件表示式過於複雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類當中,可以把複雜的邏輯簡化

2.3、狀態模式的結構圖

下面時該模式的UML類圖:
這裡寫圖片描述

  從上圖可知,狀態者模式涉及以下三個角色:

  • 抽象狀態類(State類):抽象狀態類定義了一個具體狀態類需要實現的行為約定。
  • 具體狀態類(ConcreteState類):具體狀態類,實現抽象狀態類的每個行為。
  • 具體類(Context類) :維護一個State類的一個例項,該例項標識著當前物件的狀態。

2.4、類圖實現

詳細程式碼如下:
抽象狀態類:

/// <summary>
/// 抽象狀態類
/// </summary>
abstract class State
{
    /// <summary>
    /// 定義一個與Context相關的行為
    ///
</summary>
/// <param name="context"></param> public abstract void Handle(Context context); }

Context類:

class Context
{
   private State state;
   public State State
   {
       get { return state; }
       set
       {
           state = value;
           Console.WriteLine("當前狀態:"
+ state.GetType().Name); } } /// <summary> /// 建構函式 /// </summary> /// <param name="state">預設Context的初始狀態</param> public Context(State state) { this.state = state; } /// <summary> /// 對親求做處理,並設定下一狀態 /// </summary> public void Request() { state.Handle(this); } }

具體狀態類A:

/// <summary>
/// 具體狀態
/// </summary>
class ConcreteStateA : State
{
    /// <summary>
    /// 設定ConcreteStateA的下一狀態是ConcreteStateB
    /// </summary>
    /// <param name="context"></param>
    public override void Handle(Context context)
    {
        context.State = new ConcreteStateB();
    }
}

具體狀態類B:

/// <summary>
/// 具體狀態
/// </summary>
class ConcreteStateB : State
{
    /// <summary>
    /// 設定ConcreteStateB的下一狀態是ConcreteStateA
    /// </summary>
    /// <param name="context"></param>
    public override void Handle(Context context)
    {
        context.State = new ConcreteStateA();
    }
}

測試一下:

//初始狀態設為A
Context cn = new Context(new ConcreteStateA());

//不斷請求改變狀態
cn.Request();
cn.Request();
cn.Request();
cn.Request();

測試結果:

當前狀態:ConcreteStateB
當前狀態:ConcreteStateA
當前狀態:ConcreteStateB
當前狀態:ConcreteStateA

  通過以上程式碼,狀態模式的基本思路已經有了,下面我們來舉例應用一下.

2.5、C#應用舉例

  • 情景:通過開關控制燈的開關
  • 程式碼如下:
    燈類
/// <summary>  
/// 電燈類,對應模式中的Context類  
/// </summary>  
public class Light
{
    private LightState state;
    public LightState State
    {
        get { return state; }
        set { state = value; }
    }

    public Light(LightState state)
    {
        this.state = state;
    }

    /// <summary>  
    /// 按下電燈開關  
    /// </summary>  
    public void PressSwich()
    {
        state.PressSwich(this);
    }
}

燈的狀態類:

/// <summary>  
/// 抽象的電燈狀態類,相當於State類  
/// </summary>  
public abstract class LightState
{
    public abstract void PressSwich(Light light);
}

開啟狀態:

/// <summary>  
/// 具體狀態類, 開  
/// </summary>  
public class On : LightState
{
    /// <summary>  
    /// 在開狀態下,按下開關則切換到關的狀態。  
    /// </summary>  
    /// <param name="light"></param>  
    public override void PressSwich(Light light)
    {
        Console.WriteLine("Turn off the light.");
        light.State = new Off();
    }
}

關閉狀態:

/// <summary>  
/// 具體狀態類,關  
/// </summary>  
public class Off : LightState
{
    /// <summary>  
    /// 在關狀態下,按下開關則開啟電燈。  
    /// </summary>  
    /// <param name="light"></param>  
    public override void PressSwich(Light light)
    {
        Console.WriteLine("Turn on the light.");
        light.State = new On();
    }
}

測試一下:

// 初始化電燈,原始狀態為關  
Light light = new Light(new Off());

// 第一次按下開關,開啟電燈  
light.PressSwich();
// 第二次按下開關,關閉電燈  
light.PressSwich();

測試結果如下:

Turn on the light.
Turn off the light.

  通過以上例子的瞭解我想對狀態模式的原理,已經瞭解了,下面我們來看看它的優缺點。

3、狀態模式優缺點

  • 優點:
    1. 將狀態判斷邏輯放在每個狀態類裡面,可以簡化判斷的邏輯。
    2. 當有新的狀態出現時,可以通過新增新的狀態類來進行擴充套件,擴充套件性好。
  • 缺點:
    1. 如果狀態過多的話,會導致有非常多的狀態類,加大了開銷。

4、狀態模式適用場景

在以下情況下可以考慮使用狀態者模式。

  • 當一個物件狀態轉換的條件表示式過於複雜時可以使用狀態者模式。

    把狀態的判斷邏輯轉移到表示不同狀態的一系列類中,可以把複雜的判斷邏輯簡單化。

  • 當一個物件行為取決於它的狀態,並且它需要在執行時刻根據狀態改變它的行為時,就可以考慮使用狀態者模式。

5、應用舉例(unity)

  • 情景 :控制物體的上下移動,到達螢幕邊緣時,往相反的方向運動
    為了方便我們把所有的類寫在了一個腳本里。當然,這是我們不推薦的做法。把這個指令碼掛載在攝像機上。準備UI如圖所示:
    這裡寫圖片描述
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour {

    private GameObject obj;
    private Controller ctrl;
    private GameObject text;

    void Awake()
    {
        text = GameObject.Find("Text");
        text.GetComponent<Text>().color = Color.blue;
        text.GetComponent<Text>().horizontalOverflow = HorizontalWrapMode.Overflow;
    }

    void Start ()
    {
        obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
        obj.GetComponent<MeshRenderer>().material.color = Color.red;
        //obj.name = "Cube";
        obj.transform.position = Vector3.zero;
        ctrl = new Controller(obj, new MoveToUp());

    }
    void Update()
    {
        ctrl.Handle();
        // Debug.Log(obj.transform.position);
        text.GetComponent<Text>().text = ctrl.MoveState.ToString() + " " + obj.transform.position.ToString();
    }
}

/// <summary>
/// 運動控制器
/// </summary>
public class Controller
{
    private MoveState moveState;
    public MoveState MoveState
    {
        get { return moveState; }
        set { moveState = value; }
    }

    private GameObject obj;
    public GameObject Obj
    {
        get { return obj; }
        set { obj = value; }
    }

    public Controller(GameObject obj,MoveState moveState)
    {
        this.obj = obj;
        this.moveState = moveState;
    }

    public void Handle()
    {
        moveState.Handle(this);
    }
}

/// <summary>
/// 物體的運動狀態
/// </summary>
public abstract class MoveState
{
    public abstract void Handle(Controller ctrl);   
}

/// <summary>
/// 向上的運動狀態
/// </summary>
public class MoveToUp : MoveState
{
    public override void Handle(Controller ctrl)
    {
        GameObject obj = ctrl.Obj;

        if (Camera.main.WorldToViewportPoint(obj.transform.position).y < 1)
        {
            obj.transform.Translate(Vector3.up * Time.deltaTime * 15);
        }
        else
        {
            ctrl.MoveState = new MoveToDown();
        }
    }
}

/// <summary>
/// 向下的運動狀態
/// </summary>
public class MoveToDown : MoveState
{
    public override void Handle(Controller ctrl)
    {
        GameObject obj = ctrl.Obj;

        if (Camera.main.WorldToViewportPoint(obj.transform.position).y > 0)
        {
            obj.transform.Translate(Vector3.down * Time.deltaTime * 15);
        }
        else
        {
            ctrl.MoveState = new MoveToUp();
        }
    }
}

測試結果如下:
這裡寫圖片描述
這裡寫圖片描述

6、總結

  狀態者模式是對物件狀態的抽象,從而把物件中對狀態複雜的判斷邏輯移到各個狀態類裡面,從而簡化邏輯判斷。

7、unity工程下載

  在文章的最後我們給出上述例子的工程下載連結,方便朋友們探討交流!本次演示版本Unity5.6.3f1(64-bit),VS2015需要下載的朋友,請點選這裡下載

The End
  好了,今天的分享就到這裡,如有不足之處,還望大家及時指正,隨時歡迎探討交流!!!

喜歡的朋友們,請幫頂、點贊、評論!您的肯定是我寫作的不竭動力!