游戏开发中的设计模式一(状态模式)

17 篇文章 0 订阅

在游戏开发中,我们常常会用到一些物体的状态切换,比如场景和动画剧情等等,对于常规的不需要变通的我们可以使用switch case语句实现,但是一旦使用你的逻辑就是固定的,不能添加和修改,所以对于不确定会不会增加需求的,采用状态模式会更好

状态模式的核心思想就是通过GameManager来管理状态接口,通过状态接口来对每一个实现接口的状态类进行切换,怎么进行切换不需要我们的GameManager关心,切换条件将会在每个类自己的方法里面实现,这样再有新的状态出现,我们只需要改写或者添加这些状态类就可以了

下面看一下简单的状态模式的实现

先定义对我们的状态接口,这个接口需要包含这些状态所要切换的时候做的事

public interface IState
{
    void Handle(int arg);
}

然后在我们的管理类里面对接口进行使用,主要是用来传递状态,不做什么逻辑操作

public class Context
{
    private IState mState;
    public void SetState(IState state)
    {
        mState = state;
    }

    public void Handle(int arg)
    {
        mState.Handle(arg);
    }
} 

下面定义两个实例类来使用这个接口

public class ConcreteStateA : IState
{
    private Context mContext;
    public ConcreteStateA(Context context)
    {
        mContext = context;
    }

    public void Handle(int arg)
    {
        Debug.Log("ConcreateStateA.Handle " + arg);
        if (arg > 10)
        {
            mContext.SetState(new ConcreteStateB(mContext));
        }
    }
}

public class ConcreteStateB : IState
{
    private Context mContext;
    public ConcreteStateB(Context context)
    {
        mContext = context;
    }

    public void Handle(int arg)
    {
        Debug.Log("ConcreateStateB.Handle " + arg);
        if (arg <= 10)
        {
            mContext.SetState(new ConcreteStateA(mContext));
        }
    }
}

这样我们一个简单的状态模式就出来了,我们只需要在初始化给定当前状态即可,后续状态会通过每个类的实现方式自己进行切换,如果有需要添加的就直接在后面用同样的方式添加状态类即可

    void Start()
    {
        //通过管理类调用我们的状态
        Context context = new Context();

        context.SetState(new ConcreteStateA(context));

        context.Handle(5);
        context.Handle(20);
        context.Handle(30);
        context.Handle(4);
        context.Handle(6);
    }

下面我们用这个状态模式进行游戏场景切换的一些工作

当然第一步依旧是定义我们的管理器类和状态接口

//接口
public class ISceneState
{
    private string mSceneName;
    protected SceneStateController mController;

    public ISceneState(string sceneName,SceneStateController controller)
    {
        mSceneName = sceneName;
        mController = controller;
    }

    public string SceneName
    {
        get { return mSceneName; }
    }
    //每次进入到这个状态的时候调用,初始化使用
    public virtual void StateStart() { }
    //需要结束状态的时候调用,主要是用来释放
    public virtual void StateEnd() { }
    //当前的状态行为逻辑
    public virtual void StateUpdate(){}
}

//管理器类
public class SceneStateController
{
    private ISceneState mState;
    private AsyncOperation mAO;//异步加载场景
    private bool mIsRunStart = false;//是否启动当前状态,主要是判断场景是否加载完毕

    //设置状态的方法,参数一是传入当前状态类,第二个是是否立即启动当前状态类,默认是启动的,但是因为我们这个是
    //加载场景使用的,所以第一个场景默认是加载好的,在这里我们的初始场景只是展示一个logo不需要做其他操作
    public void SetState(ISceneState state,bool isLoadScene=true)
    {
        if (mState != null)
        {
            mState.StateEnd();//让上一个场景状态做一下清理工作
        }
        mState = state;
        if (isLoadScene)
        {
            mAO = SceneManager.LoadSceneAsync(mState.SceneName);
            mIsRunStart = false;
        } else
        {
            mState.StateStart();//启动当前状态的初始化操作
            mIsRunStart = true;
        }

    }

    public void StateUpdate()
    {
        if (mAO != null && mAO.isDone == false) return;//当前场景正在加载但是没有完成,直接返回

        if (mIsRunStart==false&& mAO != null && mAO.isDone == true)//同样判断当前场景是否加载完,但是需要跳过初始场景
        {
            mState.StateStart();
            mIsRunStart = true;
        }

        if (mState != null)
        {
            mState.StateUpdate();
        }
    }
}

下面我们开始实现我们的各个场景的类以及这些场景的逻辑

//第一个场景,显示logo从暗变亮,2秒之后切换到下一个场景
public class StartState:ISceneState
{
    public StartState(SceneStateController controller):base("01StartScene",controller)
    {
    }

    private Image mLogo;
    private float mSmoothingSpeed = 1;
    private float mWaitTime = 2;
    public override void StateStart()
    {
        mLogo = GameObject.Find("Logo").GetComponent<Image>();
        mLogo.color = Color.black;
    }

    public override void StateUpdate()
    {
        mLogo.color = Color.Lerp(mLogo.color, Color.white, mSmoothingSpeed * Time.deltaTime);
        mWaitTime -= Time.deltaTime;
        if (mWaitTime <= 0)
        {
            mController.SetState(new MainMenuState(mController));
        }
    }
}

//第二个场景,初始化StateStart()方法里面绑定按钮事件,通过按钮事件切换到下一个场景,按钮事件里面可以写异步加载以及进度条等等
public class MainMenuState:ISceneState
{
    public MainMenuState(SceneStateController controller) : base("02MainMenuScene", controller) { }

    public override void StateStart()
    {
        GameObject.Find("StartButton").GetComponent<Button>().onClick.AddListener(OnStartButtonClick);
    }
    private void OnStartButtonClick()
    {
        mController.SetState(new BattleState(mController));
    }
}
public class BattleState:ISceneState
{
    public BattleState(SceneStateController controller):base("03BattleScene",controller) { }

    public override void StateStart()
    {
        print("....");
    }
}

这样的化我们只需要调用SceneStateController来管理这些状态即可,新的状态也只是需要重写这个ISceneState接口,然后调用SetState方法切换场景即可.

public class GameLoop : MonoBehaviour {

    private SceneStateController controller = null;

    void Awake()
    {
        DontDestroyOnLoad(this.gameObject);
    }

    // Use this for initialization
    void Start () {

        controller = new SceneStateController();

        controller.SetState(new StartState(controller),false);//初始化调用第一个场景
    }

    // Update is called once per frame
    void Update () {
        if(controller!=null)
            controller.StateUpdate();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值