本人游戏策划一枚,开始学习一下游戏编程,以便于将来失业了可以做做独立游戏。
命令模式
核心描述:
将一个请求(request)封装成一个对象,从而允许你使用不同的请求,队列或者日志将客户端参数化,同时支持请求操作的撤销与恢复。
知识点:
1.将“输入指令 - 角色执行”添加一个中间层,改为“输入指令 - 指令对象 - 角色执行”。
2.实现撤销与恢复,将“指令对象”存储在字典中,用整型变量currentKey来指向当前处于字典中的具体指令。用整型变量lastKey和nextKey来指向上一次和下一次的具体指令。
实现流程:
1.在Unity中创建一个GameObject,用于循环监听按键事件和初始化ExecuteCommand类(执行指令的类);
2.创建一个CommandMode类作为指令的父类,以便于在撤销和恢复的时候,让子类以父类的身份存储到指令序列的字典中。
3.创建一个ExecuteCommand类,用来执行具体的指令,并将所有的指令用来存放到指令序列的字典中。
下面贴上源代码
CommandMode类
public class CommandMode {
public CommandMode()
{ }
/// <summary>
/// 构造函数,子类可以继承
/// </summary>
/// <param name="gameObject">指令起作用的对象</param>
protected CommandMode(GameObject gameObject)
{ }
/// <summary>
/// 执行向上移动,具体在子类中实现
/// </summary>
public virtual void MoveUp()
{ }
/// <summary>
/// 执行向下移动,具体在子类中实现
/// </summary>
public virtual void MoveDown()
{ }
/// <summary>
/// 执行向左移动,具体在子类中实现
/// </summary>
public virtual void MoveLeft()
{ }
/// <summary>
/// 执行向右移动,具体在子类中实现
/// </summary>
public virtual void MoveRight()
{ }
/// <summary>
/// 撤销指令,不同的指令类型撤销方式不同,所以需要在具体的指令子类中重写
/// </summary>
public virtual void UnDo()
{ }
/// <summary>
/// 恢复指令,不同的指令类型恢复方式不同,所以需要在具体的指令子类中重写
/// </summary>
public virtual void ReDo()
{ }
}
指令的类型有很多种,例如move,jump。不同的类型Undo和Redo的方法也不一样,所以需要把具体的类型用子类来重写
Move类
public class Move : CommandMode {
GameObject gmObject;
private float[] targetPos;
private float[] currentPos;
public float[] TargetPos
{
get { return targetPos; }
set { targetPos = value; }
}
public float[] CurrentPos
{
get { return currentPos; }
set { currentPos = value; }
}
public GameObject GmObject
{
get { return gmObject; }
set { gmObject = value; }
}
/// <summary>
/// 构造函数初始化
/// </summary>
/// <param name="gameObject">指令要生效的对象</param>
public Move(GameObject gameObject) : base(gameObject)
{
TargetPos = new float[3];
CurrentPos = new float[3];
GmObject = gameObject;
CurrentPos[0] = GmObject.transform.position.x;
CurrentPos[1] = GmObject.transform.position.y;
CurrentPos[2] = GmObject.transform.position.z;
}
/// <summary>
/// 向上移动
/// </summary>
public override void MoveUp()
{
TargetPos[0] = CurrentPos[0];
TargetPos[1] = CurrentPos[1] + 0.5f;
TargetPos[2] = CurrentPos[2];
GmObject.transform.position = new Vector3(TargetPos[0], TargetPos[1], TargetPos[2]);
}
/// <summary>
/// 向下移动
/// </summary>
public override void MoveDown()
{
TargetPos[0] = CurrentPos[0];
TargetPos[1] = CurrentPos[1] - 0.5f;
TargetPos[2] = CurrentPos[2];
GmObject.transform.position = new Vector3(TargetPos[0], TargetPos[1], TargetPos[2]);
}
/// <summary>
/// 向左移动
/// </summary>
public override void MoveLeft()
{
TargetPos[0] = CurrentPos[0] - 0.5f;
TargetPos[1] = CurrentPos[1];
TargetPos[2] = CurrentPos[2];
GmObject.transform.position = new Vector3(TargetPos[0], TargetPos[1], TargetPos[2]);
}
/// <summary>
/// 向右移动
/// </summary>
public override void MoveRight()
{
TargetPos[0] = CurrentPos[0] + 0.5f;
TargetPos[1] = CurrentPos[1];
TargetPos[2] = CurrentPos[2];
GmObject.transform.position = new Vector3(TargetPos[0], TargetPos[1], TargetPos[2]);
}
/// <summary>
/// 撤销指令
/// </summary>
public override void UnDo()
{
GmObject.transform.position = new Vector3(CurrentPos[0], CurrentPos[1], CurrentPos[2]);
}
/// <summary>
/// 恢复指令
/// </summary>
public override void ReDo()
{
GmObject.transform.position = new Vector3(TargetPos[0], TargetPos[1], TargetPos[2]);
}
}
指令的具体实现已经完成,现在需要一个类来执行按键检测并该指令。
ExecuteCommand类
public class ExecuteCommand{
private GameObject gmObject; //指令执行具体对象
private CommandMode command; //具体的指令
private Dictionary<int, CommandMode> CommandDictionary; //存放指令的字典
private int currentKey; //当前指令在字典中的索引
private int lastKey; //上一步指令在字典中的索引
private int nextKey; //下一步指令在字典中的索引
/// <summary>
/// 按键变量
/// </summary>
public KeyCode KeyW;
public KeyCode KeyA;
public KeyCode KeyS;
public KeyCode KeyD;
public KeyCode KeyZ;
public KeyCode KeyY;
/// <summary>
/// 构造方法
/// </summary>
/// <param name="gameObject">指令生效的对象</param>
public ExecuteCommand(GameObject gameObject)
{
gmObject = gameObject;
CommandDictionary = new Dictionary<int, CommandMode>();
}
/// <summary>
/// 执行方法,用来在Update中调用
/// </summary>
public void ExecuteUpdate()
{
if (Input.GetKeyDown(KeyW))
{
Up();
}
if (Input.GetKeyDown(KeyS))
{
Down();
}
if (Input.GetKeyDown(KeyA))
{
Left();
}
if (Input.GetKeyDown(KeyD))
{
Right();
}
if (Input.GetKeyDown(KeyZ))
{
CommandUnDo();
}
if (Input.GetKeyDown(KeyY))
{
CommandReDo();
}
}
/// <summary>
/// 使目标向上移动,
/// </summary>
public void Up()
{
command = new Move(gmObject);
command.MoveUp();
PutCommandInDic(command);
}
/// <summary>
/// 使目标向下移动
/// </summary>
public void Down()
{
command = new Move(gmObject);
command.MoveDown();
PutCommandInDic(command);
}
/// <summary>
/// 使得目标向左移动
/// </summary>
public void Left()
{
command = new Move(gmObject);
command.MoveLeft();
PutCommandInDic(command);
}
/// <summary>
/// 使目标向右移动
/// </summary>
public void Right()
{
command = new Move(gmObject);
command.MoveRight();
PutCommandInDic(command);
}
/// <summary>
/// 指令的撤销
/// </summary>
public void CommandUnDo()
{
//如果currentKey为第一个指令,则不允许撤销。否则取出上一次的指令索引,便于撤销。
if (currentKey == 1)
{
return;
}
else
{
lastKey = currentKey - 1;
}
//用上一次的指令索引进行撤销
CommandMode UndoCommand = new CommandMode();
UndoCommand = CommandDictionary[lastKey];
UndoCommand.UnDo();
//撤销后,currentKey就应该等于新的
currentKey = lastKey;
}
/// <summary>
/// 指令的恢复
/// </summary>
public void CommandReDo()
{
//如果currentKey为最新的指令,则不允许重做。否则取出下一次的指令索引,便于重做。
if (currentKey == CommandDictionary.Count)
{
return;
}
else
{
nextKey = currentKey + 1;
}
//用下一次的指令索引进行重做。
CommandMode UndoCommand = new CommandMode();
UndoCommand = CommandDictionary[nextKey];
UndoCommand.ReDo();
//重做后,更新currentKey为最新的索引。
currentKey = nextKey;
}
/// <summary>
/// 将指令存入到字典中
/// </summary>
/// <param name="commandMode">具体的指令,该参数的值实际上指令的子类以父类的身份出现的</param>
private void PutCommandInDic(CommandMode commandMode)
{
//在指令序列中,把当前撤销/重做的指令后面的指令全部删除掉,用以记录新的指令序列。
ArrayList keyArrayList = new ArrayList();
foreach (int key in CommandDictionary.Keys)
{
if (key > currentKey)
{
keyArrayList.Add(key);
}
}
foreach (int i in keyArrayList)
{
CommandDictionary.Remove(i);
}
//记录新的指令序列,设置currentKey为最新的指令的索引。
CommandDictionary.Add(CommandDictionary.Count + 1, commandMode);
currentKey = CommandDictionary.Count;
}
}
接下来只需要在外部的Update中调用ExecuteUpdate(),并对KeyCode进行按键赋值即可。