Unity---游戏设计模式(1)状态模式




概述请看 参考博客

状态模式在游戏中可以用于切换人物动作、切换场景等。
本文介绍使用状态模式切换游戏场景。

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、状态模式优缺点

优点

  1. 降低状态类间的耦合性
  2. 代码结构化,易维护、拓展

缺点

  1. 会创建大量的类
  2. 代码结构复杂

4、新知识

3.1 DontDestroyOnLoad()

DontDestroyOnLoad(gameObject):让某个对象在切换场景时不释放。
假如想让某个音乐在每个场景都播放。使用这个,在切换场景时就不会被销毁。

3.1 AsyncOperation

AsyncOperation mAsyncOperation;
mAsyncOperation = SceneManager.LoadSceneAsync();
此方法用于异步加载场景。

mAsyncOperation.isDone
可以判断此场景是否加载完毕。

posted @ 2019-10-12 16:45  Fflyqaq  阅读(1319)  评论(0编辑  收藏  举报