设计模式一:状态模式

一、前言

在有多个状态用来回切换的时候我们可以使用状态模式
以书为例,讲的是3种状态,开始游戏场景,主界面场景,游戏战斗场景
我们在开发游戏时对这三种状态要来回加载,因此适用于状态模式

使用状态模式可以避免使用Switch结构,也就避免了游戏中频繁添加内容就要修改switch代码的操作

因此遵守了开放封闭原则:
代码重构时尽量去添加代码,而不是修改代码的设计原则

二、类的介绍

  1. 首先是状态基类,这个状态基类就是游戏场景,主界面场景它们的父类,同时也是一个抽象类,需要定义3种(开始,结束,保持状态)抽象方法给子类重写,并且可以有一个状态控制者的类对象引用。做一些其他操作
  2. 有基类就肯定有子类了,子类就是具体的游戏场景,主界面场景,游戏战斗状态场景类了。他们需要重写父类的虚方法来做具体实现。
  3. 状态控制者类,因为我们一次之能使用一种状态。且需要来回切换。因此一定需要一个状态类来对他们进行切换。

三、学习疑问

上面只是做了个大致了解,但是还是不会写。解决以下的疑问试试看
类如何初始化他们呢?
实际该如何调用他们的方法呢?
如何进行状态之间的转换呢?
三种状态方法可以做什么样的事呢?

1、类如何初始化他们呢?

我们应该要知道,一个类有一个对象,但是一个类里又有包含着其他类的情况。(关联关系),他们到底该如何初始化?
首先看状态控制类: 状态控制类有一个状态类对象,但是这个状态类成员是被私有的,那该如何给这个类成员赋值状态类对象呢?

那么,应该理解这个状态类对象,这个状态类对象就是可以进行来回设置的,因此需要一个公有方法给状态类对象行赋值(状态类的子类对象)。而这个状态类有3种状态方法,此时就可以在状态控制类中做不同的操作了。

因此:可通过公有方法来获取这个状态类对象,来操作这个状态类成员,进而可以操作各种状态方法。

这里展示状态控制类代码

using UnityEngine;
using System.Collections;
/// <summary>
/// Scene state ctrl.
/// 机器类: 用来控制各个状态(SceneBase)
/// </summary>
public class SceneStateCtrl{

    //当前状态
    public SceneBase _currentState;
    //场景是否加载完
    private bool _isLoading = false;
    //状态切换

    public void SetState(SceneBase newState,string sceneName){

        _isLoading = false;

        if (_currentState != null) {
            //先让当前场景退出,再进行场景切换
            _currentState.OnExit ();
        }

        //场景切换
        loadScene (sceneName);
        _currentState = newState;
    }

    private void loadScene(string sceneName) {
        //因为场景切换涉及新场景的资源加载,所以可能比较慢
        //如果已经点击切换,就屏蔽掉再次点击

        if (!_isLoading) {
            _isLoading = true;
            if (sceneName != "") {
                Application.LoadLevel (sceneName);
            }
        }
    }

    public void UpdateState(){

        //只有当场景加载完成,才能update,并且此时状态是一直持续的
        if (Application.isLoadingLevel || _currentState == null)
            return;

        if (_isLoading) {
            _currentState.OnEnter ();
            _isLoading = false;//因此在切换状态的时候要更新设置为 false
        }

        if (_currentState != null) {
            _currentState.OnUpdate ();
        }
    }
}

可以看出:

1. 状态场景类的状态方法都是公有的方法,被状态控制类中的状态类对象调用。
2. 而当前状态类对象在控制类的公有方法SetState中获取
3. 通过new创建状态控制类对象,修改状态类对象,状态基类对象通过状态控制者的SetState(new ())方法来创建,每次创建都new一个子类状态对象

之后看状态基类: 状态基类,状态基类有一个被保护的状态控制者类成员,这个控制者类对象是通过(外界调用)构造函数来给这个状态控制类赋值对象的。此后子类可以使用这个控制者类对象可以调用SetState方法,进行控制下一个状态场景。

2、实际该如何调用他们的方法呢?

类的公有方法的。他们之间的调用关系该是怎么样的呢?

类内部没有自己的对象,所有大部分公有方法都是外部类创建对象,在外部类调用对象的公有方法

3、场景之间的状态该如何转换?

通过点击按钮切换状态,如开始按钮,结束按钮,复杂点就是菜单栏上的开始结束按钮

4、3种状态方法(开始、结束、保持)可以做什么样的事?

因为他们都没有继承MonoBehaviour,没有Start Update等方法

因此在开始状态方法OnEnter(){}可以做状态初始化,获取组件,对象,声明事件检测(按钮点击)
在保持方法OnUpdate(){}可以做持续的if判断,持续性的内容
最后离开当前状态的OnExit()方法,该方法必须被调用,因此可以做当前状态场景切换后,新场景切换之前的事

列出状态场景基类,以及状态场景子类代码:

using UnityEngine;
using System.Collections;
/// <summary>
/// Scene base.
/// </summary>
public class SceneBase{

    protected SceneStateCtrl stateCtrl = null;

    public const string Name = "";

    public SceneBase(SceneStateCtrl stateCtrler){
        this.stateCtrl = stateCtrler;
    }

    public virtual void OnEnter(){}
    public virtual void OnUpdate(){}
    public virtual void OnExit(){}

}

开始场景类

using UnityEngine;
using System.Collections;
/// <summary>
/// Start scene state.
/// 场景中的工作
/// 例子:找到场景中的按钮,给按钮添加事件,触发事件,改变状态
/// </summary>
public class StartSceneState : SceneBase{

    public const string Name = "StartScene";

    public StartSceneState(SceneStateCtrl stateCtrler):base(stateCtrler){
    }

    private UIButton _firstBtn;

    public override void OnEnter ()
    {
        base.OnEnter ();
        _firstBtn = UITool.FindUI<UIButton> ("StartBtn");
        UIEventListener.Get (_firstBtn.gameObject).onClick = FirstBtnClick;

    }
    public override void OnUpdate ()
    {
        base.OnUpdate ();

        if (Application.isLoadingLevel || stateCtrl == null) {
            return;
        }
    }

    public override void OnExit ()
    {
        base.OnExit ();
    }

    void FirstBtnClick(GameObject go){
        Debug.Log ("开始游戏");
        stateCtrl.SetState (new MainSceneState (stateCtrl),"MainScene");
    }
}

四、总结

最后做个抽象的总结
这里使用了一个5个不继承MonoBehaviour的类,分别表示1个状态基类,3个状态子类,1个状态控制者类。然后只使用一个继承MonoBehaviour的游戏循环类。

游戏循环类开始时new一个状态控制者类对象,状态控制类对象则可以开始控制各种子类,做具体的实现方法。
游戏循环类有 Start,Update方法,分别对应(调用)控制类对象的Start(setState初始化),update方法,然后又控制类对象对应(调用)状态子类的OnEnter,OnUpdate方法。

这里看下GameLoop游戏状态循环类

using UnityEngine;
using System.Collections;

public class GameLoop : MonoBehaviour {

    private SceneStateCtrl _currentStateCtrl = null;

    void Start () {
        //切换场景,游戏物体不销毁
        DontDestroyOnLoad (this.gameObject);
        _currentStateCtrl = new SceneStateCtrl ();

        //字符串如此做是不让场景做切换,因为运行程序就在start场景中,
        //不需要再次切换到Start场景
        _currentStateCtrl.SetState (new StartSceneState (_currentStateCtrl), "");

    }

    void Update () {
        _currentStateCtrl.UpdateState ();
    }
}

五、细节上

再说一个细节上的问题,因为每次切换状态都会调用SetState()方法new一个状态子类对象,但是如果重复切换则会new出很多个同样的子类状态对象。此时GC管理内存的功能就体现出来了,它会把智能的把不会再用到的子类状态对象进行释放,因此可以放心的通过方法来new对象。

当然我们还是要少new出对象,因为释放内存同样会降低程序效率

猜你喜欢

转载自blog.csdn.net/liaoshengg/article/details/81161902
今日推荐