此类型的博客的写作初衷在于,整理自己平时所学的知识,随时欢迎各路大神指正
//绿色字的内容为注释
- 什么时候使用状态模式/状态模式有什么用?
官方的说法是,当对象有很明确的状态划分的时候;
1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
2.一个操作中含有庞大的多分支结构,并且这些分支决定于对象的状态。(360百科)
如:地下城与勇士中,战斗法师的觉醒技能:变身贝亚娜斗神;如下情况通常会使用状态模式
当玩家变身为贝亚娜状态之前,战斗法师的前冲攻击(→→+X)是这样的——
![](https://i-blog.csdnimg.cn/blog_migrate/27715fb0bf3cf056aa97607bc9d7d5ea.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/fa7801ed125221dc96a01517c7bf17d3.jpeg)
总结来讲就是://这种方法只是此模式的一种用法
当你希望用户(玩家)
使用相同的操作方式/*都是释放前冲攻击*/(调用相同的函数名,但实际会因当前的状态不同,使用的是不同的函数,或者说函数调用的方式不变),
游戏或者程序会产生不同的操作响应时就可以使用状态模式;
状态模式的好处
符合单一指责原则、开闭原则、迪米特法则(最少知道法则)
将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
- 状态模式的核心写法/如何写一个状态模式?(小部分内容参考自《设计模式与游戏完美开发》)
想写一个状态模式的基本框架很简单,我们只需要写三部分(三种类型的类)的内容
1、Context(状态的拥有者:战斗法师)
2、IState(抽象状态类/抽象状态模板,定义了前冲攻击这个技能,但并不会写出具体的实现方法/*通常是虚方法*/,通常会使用abstract抽象类去写,也可以使用接口Instance,或者普通类class)
3、继承IState的具体状态类(通常有多个 这个类override重写了前冲攻击这个技能的技能效果)
(1)State战斗法师(战斗法师使用手中的武器敲击敌方的头部;)
(2)State贝亚娜斗神(战斗法师使用手中矛形的武器突刺对方;)
这三部分拆分开来讲:
Context状态的拥有者只需要写3条内容:
1、存放IState类的字段//用来存放当前状态,和下面类似
2、SetState(IState)方法//用来设置当前状态,和下面类似/给上面的一条赋值
3、Requast()//可以同时写多个这种类型的方法,方法内部,放入对应的状态框架中的方法,像什么Update()方法,Enable()方法等专门给外界调用的方法,是子状态中的方法与外界交流的唯一出口
public class Context
{
/// <summary>
/// 用来保存当前状态
/// </summary>
protected State state;
/// <summary>
/// 给外界调用的设置状态方法
/// </summary>
/// <param name="state"></param>
public void SetState(State state)
{
this.state = state;
}
/// <summary>
/// 使用状态模式的方法,通过这个可以间接的转换状态
/// </summary>
/// <param name="var"></param>
public void Request(int var)
{
state.Headle(var);
}
}
IState状态的框架也同样有3样东西:
1、存放Context类的字段//用来方便子状态使用,和上面类似:如 切换状态
2、自身的构造函数//用来给Context赋值,和上面类似/给上面一条赋值
3、留给子状态继承并重写的所有抽象方法//和上面一样,也不限于一个方法
public abstract class State
{
/// <summary>
/// 状态中保存有当前状态的拥有者
/// </summary>
protected Context context;
/// <summary>
/// 想创建一个State 必须new一个此状态的拥有者,没有拥有者哪来的状态
/// </summary>
/// <param name="context"></param>
public State(Context context)
{
this.context = context;
}
/// <summary>
/// 给其他扩展状态设置的此状态执行的操作
/// </summary>
/// <param name="var"></param>
public abstract void Headle(int var);
}
子状态只有2个主要任务:
1、重写父类方法,实现功能
2、切换状态
//使用默认构造函数
public class ConcreteStateA : State
{
public ConcreteStateA(Context context) : base(context)
{
}
public override void Headle(int var)
{
if (var > 10)
context.SetState(new ConcreteStateB(context));
Debug.Log("ConcreteStateA");
}
}
public class ConcreteStateB : State
{
public ConcreteStateB(Context context) : base(context)
{
}
public override void Headle(int var)
{
if (var > 40)
{
context.SetState(new ConcreteStateA(context));
}
Debug.Log("ConcreteStateB");
}
}
- 写好了状态模式后,又该如何去使用状态模式呢?
使用状态模式分3步
1、new一个状态的拥有者出来//连角色都没有,怎么可能让角色有状态
2、给刚刚new出来的拥有者SetState()设置一个初始状态//创建好了新角色以后,此角色总得有个状态吧,让她站着还是坐着随你便
3、使用拥有者中的公有方法来间接调用子状态中的方法
public class StateText : MonoBehaviour {
void Start()
{
UnitTest();
}
void UnitTest()
{
Context theContext = new Context();//new一个状态的拥有者
theContext.SetState(new ConcreteStateA(theContext));//设置一个初始状态,这里传入new出的拥有者的目地是,给IState中的Context赋值
theContext.Request(5);//使用拥有者中的公有方法来间接调用子状态中的方法
theContext.Request(15);
theContext.Request(25);
theContext.Request(35);
theContext.Request(55);
}
}