用命令模式實現命令的輸入與撤銷

首先說下目的,是爲了實現相似手柄的輸入功能。假設一個手柄有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

相關文章
相關標籤/搜索