1、引言
在我们平常的游戏开发中,我们常常遇到游戏物体状态切换的问题,比如:人物的运动状态,有待机,行走、奔跑、攻击和死亡状态等。今天我们将用状态模式来演示游戏物体状态切换办法。下面我们一起来了解下吧。
2、状态模式详细介绍
2.1、定义
- 状态模式(State Pattern)
允许一个对象在其内部状态改变时自动改变其行为,对象看起来就像是改变了它的类。
2.2、解决的问题
主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的逻辑简化。
2.3、状态模式的结构图
下面时该模式的UML类图:
从上图可知,状态者模式涉及以下三个角色:
- 抽象状态类(State类):抽象状态类定义了一个具体状态类需要实现的行为约定。
- 具体状态类(ConcreteState类):具体状态类,实现抽象状态类的每个行为。
- 具体类(Context类) :维护一个State类的一个实例,该实例标识着当前对象的状态。
2.4、类图实现
详细代码如下:
抽象状态类:
/// <summary>
/// 抽象状态类
/// </summary>
abstract class State
{
/// <summary>
/// 定义一个与Context相关的行为
/// </summary>
/// <param name="context"></param>
public abstract void Handle(Context context);
}
Context类:
class Context
{
private State state;
public State State
{
get { return state; }
set
{
state = value;
Console.WriteLine("当前状态:" + state.GetType().Name);
}
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="state">默认Context的初始状态</param>
public Context(State state)
{
this.state = state;
}
/// <summary>
/// 对亲求做处理,并设置下一状态
/// </summary>
public void Request()
{
state.Handle(this);
}
}
具体状态类A:
/// <summary>
/// 具体状态
/// </summary>
class ConcreteStateA : State
{
/// <summary>
/// 设置ConcreteStateA的下一状态是ConcreteStateB
/// </summary>
/// <param name="context"></param>
public override void Handle(Context context)
{
context.State = new ConcreteStateB();
}
}
具体状态类B:
/// <summary>
/// 具体状态
/// </summary>
class ConcreteStateB : State
{
/// <summary>
/// 设置ConcreteStateB的下一状态是ConcreteStateA
/// </summary>
/// <param name="context"></param>
public override void Handle(Context context)
{
context.State = new ConcreteStateA();
}
}
测试一下:
//初始状态设为A
Context cn = new Context(new ConcreteStateA());
//不断请求改变状态
cn.Request();
cn.Request();
cn.Request();
cn.Request();
测试结果:
当前状态:ConcreteStateB
当前状态:ConcreteStateA
当前状态:ConcreteStateB
当前状态:ConcreteStateA
通过以上代码,状态模式的基本思路已经有了,下面我们来举例应用一下.
2.5、C#应用举例
- 情景:通过开关控制灯的开关
- 代码如下:
灯类
/// <summary>
/// 电灯类,对应模式中的Context类
/// </summary>
public class Light
{
private LightState state;
public LightState State
{
get { return state; }
set { state = value; }
}
public Light(LightState state)
{
this.state = state;
}
/// <summary>
/// 按下电灯开关
/// </summary>
public void PressSwich()
{
state.PressSwich(this);
}
}
灯的状态类:
/// <summary>
/// 抽象的电灯状态类,相当于State类
/// </summary>
public abstract class LightState
{
public abstract void PressSwich(Light light);
}
开启状态:
/// <summary>
/// 具体状态类, 开
/// </summary>
public class On : LightState
{
/// <summary>
/// 在开状态下,按下开关则切换到关的状态。
/// </summary>
/// <param name="light"></param>
public override void PressSwich(Light light)
{
Console.WriteLine("Turn off the light.");
light.State = new Off();
}
}
关闭状态:
/// <summary>
/// 具体状态类,关
/// </summary>
public class Off : LightState
{
/// <summary>
/// 在关状态下,按下开关则打开电灯。
/// </summary>
/// <param name="light"></param>
public override void PressSwich(Light light)
{
Console.WriteLine("Turn on the light.");
light.State = new On();
}
}
测试一下:
// 初始化电灯,原始状态为关
Light light = new Light(new Off());
// 第一次按下开关,打开电灯
light.PressSwich();
// 第二次按下开关,关闭电灯
light.PressSwich();
测试结果如下:
Turn on the light.
Turn off the light.
通过以上例子的了解我想对状态模式的原理,已经了解了,下面我们来看看它的优缺点。
3、状态模式优缺点
- 优点:
- 将状态判断逻辑放在每个状态类里面,可以简化判断的逻辑。
- 当有新的状态出现时,可以通过添加新的状态类来进行扩展,扩展性好。
- 缺点:
- 如果状态过多的话,会导致有非常多的状态类,加大了开销。
4、状态模式适用场景
在以下情况下可以考虑使用状态者模式。
当一个对象状态转换的条件表达式过于复杂时可以使用状态者模式。
把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简单化。
当一个对象行为取决于它的状态,并且它需要在运行时刻根据状态改变它的行为时,就可以考虑使用状态者模式。
5、应用举例(unity)
- 情景 :控制物体的上下移动,到达屏幕边缘时,往相反的方向运动
为了方便我们把所有的类写在了一个脚本里。当然,这是我们不推荐的做法。把这个脚本挂载在摄像机上。准备UI如图所示:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Test : MonoBehaviour {
private GameObject obj;
private Controller ctrl;
private GameObject text;
void Awake()
{
text = GameObject.Find("Text");
text.GetComponent<Text>().color = Color.blue;
text.GetComponent<Text>().horizontalOverflow = HorizontalWrapMode.Overflow;
}
void Start ()
{
obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
obj.GetComponent<MeshRenderer>().material.color = Color.red;
//obj.name = "Cube";
obj.transform.position = Vector3.zero;
ctrl = new Controller(obj, new MoveToUp());
}
void Update()
{
ctrl.Handle();
// Debug.Log(obj.transform.position);
text.GetComponent<Text>().text = ctrl.MoveState.ToString() + " " + obj.transform.position.ToString();
}
}
/// <summary>
/// 运动控制器
/// </summary>
public class Controller
{
private MoveState moveState;
public MoveState MoveState
{
get { return moveState; }
set { moveState = value; }
}
private GameObject obj;
public GameObject Obj
{
get { return obj; }
set { obj = value; }
}
public Controller(GameObject obj,MoveState moveState)
{
this.obj = obj;
this.moveState = moveState;
}
public void Handle()
{
moveState.Handle(this);
}
}
/// <summary>
/// 物体的运动状态
/// </summary>
public abstract class MoveState
{
public abstract void Handle(Controller ctrl);
}
/// <summary>
/// 向上的运动状态
/// </summary>
public class MoveToUp : MoveState
{
public override void Handle(Controller ctrl)
{
GameObject obj = ctrl.Obj;
if (Camera.main.WorldToViewportPoint(obj.transform.position).y < 1)
{
obj.transform.Translate(Vector3.up * Time.deltaTime * 15);
}
else
{
ctrl.MoveState = new MoveToDown();
}
}
}
/// <summary>
/// 向下的运动状态
/// </summary>
public class MoveToDown : MoveState
{
public override void Handle(Controller ctrl)
{
GameObject obj = ctrl.Obj;
if (Camera.main.WorldToViewportPoint(obj.transform.position).y > 0)
{
obj.transform.Translate(Vector3.down * Time.deltaTime * 15);
}
else
{
ctrl.MoveState = new MoveToUp();
}
}
}
测试结果如下:
6、总结
状态者模式是对对象状态的抽象,从而把对象中对状态复杂的判断逻辑移到各个状态类里面,从而简化逻辑判断。
7、unity工程下载
在文章的最后我们给出上述例子的工程下载链接,方便朋友们探讨交流!本次演示版本Unity5.6.3f1(64-bit),VS2015需要下载的朋友,请点击这里下载。
The End
好了,今天的分享就到这里,如有不足之处,还望大家及时指正,随时欢迎探讨交流!!!
喜欢的朋友们,请帮顶、点赞、评论!您的肯定是我写作的不竭动力!
相关阅读
C# 23种设计模式(unity演示)