源代码来自:https://github.com/TYJia/GameDesignPattern_U3D_Version
命令模式基本思想:将命令封装,与目标行为解耦,使命令由流程概念变为对象数据(需要大量实例化命令对象)
缺点:可能会导致大量的实例化,从而浪费内存
扩展:可用享元模式代替大量的实例化
命令变成了数据,也就变成了可被传递、存储、重复利用的:
- 通过命令数据 队列/栈 可以轻易实现撤销、重做、时光倒流
- 命令数据还可以形成日志,用于复现用户行为,便于重复测试同样序列命令对各种目标的影响
- 这些命令数据可以发送给不同的目标,比如同样的“出发,5分钟后,停止”,发送给飞机就可以变成“起飞,5分钟后,降落”,发送给轮船就成了“离港,5分钟后,抛锚”
Command.cs
//抽象基类,包含了时间戳和运行、回退的虚方法
public abstract class Command {
protected float _TheTime;
public float TheTime
{
get { return _TheTime; }
}
public virtual void execute(Avatar avatar) { }
public virtual void undo(Avatar avatar) { }
}
CommandMove.cs
using UnityEngine;
//Command的子类,可以调用指定Avatar的Move函数
public class CommandMove : Command {
private Vector3 TransPos;
public CommandMove(Vector3 transPos, float time)
{
TransPos = transPos;
_TheTime = time;
}
public override void execute(Avatar avatar)
{
avatar.Move(TransPos);
}
public override void undo(Avatar avatar)
{
avatar.Move(-TransPos);
}
}
CommandManager.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/*
当IsRun为true时,由WASD按键生成命令对象,执行Execute(Avatar)
当IsRun为false时,调用RunCallBack(),按时间将栈内命令提取出并执行Undo(Avatar)
*/
public class CommandManager : MonoBehaviour
{
public Avatar TheAvatar;
private Stack<Command> mCommandStack;
private float mCallBackTime;
public bool IsRun = true;
// Use this for initialization
void Start()
{
mCommandStack = new Stack<Command>();
mCallBackTime = 0;
}
// Update is called once per frame
void Update()
{
if (IsRun)
{
Control();
}
else
{
RunCallBack();
}
}
private void RunCallBack()
{
mCallBackTime -= Time.deltaTime;
if (mCommandStack.Count > 0 && mCallBackTime < mCommandStack.Peek().TheTime) //Peek返回栈顶值,不删除
{
mCommandStack.Pop().undo(TheAvatar);
}
}
private Command InputHandler()
{
if (Input.GetKey(KeyCode.W))
{
return new CommandMove(new Vector3(0, Time.deltaTime, 0), mCallBackTime);
}
if (Input.GetKey(KeyCode.S))
{
return new CommandMove(new Vector3(0, -Time.deltaTime, 0), mCallBackTime);
}
if (Input.GetKey(KeyCode.A))
{
return new CommandMove(new Vector3(-Time.deltaTime, 0, 0), mCallBackTime);
}
if (Input.GetKey(KeyCode.D))
{
return new CommandMove(new Vector3(Time.deltaTime, 0, 0), mCallBackTime);
}
return null;
}
private void Control()
{
mCallBackTime += Time.deltaTime;
Command cmd = InputHandler();
if (cmd != null)
{
mCommandStack.Push(cmd);
cmd.execute(TheAvatar);
}
}
}
Avatar.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//执行行为的目标物体,拥有Move函数
public class Avatar : MonoBehaviour {
private Transform mTransform;
void Start () {
mTransform = transform;
}
public void Move(Vector3 DeltaV3)
{
mTransform.Translate(DeltaV3);
}
}