概述請看 參考博客
狀態模式在游戲中可以用於切換人物動作、切換場景等。
本文介紹使用狀態模式切換游戲場景。
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、狀態模式優缺點
優點
- 降低狀態類間的耦合性
- 代碼結構化,易維護、拓展
缺點
- 會創建大量的類
- 代碼結構復雜
4、新知識
3.1 DontDestroyOnLoad()
DontDestroyOnLoad(gameObject):讓某個對象在切換場景時不釋放。
假如想讓某個音樂在每個場景都播放。使用這個,在切換場景時就不會被銷毀。
3.1 AsyncOperation
AsyncOperation mAsyncOperation;
mAsyncOperation = SceneManager.LoadSceneAsync();
此方法用於異步加載場景。
mAsyncOperation.isDone
可以判斷此場景是否加載完畢。