首先说下目的,是为了实现类似手柄的输入功能。假设一个手柄有A/B两个键,可以输入不同的指令并且可以替换(比如吃血瓶/跳跃/射击等操作)。除此之外还有一个额外的需求,就是可以记录玩家最近输入的X次操作,并且可以撤销。

我们先写一个Actor类,作为被控制的主角,这里只写了HP一个字段,因为我们下面暂时将A/B键的功能设为加血和扣血。

public class Actor {
public int HP;
public Actor(int hp)
{
HP = hp;
}
}

然后写好Command基类,需要设置一个被控制的Actor(游戏中可以替换主角/AI等),并且包含一个执行和撤销的方法。

public class Command
{
protected Actor actor;
public Command(Actor a)
{
actor = a;
}
~Command() { }
public virtual void Excute() { }
public virtual void Undo() { }
}

下面写了一个CommandA,执行的操作是对某一个Actor加10点血量,除此之外还写了一个CommandB,执行扣血,因为与CommandA差不多就不展示了。

public class CommandA : Command
{
~CommandA() { }
public CommandA(Actor a) : base(a) { }
public override void Excute()
{
actor.HP += 10;
Debug.Log("actor 加了10hp, 现在hp: " + actor.HP);
}
public override void Undo()
{
actor.HP -= 10;
Debug.Log("actor 撤销了加血, 现在hp: " + actor.HP);
}
}

下面是要实现一个命令的栈,可以记录最近的X次命令(我假设的是五次),并可以逐一撤销。

public class CommandNode //命令栈中的节点,记录的当前的命令以及它的上一个和下一个命令
{
~CommandNode()
{
Debug.Log("我被释放了");
}
public Command m_command;
public Command Command{ get { return m_command; } }
public CommandNode(Command command)
{
m_command = command;
}
public CommandNode preCommand;
public CommandNode nextCommand;
}
public class CommandStack
{
private int m_iCapacity = 0; //stack容量
private int m_iCount = 0; //stack内命令数量
private CommandNode firstNode; //存储的第一次的命令
private CommandNode lastNode; //最后一次命令
public CommandStack(int capacity)
{
m_iCapacity = capacity;
}
public bool IsEmpty()
{
return m_iCount == 0;
}
public void Push(Command command)
{
if(IsEmpty()) //如果stack为空,将第一个命令和最后一个命令都设成当前这个
{
CommandNode node = new CommandNode(command);
node.preCommand = null;
node.nextCommand = null;
firstNode = node;
lastNode = node;
}
else //stack不为空,将当前的命令设为最后的命令
{
CommandNode node = new CommandNode(command);
node.preCommand = lastNode;
node.nextCommand = null;
lastNode.nextCommand = node;
lastNode = node;
}
m_iCount++;
if (m_iCount > m_iCapacity) //命令数超过容量了,去除第一个
{
firstNode = firstNode.nextCommand;
firstNode.preCommand = null;
m_iCount--;
}
}
public Command Pop()
{
Command result = lastNode.Command; //返回最后一次命令
if (lastNode.preCommand != null) //如果最后一次命令之前还有命令,将其设为最后的命令
{
lastNode = lastNode.preCommand;
lastNode.nextCommand = null;
}
else //如果没有了,说明stack空了,将第一次和最后一次命令置空
{
firstNode = null;
lastNode = null;
}
m_iCount--;
return result;
}
}
写完上面的下面就是我的主要控制类了。
public class CommandPatternMain : MonoBehaviour
{
private Actor actor = new Actor(100);
CommandStack commands = new CommandStack(5);
Command command;
Command tempCommand = null;
private Command m_CommandA;
private Command m_CommandB;
private void Start()
{
SetCommands();
}
private void SetCommands()
{
m_CommandA = new CommandA(actor);
m_CommandB = new CommandB(actor);
}
private void Update()
{
command = InputHandler();
if(command != null)
{
command.Excute();
commands.Push(command);
}
if(Input.GetKeyDown(KeyCode.Space) && !commands.IsEmpty())
{
tempCommand = commands.Pop();
if(tempCommand != null)
tempCommand.Undo();
}
}
private Command InputHandler()
{
if (Input.GetKeyDown(KeyCode.A)) { return m_CommandA; }
if (Input.GetKeyDown(KeyCode.B)) { return m_CommandB; }
return null;
}
}

这个类里有两个固定的命令A和B,当玩家按下A和B对应的键位时执行这两个命令。至于具体是什么命令,我们在SetCommands方法里赋值了,当然游戏进行中可以随时替换。每当玩家执行一次命令之后会推入一个容量为5的命令栈,当玩家按下撤销的按键时会Pop出最后一次命令,并执行Undo的方法。下面看看测试结果,我们先加五次血,再扣三次血,再撤销五次,然后再加五次血,再扣三次血,再撤销五次,结果如下:

Java实现撤销 java怎么撤销_ide

Java实现撤销 java怎么撤销_Java实现撤销_02