状态模式
- 角色状态
- AI状态
- 账号登陆状态
- 场景状态
- 动画机状态
1.简单的状态控制
https://www.youtube.com/watch?v=r-RCfmQqLA0
(作者本意为避免if嵌套,避免定义过多变量进行开关控制)
有人指出这不是一个好办法,因为在添加状态时要不断为新状态创建新的类,一个方法占据一个单独的类,过多的类反而使管理和可读性变差。
状态管理利用字典返回一个状态标志。
class Character:MonoBenaviour
{
characterState state;
void Update()
{
state = state.handleInput();
}
}
class characterState
{
virtual public characterState handleInput(){return this;}
}
class JumpingState:characterState
{
override public characterState handleInput(){return this;}
}
class IdleState:characterState
{
override public characterState handleInput()
{
if(按下跳跃键)
return new JumpingState();
return this;
}
}
2.场景执行用状态管理
https://zhuanlan.zhihu.com/p/85095810
和上个部分类似,说明更详细一些
以场景的转换作为示例展示状态模式
- GameLoop:游戏的主循环,继承自MonoBehavior,包含了游戏的初始化和循环更新操作,同时也是设置起始场景的地方。
- SceneStateController:场景状态的拥有者,对应上面说的Context(状态拥有者),执行场景转换的地方,在GameLoop中完成创建。
- SceneState:场景的抽象类,定义了场景转换和执行时所需的方法。
StartState、MainMenuState、BattleState:分别对应了开始场景、主界面场景、战斗场景的具体实现类。
public class GameLoop : MonoBehaviour
{
// 场景状态持有者
SceneStateController m_SceneStateController = new SceneStateController();
//
void Awake()
{
// 保证场景切换时不会被删除
GameObject.DontDestroyOnLoad( this.gameObject );
}
// Use this for initialization
void Start ()
{
// 设定起始场景
m_SceneStateController.SetState(new StartState(m_SceneStateController), "");
}
// Update is called once per frame
void Update ()
{
m_SceneStateController.StateUpdate();
}
}
public class SceneStateController
{
private SceneState m_State;
private bool m_bRunBegin = false;
public SceneStateController()
{}
// 设定状态
public void SetState(SceneState State, string LoadSceneName)
{
//Debug.Log ("SetState:"+State.ToString());
m_bRunBegin = false;
// 载入场景
LoadScene(LoadSceneName);
// 通知前一个State結束
if( m_State != null )
m_State.StateEnd();
// 设定
m_State=State;
}
// 载入
private void LoadScene(string LoadSceneName)
{
if( LoadSceneName==null || LoadSceneName.Length == 0 )
return ;
SceneManager.LoadSceneAsync(LoadSceneName);
}
// 更新
public void StateUpdate()
{
// 是否還在載入
if( Application.isLoadingLevel)
return ;
// 通知新的State開始
if( m_State != null && m_bRunBegin==false)
{
m_State.StateBegin();
m_bRunBegin = true;
}
if( m_State != null)
m_State.StateUpdate();
}
}
```csharp public abstract class SceneState { // 状态名称 private string m_StateName = “SceneState”; public string StateName {
get{ return m_StateName; }
set{ m_StateName = value; }
} // 状态拥有者 protected SceneStateController m_Controller = null; // 构造 public SceneState(SceneStateController Controller) {
m_Controller = Controller;
} // 开始 public virtual void StateBegin() {} // 結束 public virtual void StateEnd() {} // 更新 public virtual void StateUpdate() {} public override string ToString () {
return string.Format ("[I_SceneState: StateName={0}]", StateName);
}
}
我们在相应的时机更改对应的状态:主菜单中我们添加按钮监听(开始战斗的按钮)-
```csharp
// 主菜单状态
public class MainMenuState : SceneState
{
public MainMenuState(SceneStateController Controller):base(Controller)
{
//先调用父类Controller为参的构造再把设置父类StateName的值
this.StateName = "MainMenuState";
}
// 开始
public override void StateBegin()
{
// 取得开始按钮
Button tmpBtn = UITool.GetUIComponent<Button>("StartGameBtn");
if(tmpBtn!=null)
tmpBtn.onClick.AddListener( ()=> OnStartGameBtnClick(tmpBtn) );
}
// 开始战斗
private void OnStartGameBtnClick(Button theButton)
{
//Debug.Log ("OnStartBtnClick:"+theButton.gameObject.name);
m_Controller.SetState(new BattleState(m_Controller), "BattleScene" );
}
}