zoukankan      html  css  js  c++  java
  • 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
    可以判断此场景是否加载完毕。

  • 相关阅读:
    实验报告五
    实验报告四
    树莓派系统安装和调试
    Linux内核分析——扒开系统调用的三层皮(下)
    Linux内核分析——第二章 从内核出发
    Linux内核分析——第一章 Linux内核简介
    Linux内核分析——扒开系统调用的三层皮(上)
    Linux内核分析——构造一个简单的Linux系统MenuOS
    Linux内核分析——操作系统是如何工作的
    Linux内核分析——计算机是如何工作的
  • 原文地址:https://www.cnblogs.com/Fflyqaq/p/11576516.html
Copyright © 2011-2022 走看看