- void HandleInput()
- {
- if (Input.GetKeyDown(KeyCode.A))
- {
- Attack();
- }
- else if (Input.GetKeyDown(KeyCode.B))
- {
- Run();
- }
- else if (Input.GetKeyDown(KeyCode.C))
- {
- Jump();
- }
- }
然后, 产品经理又提了需求, 用户可以自定义按键功能, 在很多游戏中都有做这样的功能, 为了实现这样的功能, 我们应该将这些对 Attack()和 Run()的调用转化成可以变换的东西, 下面用命令模式来重写一下这个功能:
先定义一个抽象类 Command 作为基类, 再定义具体的子类来重写 Excute();
- public abstract class Command{
- public abstract void Excute(GameActor actor);
- }
- public class AttackCommand : Command
- {
- public override void Excute()
- {
- // 攻击逻辑
- }
- }
- public class RunCommand : Command
- {
- public override void Excute()
- {
- // 奔跑逻辑
- }
- }
- public class JumpCommand : Command
- {
- public override void Excute()
- {
- // 跳跃逻辑
- }
- }
在 MonoBehaviour 的 Update 函数中, 每帧去监听用户输入, 并返回对应的 command
- public class GameControl : MonoBehaviour
- {
- private Command buttonA;
- private Command buttonB;
- private Command buttonC;
- private void Start()
- {
- buttonA = new AttackCommand();
- buttonB = new JumpCommand();
- buttonC = new RunCommand();
- }
- private void Update()
- {
- Command cmd = HandleInput();
- if (cmd != null)
- {
- cmd.Excute(actor);
- }
- }
- // 处理用户输入
- private Command HandleInput()
- {
- if (Input.GetKeyDown(KeyCode.A))
- {
- return buttonA;
- }
- else if (Input.GetKeyDown(KeyCode.B))
- {
- return buttonB;
- }
- else if (Input.GetKeyDown(KeyCode.C))
- {
- return buttonC;
- }
- else
- {
- return null;
- }
- }
- }
这样, 在按键触发和函数调用中间就加了一层 Command, 如果要自定义按键功能, 直接修改 Button 对应的 Command 就行了. 现在我们也可以修改一下上面的代码, 让我们可以用这套机制去控制任意角色对象, 只需将要控制的角色对象传进来即可:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- public class GameActor { }
- public class Actor1 : GameActor { }
- public class Actor2 : GameActor { }
- public abstract class Command{
- public abstract void Excute(GameActor actor);
- }
- public class AttackCommand : Command
- {
- public override void Excute(GameActor actor)
- {
- // 攻击逻辑
- }
- }
- public class RunCommand : Command
- {
- public override void Excute(GameActor actor)
- {
- // 奔跑逻辑
- }
- }
- public class JumpCommand : Command
- {
- public override void Excute(GameActor actor)
- {
- // 跳跃逻辑
- }
- }
- public class GameControl : MonoBehaviour
- {
- private Command buttonA;
- private Command buttonB;
- private Command buttonC;
- private GameActor actor;
- private void Start()
- {
- buttonA = new AttackCommand();
- buttonB = new JumpCommand();
- buttonC = new RunCommand();
- actor = new Actor1();
- }
- private void Update()
- {
- Command cmd = HandleInput();
- if (cmd != null)
- {
- cmd.Excute(actor);
- }
- }
- // 处理用户输入
- private Command HandleInput()
- {
- if (Input.GetKeyDown(KeyCode.A))
- {
- return buttonA;
- }
- else if (Input.GetKeyDown(KeyCode.B))
- {
- return buttonB;
- }
- else if (Input.GetKeyDown(KeyCode.C))
- {
- return buttonC;
- }
- else
- {
- return null;
- }
- }
- }
3, 支持可撤销的操作
命令模式在需要支持可撤销操作的情况下也能轻松应对, 假如我们需要给玩家提供撤销移动操作的功能时, 我们可以先把玩家输入产生的 command 存入栈中 (或者其他数据结构), 在撤销时, 从栈中取出栈顶的 Command, 再调用该 Command 的 Undo(), 就实现了撤销功能(Undo() 为撤销方法, 与 Excute()相反), 代码如下:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- public class GameActor {
- public Transform selfTra;
- public void Move(Vector3 offset)
- {
- selfTra.Translate(offset);
- }
- }
- public class Actor1 : GameActor { }
- public class Actor2 : GameActor { }
- public abstract class Command{
- public abstract void Excute(GameActor actor);// 执行
- public abstract void Undo(GameActor actor);// 撤销
- }
- public class MoveCommand : Command
- {
- public Vector3 moveOffset;
- public MoveCommand(Vector3 offset)
- {
- moveOffset = offset;
- }
- public override void Excute(GameActor actor)
- {
- actor.Move(moveOffset);
- }
- public override void Undo(GameActor actor)
- {
- actor.Move(-moveOffset);
- }
- }
- public class CommandControl : MonoBehaviour
- {
- private Command moveCommand;
- private GameActor actor;
- private Stack<Command> commandStack;
- private void Start()
- {
- moveCommand = new MoveCommand(Vector3.one);
- actor = new Actor1();
- commandStack = new Stack<Command>();
- }
- private void Update()
- {
- Command cmd = HandleInput();
- if (cmd != null)
- {
- commandStack.Push(cmd);
- cmd.Excute(actor);
- }
- }
- // 需要撤销操作时调用这个函数
- public void PlayReverse()
- {
- if (commandStack.Count> 0)
- {
- commandStack.Pop().Undo(actor);
- }
- }
- // 处理用户输入
- public Command HandleInput()
- {
- if (Input.GetKeyDown(KeyCode.A))
- {
- return new MoveCommand(new Vector3(2, 4, 5));
- }
- if (Input.GetKeyDown(KeyCode.B))
- {
- return new MoveCommand(new Vector3(1, 2, 4));
- }
- else
- {
- return null;
- }
- }
- }
上面代码中, 每次产生一个 command 时就将它存到 Stack 中, 当需要撤销操作时, 就取出 Stack 顶部的 command, 并执行它的 Undo(), 按照这种方法, 可以实现多重撤销.
4, 总结
通过上面的例子, 我们再看命令模式的定义: 将一个请求封装为一个对象, 从而使你可以用不同的请求对客户进行参数化; 对请求排队或记录请求日志, 以及支持可撤销的操作. 现在我们差不多明白了命令模式的用法, 它优点很明显, 缺点也是有的: 第一个优点是类间解耦, 调用者和接收者之间没有任何依赖关系, 调用者在实现功能时只需调用 Command 抽象类的 Excute 方法即可, 不需要关注是哪个接收者执行; 第二个优点是可扩展性, Command 的子类可以很容易地扩展; 缺点是如果有大量命令, 那么 Command 的子类将会非常庞大. 我们在实际开发中, 应该发挥出命令模式的优点, 并结合其他模式, 减少 Command 子类庞大的问题.
来源: https://www.cnblogs.com/IAMTOM/p/10190554.html