用命令模式实现命令的输入与撤销

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

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

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差很少就不展现了。code

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次命令(我假设的是五次),并能够逐一撤销。blog

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的方法。下面看看测试结果,咱们先加五次血,再扣三次血,再撤销五次,而后再加五次血,再扣三次血,再撤销五次,结果以下:ci

相关文章
相关标签/搜索