Unity---游戲設計模式(1)狀態模式





概述請看 參考博客

狀態模式在游戲中可以用於切換人物動作、切換場景等。
本文介紹使用狀態模式切換游戲場景。

1、狀態模式原型

狀態模式原型的UML圖

狀態模式代碼

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Client : MonoBehaviour
{
    private void Start()
    {
        IState state1 = new ConcreteState1();
        IState state2 = new ConcreteState2();
        //默認狀態state1
        Context context = new Context(state1);

        //狀態切換
        context.Request();
        context.Request();
    }
}

/// <summary>
/// 維護一個狀態實例,定義當前的狀態
/// </summary>
public class Context
{
    private IState mNowState;
    public IState NowState { set { mNowState = value; } }

    public Context(IState state)
    {
        mNowState = state;
    }

    public void Request()
    {
        mNowState.Handle(this);
    }
}

/// <summary>
/// 抽象狀態類
/// </summary>
public abstract class IState
{
    public abstract void Handle(Context context);
}

//兩個具體狀態子類
public class ConcreteState1 : IState
{
    public override void Handle(Context context)
    {
        Debug.Log("當前狀態是狀態1.");
        //狀態1結束后切換為狀態2
        context.NowState = new ConcreteState2();
    }
}
public class ConcreteState2 : IState
{
    public override void Handle(Context context)
    {
        Debug.Log("當前狀態是狀態2.");
        //狀態2結束后切換為狀態1
        context.NowState = new ConcreteState1();
    }
}

效果

2、狀態模式實例:用於游戲切換場景

假如現在我們需要設置三個場景。
場景1是Logo界面,顯示一下logo后會進入場景2;
場景2是主菜單界面,主菜單點擊可以進入游戲界面;
場景3是游戲界面,和命令模式實例相接。請查看命令模式(13)篇實例。

實例UML圖

實例代碼

GameLoop類

public class GameLoop : MonoBehaviour
{
    private SceneStateController mSceneStateController;

    private void Awake()
    {
        DontDestroyOnLoad(this.gameObject);

        mSceneStateController = new SceneStateController();
		//設置默認狀態
        mSceneStateController.ChangeState(new StartSceneState(mSceneStateController), false);
    }

    private void Update()
    {
        if (mSceneStateController != null)
        {
            mSceneStateController.StateUpdate();
        }
    }
}

SceneStateController類

/// <summary>
/// 場景狀態控制類
/// </summary>
public class SceneStateController
{
    private ISceneState mNowSceneState;
    public ISceneState NowSceneState { get; set; }

    /// <summary>
    /// 用於判斷場景是否加載完成
    /// </summary>
    private AsyncOperation mAsyncOperation;

    /// <summary>
    /// 當進入場景時執行EnterScene,且只執行一次
    /// </summary>
    private bool mDoOnceEnterScene = false;


    /// <summary>
    /// 每幀更新場景
    /// </summary>
    public void UpdateState()
    {
        //正在加載場景過程中時不更新
        if (mAsyncOperation != null && mAsyncOperation.isDone == false) return;

		//剛進入游戲場景時更新一次
        if (mAsyncOperation.isDone && mDoOnceEnterScene == false)
        {
            mNowSceneState.EnterScene();
            mDoOnceEnterScene = true;
        }

		//Update一直更新
        if (mNowSceneState != null)
        {
            mNowSceneState.UpdateScene();
        }
    }

    /// <summary>
    /// 改變場景
    /// </summary>
    public void ChangeState(ISceneState state, bool isLoadScene = true)
    {
        if (mNowSceneState != null)
        {
            mNowSceneState.LeaveScene();
        }
        mNowSceneState = state;

        //是否需要加載場景,第一個不需要加載!
        //如果第一個也加載場景,那么會出bug死機。(我被這個bug搞了好大一會兒)
        if (isLoadScene)
        {
            mAsyncOperation = SceneManager.LoadSceneAsync(state.SceneName);
            mDoOnceEnterScene = false;
        }
        else
        {
            mNowSceneState.EnterScene();
            mDoOnceEnterScene = true;
        }
    }
}

ISceneState類

/// <summary>
/// 場景狀態基類
/// </summary>
public class ISceneState
{
    //根據場景名字加載場景
    private string mSceneName;
    public string SceneName { get { return mSceneName; } }

    private SceneStateController mSceneStateController;

    public ISceneState(string sceneName, SceneStateController sceneStateController)
    {
        mSceneName = sceneName;
        mSceneStateController = sceneStateController;
    }

    //場景的進入、更新、離開
    public virtual void EnterScene() { }
    public virtual void UpdateScene() { }
    public virtual void LeaveScene() { }
}

三個場景子類

/// <summary>
/// 開始界面場景
/// </summary>
public class StartSceneState : ISceneState1
{
    public StartSceneState(SceneStateController controller)
        :base("01StartScene",controller)
    {
    }

	//Logo圖片
	private Image mLogo;

    public override void EnterScene()
    {
        mLogo = GameObject.Find("Logo").GetComponent<Image>();
        mLogo.color = Color.black;
    }
    public override void UpdateScene()
    {
		//設置logo漸漸顯示
        mLogo.DOColor(Color.white, 3).OnComplete(() =>
        {
            mSceneStateController.SetState(new MainMenuSceneState(mSceneStateController));
        });
    }
}
/// <summary>
/// 主菜單界面場景
/// </summary>
public class MainMenuSceneState : ISceneState1
{
    public MainMenuSceneState(SceneStateController controller)
        : base("02MainMenuScene", controller)
    {
    }

	//當用戶點擊按鈕時,進入游戲場景
    private Button mGameStartBtn;

    public override void EnterScene()
    {
        mGameStartBtn = GameObject.Find("GameStartButton").GetComponent<Button>();
        mGameStartBtn.onClick.AddListener(OnGameStartBtnClick);
    }

    private void OnGameStartBtnClick()
    {
        mSceneStateController.SetState(new GameSceneState(mSceneStateController));
    }
}
/// <summary>
/// 游戲界面場景
/// </summary>
public class GameSceneState : ISceneState1
{
    public GameSceneState(SceneStateController controller)
        : base("03GameScene", controller)
    {
    }
}

效果

3、狀態模式優缺點

優點

  1. 降低狀態類間的耦合性
  2. 代碼結構化,易維護、拓展

缺點

  1. 會創建大量的類
  2. 代碼結構復雜

4、新知識

3.1 DontDestroyOnLoad()

DontDestroyOnLoad(gameObject):讓某個對象在切換場景時不釋放。
假如想讓某個音樂在每個場景都播放。使用這個,在切換場景時就不會被銷毀。

3.1 AsyncOperation

AsyncOperation mAsyncOperation;
mAsyncOperation = SceneManager.LoadSceneAsync();
此方法用於異步加載場景。

mAsyncOperation.isDone
可以判斷此場景是否加載完畢。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM