概述请看 参考博客
状态模式在游戏中可以用于切换人物动作、切换场景等。
本文介绍使用状态模式切换游戏场景。
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
可以判断此场景是否加载完毕。