遊戲程式設計模式(Game Programming Patterns)的 C#&Unity 練習一:命令模式
阿新 • • 發佈:2019-01-08
本人遊戲策劃一枚,開始學習一下游戲程式設計,以便於將來失業了可以做做獨立遊戲。
命令模式
核心描述:
將一個請求(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進行按鍵賦值即可。