设计模式:命令模式(Command)
命令模式在游戏中的应用 最近在做的一个游戏需要撤销重做功能,而这正是命令模式使用的场合,于是尝试使用命令模式实现这个功能
并且该游戏的操作可能会引发一系列结果,到时候撤销需要将一系列结果撤销=。=
这里就不讲命令模式是什么了直接上链接命令模式 · Design Patterns Revisited · 游戏设计模式 (tkchu.me)
之前的实现 之前忘记了命令模式这个设计模式,实现了一个丑陋的东西
之前对于对应的操作,调用操作并记录该操作,在操作前后记录该物体的状态,然后撤销时调用该操作对应的撤销操作,十分不方便且会记录一些不需要的状态,并且每个操作都需对应的撤销操作写在case中
并且一个操作导致一系列的后续结果时,此时撤销将十分麻烦
命令模式实现 对于操作,实现一个抽象Command类,里面有执行撤销两个方法,还有两个命令类的字段,记录前后指令,last指令指向什么指令导致该指令执行,next用于记录该一个命令引发了什么指令发生,可能某个指令会导致一系列指令发生,于是使用List记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public abstract class Command { public abstract void Execute () ; public abstract void Undo () ; protected Command last; protected List<Command> next; public Command Last { get => last; } public List<Command> Next { set { next = value ; } get { if (next == null ) next = new List<Command>(); return next; } } }
对于具体的command,继承该类并实现执行、撤销方法
现在的结构是:
PlayerCommand实现了player的command父类,其下一堆子类实现具体command
以玩家移动为例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public abstract class PlayerCommand : Command { protected Player player; public PlayerCommand (Player player ) { this .player = player; } } public class PlayerMove : PlayerCommand { Vector2 dir; public PlayerMove (Player player,Vector2 dir ):base (player ) { this .dir = dir; } public override void Execute () { player.Move(dir); } public override void Undo () { player.Move(-dir); } }
对于指令的执行实际调用对应的操作,并将其添加到RedoManager已执行操作中,对指令的撤销重做执行交给redomanager管理执行,到此重做撤销就方便的实现啦
1 2 3 4 5 6 private void Move (Vector2 dir ){ Command cmd = new PlayerMove(player, dir); cmd.Execute(); RedoManager.Instance.AddCommand(cmd); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class RedoManager : MonoBehaviour { private RedoCommandList redo; private static RedoManager instance; public static RedoManager Instance { get { return instance; } } private void Awake () { instance = this ; redo = new RedoCommandList(); } public void AddCommand (Command command ) { redo.AppendRedoCommand(command, true ); } public void Redo () { Command command = redo.GetUndoCommand(out bool flag); if (!flag) return ; command.Execute(); redo.RedoHelp(command); } public void Undo () { Command command = redo.GetRedoCommand(out bool flag); if (!flag) return ; undo(command); redo.UndoHelp(command); } private void undo (Command cmd ) { foreach (Command command in cmd.Next) { undo(command); } cmd.Undo(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 using MyInputSystem;using System;using System.Collections.Generic;public class RedoCommandList { private List<Command> undoList; private List<Command> redoList; public RedoCommandList () { undoList = new List<Command>(); redoList = new List<Command>(); } public virtual void AppendRedoCommand (Command command, bool flag ) { redoList.Add(command); if (flag) { undoList.Clear(); } } public void AppendUndoCommand (Command command ) { undoList.Add(command); } public Command GetUndoCommand (out bool flag ) { Command t = null ; if (undoList.Count != 0 ) { flag = true ; t = undoList[undoList.Count - 1 ]; undoList.RemoveAt(undoList.Count - 1 ); } else flag = false ; return t; } public Command GetRedoCommand (out bool flag ) { Command t = null ; if (redoList.Count != 0 ) { flag = true ; t = redoList[redoList.Count - 1 ]; redoList.RemoveAt(redoList.Count - 1 ); } else flag = false ; return t; } internal void RedoHelp (Command command ) { AppendRedoCommand(command, false ); } internal void UndoHelp (Command command ) { AppendUndoCommand(command); } }