1. 程式人生 > >遊戲程式設計模式(Game Programming Patterns)的 C#&Unity 練習一:命令模式

遊戲程式設計模式(Game Programming Patterns)的 C#&Unity 練習一:命令模式

本人遊戲策劃一枚,開始學習一下游戲程式設計,以便於將來失業了可以做做獨立遊戲。

命令模式

核心描述:
將一個請求(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進行按鍵賦值即可。