返回

1.状态模式

让一个对象的行为随着内部状态的改变而变化,而该对象也像是换了类一样。

场景切换

需求:

现在有三个游戏场景,Start、Main、Battle,要求写一个切换场景的功能。

错误示范:

using System;
using UnityEngine;

public class SceneManager : MonoBehaviour
{
    public enum SceneEnum
    {
        Start,
        Main,
        Battle
    }

    private SceneEnum m_Scene = SceneEnum.Start;

    public void ChangeScene(SceneEnum scene)
    {
        m_Scene = scene;
        switch (scene)
        {
            case SceneEnum.Start:
                Application.LoadLevel("StartScene");
                break;
            case SceneEnum.Main:
                Application.LoadLevel("MainMenuScene");
                break;
            case SceneEnum.Battle:
                Application.LoadLevel("GameScene");
                break;
            default:
                return;
        }
    }

    public void Update()
    {
        switch (m_Scene)
        {
            case SceneEnum.Start:
                break;
            case SceneEnum.Main:
                break;
            case SceneEnum.Battle:
                break;
            default:
                return;
        }
    }
}

存在的问题:

  • 如果现在新增一个场景,那么就会涉及到修改原程序代码的问题(需要增加枚举,修改switch中的逻辑代码)。

  • 如果需要在场景加载结束时增加逻辑代码,以上代码无法满足。

状态模式

让一个对象的行为随着内部状态的改变而变化,该对象也想是换了类一样。

状态模式的说明

  • Context(状态拥有者)
    • 是要给具有“状态”属性的类,可以制定相关的接口,让外界能够得知状态的改变或通过操作让状态改变。
    • 有状态属性的类,例如:游戏角色有潜行、攻击、施法等;好友上线、脱机、忙碌等状态;这些类中会有一个ConcreteState 子类的对象为其成员,用来代表当前的状态。
  • State(状态接口类):制定状态的接口,负责规范Contex(状态拥有者)在特定状态下要表现的行为。
  • ConcreteState(具体状态类)
    • 继承自State(状态接口类)。
    • 实现Contex(状态拥有者)在特定状态下该有的行为,例如,实现角色在潜行状态时该有的行动变缓、3D模型半透明、不能被敌方角色觉察等行为。

具体实现:

using UnityEngine;
using System.Collections;

namespace DesignPattern_State
{
    // 持有目前的狀態,並將有關的訊息傳給狀態
    public class Context
    {
        State m_State = null;

        public void Request(int Value)
        {
            m_State.Handle(Value);
        }

        public void SetState(State theState)
        {
            Debug.Log("Context.SetState:" + theState);
            m_State = theState;
        }
    }

    // 負責封裝當Context處於特定狀態時所該展現的行為
    public abstract class State
    {
        protected Context m_Context = null;

        public State(Context theContext)
        {
            m_Context = theContext;
        }

        public abstract void Handle(int Value);
    }

    // 狀態A
    public class ConcreteStateA : State
    {
        public ConcreteStateA(Context theContext) : base(theContext)
        {
        }

        public override void Handle(int Value)
        {
            Debug.Log("ConcreteStateA.Handle");
            if (Value > 10)
                m_Context.SetState(new ConcreteStateB(m_Context));
        }
    }

    // 狀態B
    public class ConcreteStateB : State
    {
        public ConcreteStateB(Context theContext) : base(theContext)
        {
        }

        public override void Handle(int Value)
        {
            Debug.Log("ConcreteStateB.Handle");
            if (Value > 20)
                m_Context.SetState(new ConcreteStateC(m_Context));
        }
    }

    // 狀態C
    public class ConcreteStateC : State
    {
        public ConcreteStateC(Context theContext) : base(theContext)
        {
        }

        public override void Handle(int Value)
        {
            Debug.Log("ConcreteStateC.Handle");
            if (Value > 30)
                m_Context.SetState(new ConcreteStateA(m_Context));
        }
    }
}
using UnityEngine;
using System.Collections;
using DesignPattern_State;

public class StateTest : MonoBehaviour
{
    // Use this for initialization
    void Start()
    {
        UnitTest();
    }

    // 
    void UnitTest()
    {
        Context theContext = new Context();
        theContext.SetState(new ConcreteStateA(theContext));
        theContext.Request(5);
        theContext.Request(15);
        theContext.Request(25);
        theContext.Request(35);
    }
}

再谈场景切换

现在我们可以把场景抽象为一个状态对象(ISceneState)(可以是基类、也可以是接口),抽象场景在不同时间段可能需要实现的逻辑接口。然后我们根据不同场景实现不同的实体子类(StartState),在子类中重写基类的逻辑方法。

在外部,我们还需要一个管理类(SceneStateController)来对场景进行统一切换。

现在在反观开始提到的两个问题,Scene扩展和场景加载结束时逻辑,都迎刃而解。

状态模式的优点

  • 减少错误发生并降低维护难度

    不在使用Swith(m_state)来判断当前的状态,这样可以减少新增游戏状态时,因未能检查到所有switch(m_state)程序代码而造成的错误

  • 状态执行环境单一化

    与每一个状态有关的对象及操作都被实现在一个场景状态类下,对程序设计来说,这样可以清楚的了解每一个状态执行时所需要的对象及配合的类。

状态模式的应用

  • 角色AI:使用状态模式来控制角色在不同状态下的AI行为。
  • 游戏服务器连线状态:网络游戏的客户端,需要处理与游戏服务器的连线状态,一般包含开始连线、连线中、断线等状态,而在不同的状态下,会有不同的封包信息处理方式,需要分别实现。
  • 关卡进行状态:如果是通关型游戏,进入关卡时通常会分成不同的阶段,包含加载数据,显示关卡信息、倒数通知开始、关卡进行、关卡结束和分数计算,这些不同的阶段可以使用不同的状态来负责实现。