目录
三、SceneStateController-场景状态控制者
一、定义
在软件开发过程中,应用程序中的部分对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态就会发生改变,从而使其行为也发生改变。
简单一句话就是:
当一个对象内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
二、使用场景
状态模式适用场景
- 用户登录、注册状态
- 场景状态
- 角色状态
- 人物心情系统
- AI状态
等不同的场景。
你发现你的代码里面存在一个又长又臭的if else
,又或者switch ,而这些分支都是因为不同状态下执行的操作不一样时考虑使用此模式。
三、快速理解状态模式
例:RiLee前段时间谈了一个女朋友,但是呢,这个女朋友的心情老是漂浮不定,这就让他有些苦恼,当女朋友开心的时候呢,对他是唯命是从,说什么是什么,给他买电脑买键盘;当女朋友难过的时候呢,对他是哭哭啼啼,靠着哭;当女朋友生气的时候呢,是最烦恼的时候,对他是爱答不理,甚至还想至他于九泉。
这种女友心情变化,就很适合用状态模式来开发,因为不同的心情,对RiLee有着不同的态度。
1、状态类父类——女朋友心情状态接口
因为所有的心情变化以及不同心情做的不同事,都是由女朋友身上发出来的,所以将女朋友作为参数(Girlfriend => 状态拥有者)。
//女朋友心情状态
public interface GirlfriendStatus
{
public void DoAction(Girlfriend g);
}
2、状态类——女朋友不同心情状态
(1)开心状态
public class HappyState : GirlfriendStatus
{
public void DoAction(Girlfriend g)
{
Console.WriteLine("女朋友很开心,想给你买兰博基尼!!!");
}
}
(2)生气状态
public class AngryState : GirlfriendStatus
{
public void DoAction(Girlfriend g)
{
Console.WriteLine("女朋友很生气,想揍你!!!");
}
}
(3)伤心状态
public class SadState : GirlfriendStatus
{
public void DoAction(Girlfriend g)
{
Console.WriteLine("女朋友很伤心,想抱抱你!!!");
}
}
3、控制类——女朋友(状态拥有者)
为什么女朋友是控制类呢,RiLee所感受到的所有变化都是由女朋友带来的,女朋友可以有不同的心情,可以展现出来给你看也可以不展现出来给你看。相当于对应的管理器,类需要的可以从管理器中取。(当然你也可能会换女朋友,同理的你也可能会换不同的女朋友,RiLee可以从不同的女朋友身上获取不同的感受,和上面操作一样)
public class Girlfriend
{
public GirlfriendStatus girlfriendStatus;
public void SetState(GirlfriendStatus gs)
{
this.girlfriendStatus = gs;
}
public GirlfriendStatus GetState()
{
return girlfriendStatus;
}
public void DoAction()
{
girlfriendStatus.DoAction(this);
}
}
4、测试——RiLee
RiLee是实际感受到女朋友做对应的事的人
public class RiLee
{
public static void Main(string[] args)
{
//状态的保持与切换者
Girlfriend g = new Girlfriend();
//开心状态
HappyState hs = new HappyState();
g.SetState(hs);
g.DoAction();
//生气状态
AngryState AS = new AngryState();
g.SetState(AS);
g.DoAction();
//伤心状态
SadState ss = new SadState();
g.SetState(ss);
g.DoAction();
}
}
控制台输出:
女朋友很开心,想给你买兰博基尼!!!
女朋友很生气,想揍你!!!
女朋友很伤心,想抱抱你!!!
四、实例
运用在Unity也是同样的道理, 以场景状态为例:
一、场景类接口——ISceneState
public class ISceneState
{
private string sceneName;
public string SceneName { get => sceneName;}
protected SceneStateController controller;
public ISceneState(string sceneName,SceneStateController controller)
{
this.sceneName = sceneName;
this.controller = controller;
}
public virtual void OnStart() { }
public virtual void OnUpdate() { }
public virtual void OnDestroy() { }
}
二、场景类——两个场景类
(1)MainState (开始场景)
在构建函数的时候,直接赋值场景名字,和状态持有者(SceneStateController)
public class MainState : ISceneState
{
public MainState(SceneStateController controller) : base("MainScene", controller)
{
}
}
(2)BattleState(战斗场景)
public class BattleState: ISceneState
{
public BattleState(SceneStateController controller) : base("BattleScene", controller)
{
}
}
三、SceneStateController-场景状态控制者
通过场景的名字来切换不同的场景
public class SceneStateController
{
private AsyncOperation _async;
private ISceneState mScene;
public void SetState(ISceneState iscene, bool isLoadScene = true)
{
if (iscene != null && IsRunStart == false)
{
mScene.OnDestroy();//摧毁上一个场景
}
mScene = iscene;
if (isLoadScene)
{
if (mScene.SceneName == "MainScene")
{
_async = SceneManager.LoadSceneAsync(mScene.SceneName);
}
_async = SceneManager.LoadSceneAsync(mScene.SceneName);
IsRunStart = true;
}
else
{
mScene.OnStart();
IsRunStart = false;
}
}
public void StateUpdate()
{
//还在加载场景
if (_async != null && !_async.isDone)
{Return;};
if (_async.isDone && _async != null && IsRunStart == true)
{
mScene.OnStart();
IsRunStart = false;
}
if (mScene != null)
{
mScene.OnUpdate();
}
}
}
四、GameLoop——需要调用的脚本
public class GameLoop:MonoBehaviour
{
//场景状态
SceneStateController sceneStateController = new SceneStateController();
void Awake()
{
// 转换场景不会被删除
GameObject.DontDestroyOnLoad(gameObject);
}
private void Start()
{
//设置起始场景
sceneStateController Controller.SetState(new MainState(sceneController), false);//如果StartScene用Resources加载时就需要开启
}
private void Update()
{
sceneStateController.StateUpdate();
}
}
例如我们想加多一个场景TestScene,我们只需要添加一个TestState状态类实现ISceneState就可以实现添加场景了,同理增加场景的同时,直接添加状态类即可。
五、优点以及缺点
(1)优点
- 增强了程序的可扩展性,因为我们很容易添加一个State
- 增强了程序的封装性,每个状态的操作都被封装到了一个状态类中,减少依赖
(2)缺点
- 类变多,业务场景变得复杂