日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

命令模式(2)

發布時間:2025/3/20 编程问答 11 豆豆
生活随笔 收集整理的這篇文章主要介紹了 命令模式(2) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

實現undo和redo-------基于Unity3D

我們已經知道了什么是命令模式,一個命令即是一個對象。
撤銷和重做是命令模式成名之作, 利用撤銷,我們可以回滾一些不滿意的操作。例如在策略游戲中,我們常常需要排兵布陣,布置自己的戰術,往往在沒有確定之前,對某個或某些操作不是很滿意,進而希望撤銷之前的操作,或者自己的誤操作撤銷了某些步驟,希望重做。在策略游戲中,我們更希望玩家的注意力集中在策略上,而不是因為誤操作而無法回滾。

如果我們不使用命令模式,我們將很難實現撤銷和重做的功能,但事實上,我們利用命令模式就可以輕而易舉的實現這個功能。還是很之前一樣,我將基于Unity3D來實現這些功能。

為了簡單起見,我將完成一個最簡單的undo和redo的功能。這個可以undo和redo的命令是:移動玩家一個單位。

這個命令和之前的命令有所區別,最本質的區別在于之前的命令只要創建一次,例如Jump功能,默認會綁定給K,在之后如果不進行修改的話,這個命令始終會保持只有一個實例。而之上我們描述的命令更加具體,這也就意味著每次玩家選擇一個動作,輸入處理程序都要創建一個新的命令實例。

既然我們希望撤銷和重做,那么毫無疑問的是我們必須保存下來這些命令,如果不保存,那么當我們需要撤銷或重做的時候,去哪里找這些已經執行了的命令呢?

我們再來仔細想想,通常情況下,我們后執行的命令會被先撤銷。后撤銷的命令會被先執行。后……先……,沒錯stack,我們可以利用棧這一數據結構來保存我們使用的命令。具體來說我是這樣設計的:

  • commands棧,當每執行一個命令的時候(在這里我們執行的對象移動的操作),將這個命令壓入到commands這個棧中去。
  • redoCommands棧,當我們每撤銷一個命令的時候,我們將這個撤銷的命令壓入到 redoCommands這個棧中去,當我們需要重做時,將命令彈出。注意此時的命令相當于是又執行了命令,于是我們再次將這個命令壓入到commands這個棧中去。當有新的命令產生的時候,將清空redoCommands這個棧

接下來我會對命令模式(1)中的代碼進行進一步的修改,來達到undo和redo的效果。

1. 創建一個move的命令類:

x_,y_代表此命令,希望傳遞進來的游戲對象移動到的位置坐標xBefore_,yBefore_代表游戲對象在執行這個命令之前的坐標execute和undo分別會調用actor的方法,讓其移動。

using System.Collections; using System.Collections.Generic; using UnityEngine;public class MoveCommand : Command {private int x_, y_;private int xBefore_, yBefore_;public MoveCommand(ActorAction actor, int x,int y){x_ = x;y_ = y;xBefore_ = actor.getX();yBefore_ = actor.getY();}public override void execute(ref ActorAction actor){actor.moveTo(x_,y_);}public override void undo(ref ActorAction actor){actor.moveTo(xBefore_, yBefore_);} }

2. ActorAction中增加移動的方法

using System.Collections; using System; using System.Collections.Generic; using UnityEngine;public class ActorAction : MonoBehaviour {private int x_, y_;private Transform actorTrans_;void Start(){actorTrans_ = this.gameObject.GetComponent<Transform>();}public int getX(){return x_;}public int getY(){return y_;}public void moveTo(int x,int y){x_ = x;y_ = y;actorTrans_.position = new Vector3(x, y, actorTrans_.position.z);}public void attack(){Debug.Log("attack");}public void jump(){Debug.Log("jump");}public void avoid(){Debug.Log("avoid");}}

3. 在InputHandler實現上述的兩個棧

  • 在InputHandler類中添加兩個新成員
    private Stack commands;
    private Stack redoCommands;
    在開始的時候初始化它們(在Start函數中進行初始化)
    commands = new Stack();
    redoCommands = new Stack();

  • 將handleInput這個函數做出修改
    當前我們可以撤銷的操作是向上下左右四個方向進行移動。當接到新的命令時,我們將清空redo棧,即來了新的命令以后,就不可以重做了。接著我們根據輸入創建新的移動命令,并壓入到commands棧中,返回這個命令。

  • 實現undo方法
    當commands里不為空的時候,我們將當前的命令壓入要redoCommands棧中,然后彈出這個命令并執行

  • 實現redo方法
    當redoCommands不為空時,同樣將其壓入commands中,然后彈出并執行

  • 總而言之
    如果要是在撤銷和重做之間來回切換的話,執行的操作也就是在commands和redoCommands這兩個棧之間進行pop和push操作。

以下是完整的代碼

using System.Collections; using System.Collections.Generic; using System; using UnityEngine;public class InputHandler :MonoBehaviour{private Command buttonJ_;private Command buttonK_;private Command buttonL_;private Command buttonUp_;private Command buttonDown_;private Command buttonLeft_;private Command buttonRight_;private ActorAction actor_;private Stack<Command> commands;private Stack<Command> redoCommands;void Start(){actor_ = this.gameObject.GetComponent<ActorAction>();commands = new Stack<Command>();redoCommands = new Stack<Command>();buttonJ_ = new AttackCommand();if (buttonJ_ == null) Debug.Log("buttonJ_ is null!");buttonK_ = new JumpCommand();if (buttonK_ == null) Debug.Log("buttonK_ is null!");buttonL_ = new AvoidCommand();if (buttonL_ == null) Debug.Log("buttonL_ is null!");}public void bindCommand(string buttonName,Command command){switch (buttonName){case "J":case "j": buttonJ_ = command; break;case "K":case "k": buttonK_ = command; break;case "L":case "l": buttonL_ = command; break;}}public Command handleInput(string keyName){switch (keyName){case "J":return buttonJ_;case "K":return buttonK_;case "L":return buttonL_;case "Up":redoCommands.Clear();buttonUp_ = new MoveCommand(actor_, actor_.getX(), actor_.getY() + 1);if (buttonUp_ == null) Debug.Log("buttonUp_ is null!");commands.Push(buttonUp_);return buttonUp_;case "Down":redoCommands.Clear();buttonDown_ = new MoveCommand(actor_, actor_.getX(), actor_.getY() - 1);if (buttonDown_ == null) Debug.Log("buttonDown_ is null!");commands.Push(buttonDown_);return buttonDown_;case "Left":redoCommands.Clear();buttonLeft_ = new MoveCommand(actor_, actor_.getX() - 1, actor_.getY());if (buttonLeft_ == null) Debug.Log("buttonLeft_ is null!");commands.Push(buttonLeft_);return buttonLeft_;case "Right":redoCommands.Clear();buttonRight_ = new MoveCommand(actor_, actor_.getX() + 1, actor_.getY());if (buttonRight_ == null) Debug.Log("buttonRight_ is null!");commands.Push(buttonRight_);return buttonRight_;default:return null;}}public void undo(){if(commands.Count!=0){redoCommands.Push(commands.Peek());commands.Pop().undo(ref actor_);} }public void redo(){if (redoCommands.Count != 0){commands.Push(redoCommands.Peek());redoCommands.Pop().execute(ref actor_);}} }

4. 修改判斷輸入的主邏輯

在判斷輸入的地方加上Z和X對應的功能,我定義Z為undoX為redo。

else if (Input.GetKeyDown(KeyCode.Z))inputHandler.undo(); else if (Input.GetKeyDown(KeyCode.X))inputHandler.redo();

至此我們已經完成了基本的undo和redo的功能了!

轉載于:https://www.cnblogs.com/WAoyu/p/8459249.html

總結

以上是生活随笔為你收集整理的命令模式(2)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。