命令模式:
将一个请求封装成一个对象,从而允许你使用不同的请求、队列或日志将客户端参数化。同时支持请求操作的撤销与恢复。
命令就是面向对象化的回调。
最常见的便是用在输入控制上。
我们可以将输入的信号转换成对应的指令,存入命令队列中,依次执行。
下面是一个小例子:
在Unity中,主要用到了这几个脚本。
代码部分:
Unit:所有单位的基类,封装了通用的高级方法。后续如果新建不同的单位类型从这个类继承就可以了。简便起见,只预设了两个方法。
public class Unit:MonoBehaviour {
//速度
public float Speed=5;
//移动
public void Move(Vector2 dir)
{
transform.Translate(dir * Speed * Time.deltaTime);
}
//射击
public void Shot()
{
Debug.Log("Shoot!!!");
}
}
Player:继承自Unit,这里没有定义任何额外的操作。引入的目的只是为了表明,我们可以通过执行命令进行任何单位的基本操作。
public class Player : Unit
{
}
Command:命令接口,里面定义了执行和撤销方法。
public interface Command {
//执行
void Excute();
//撤销
void Undo();
}
CmdMove:移动命令,实现Command接口,构造时传递命令所需参数。
public class CmdMove : Command {
//单位
Unit unit;
//方向
Vector2 dir;
public CmdMove(Unit unit,Vector2 dir)
{
this.unit = unit;
this.dir = dir;
}
public void Excute()
{
unit.Move(dir);
}
public void Undo()
{
unit.Move(-dir);
}
}
CmdShoot:射击命令,实现Command接口,构造时传递所需参数。
public class CmdShoot : Command
{
//单位
Unit unit;
public CmdShoot(Unit unit)
{
this.unit = unit;
}
public void Excute()
{
unit.Shot();
}
public void Undo()
{
Debug.Log("undo shoot!!!");
}
}
注:就射击来讲,应该没有撤销操作可言,这里加入只是为了体现undo的调用。
InputManager:负责将输入以命令队列的形式执行。同时支持撤销操作。
public class InputManager : MonoBehaviour {
//操作的对象
public Player underControl;
//待执行命令
Queue<Command> nextCmd = new Queue<Command>();
//已执行命令
Stack<Command> preCmd = new Stack<Command>();
private void Update()
{
GetCommand();
//若命令队列存在命令,则取出执行
if (nextCmd.Count > 0)
{
Command cmd = nextCmd.Dequeue();
cmd.Excute();
//放入已执行栈中
preCmd.Push(cmd);
}
//撤销操作
if (Input.GetKey(KeyCode.Z))
{
//防溢出
if (preCmd.Count == 0)
{
Debug.Log("cmd is null");
return;
}
preCmd.Pop().Undo();
}
}
//获取命令并存入队列
void GetCommand()
{
float x = Input.GetAxis("Horizontal");
float y = Input.GetAxis("Vertical");
if (x != 0 || y != 0)
{
Command cmd = new CmdMove(underControl, new Vector2(x, y));
nextCmd.Enqueue(cmd);
}
if (Input.GetKeyDown(KeyCode.J))
{
Command cmd= new CmdShoot(underControl);
nextCmd.Enqueue(cmd);
}
}
}
将InputManager挂载到场景任意物体。将Player挂载到玩家对象上,并赋给InputManager。运行即可。
这只是一个很简单的案例,但是已经可以充分发现命令模式带给我们的惊喜。
它使代码更加优雅、简洁。
这只是一个类似同步执行命令的例子。
想实现更加复杂的功能则需要在这基础之上拓展。
我一直是war3、刀塔的爱好者,这两个游戏中的shift操作一直是我认为比许多其他同类型游戏优异的地方。
而这种操作的实现,应该就是命令模式在异步状态下的表现。