Unity%20%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20%E4%B9%8B%20%E8%A1%8C%E4%B8%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F-%E3%80%90%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F%E3%80%91%E3%80%90%E8%B4%A3%E4%BB%BB%E9%93%BE%E6%A8%A1%E5%BC%8F%E3%80%91">Unity 设计模式 之 行为型模式-【命令模式】【责任链模式】
目录
Unity%20%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20%E4%B9%8B%20%E8%A1%8C%E4%B8%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F-%E3%80%90%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F%E3%80%91%E3%80%90%E8%B4%A3%E4%BB%BB%E9%93%BE%E6%A8%A1%E5%BC%8F%E3%80%91-toc" style="margin-left:0px;">Unity 设计模式 之 行为型模式-【命令模式】【责任链模式】
一、简单介绍
二、命令模式(Command Pattern)
1、什么时候使用命令模式
2、使用命令模式的好处
3、使用时的注意事项
Unity%20%E4%B8%AD%E4%BD%BF%E7%94%A8%20%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F-toc" style="margin-left:40px;">三、在 Unity 中使用 命令模式
1、定义命令接口
2、定义接收者类
3、实现具体命令类
4、实现请求者类
5、创建游戏管理器
Unity%20%E4%B8%AD%E6%B5%8B%E8%AF%95-toc" style="margin-left:120px;">6、在 Unity 中测试
7、示例分析
四、责任链模式(Chain of Responsibility Pattern)
1、什么时候使用责任链模式
2、使用责任链模式的好处
4、使用时的注意事项
1、定义请求类
2、定义处理者接口
3、实现具体处理者类
4、创建请求者类
Unity%20%E4%B8%AD%E6%B5%8B%E8%AF%95-toc" style="margin-left:120px;">5、在 Unity 中测试
6、示例分析
一、简单介绍
设计模式 是指在软件开发中为解决常见问题而总结出的一套 可复用的解决方案。这些模式是经过长期实践证明有效的 编程经验总结,并可以在不同的项目中复用。设计模式并不是代码片段,而是对常见问题的 抽象解决方案,它提供了代码结构和模块间交互的一种设计思路,帮助开发者解决特定的设计问题。
设计模式的特点:
二、命令模式(Command Pattern)
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成对象,从而使得可以使用不同的请求、队列或日志请求,以及支持可撤销的操作。命令模式通常包含四个主要角色:命令(Command)、接收者(Receiver)、请求者(Invoker)和客户端(Client)。命令对象持有请求的详细信息,而接收者则执行这些请求。
1、什么时候使用命令模式
- 需要支持撤销/重做功能时:例如文本编辑器、绘图软件等需要频繁撤销和重做操作的应用。
- 需要将请求发送者与接收者解耦时:当请求的发送者和接收者之间的依赖关系需要降低时,可以使用命令模式。
- 需要记录请求日志时:当需要跟踪请求的历史以进行审计时,命令模式提供了一种优雅的解决方案。
- 需要实现异步请求时:可以将请求放入队列中,并在未来的某个时间点执行。
2、使用命令模式的好处
- 解耦请求与执行:命令模式将请求的发送者与接收者解耦,使得两者之间的关系更加灵活。
- 支持撤销操作:通过将操作封装成命令对象,可以轻松实现撤销(Undo)和重做(Redo)功能。
- 支持日志请求:可以将请求的历史记录进行存储,方便后续审计或分析。
- 可以实现队列请求:可以将命令对象放入队列中,按顺序执行或延迟执行。
3、使用时的注意事项
- 复杂性:命令模式可能会引入额外的复杂性,尤其是在命令对象数量较多时。需要确保命令的设计和管理是清晰的。
- 性能考虑:由于命令模式会创建多个命令对象,因此可能会增加内存开销。在性能敏感的场合,需要谨慎使用。
- 过度设计:对于简单的应用,使用命令模式可能会导致过度设计。在这些情况下,直接的方法调用可能更为简单和有效。
命令模式通过将请求封装为对象,实现了解耦、可撤销性和请求队列等功能。它适用于需要支持撤销、日志记录或异步请求的场合,但在设计时需要考虑到可能引入的复杂性和性能问题。
Unity%20%E4%B8%AD%E4%BD%BF%E7%94%A8%20%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F">三、在 Unity 中使用 命令模式
在 Unity 中实现 命令模式 的示例,我们可以创建一个简单的场景,其中玩家可以使用命令来控制一个角色的移动和跳跃。这个示例将演示如何使用命令模式来处理这些操作。
使用命令模式控制角色
参考类图如下:
1、定义命令接口
首先,我们定义一个命令接口,描述执行命令的基本操作。
public interface ICommand
{void Execute();void Undo();
}
2、定义接收者类
接下来,我们定义一个接收者类,代表要执行命令的对象(如角色)。
using UnityEngine;public class PlayerCharacter : MonoBehaviour
{public void Move(Vector3 direction){transform.position += direction;Debug.Log($"Moved to: {transform.position}");}public void Jump(float height){transform.position += Vector3.up * height;Debug.Log($"Jumped to: {transform.position}");}
}
3、实现具体命令类
然后,我们实现具体的命令类,分别用于角色的移动和跳跃操作。
public class MoveCommand : ICommand
{private PlayerCharacter player;private Vector3 direction;public MoveCommand(PlayerCharacter player, Vector3 direction){this.player = player;this.direction = direction;}public void Execute(){player.Move(direction);}public void Undo(){player.Move(-direction); // 反向移动}
}public class JumpCommand : ICommand
{private PlayerCharacter player;private float height;public JumpCommand(PlayerCharacter player, float height){this.player = player;this.height = height;}public void Execute(){player.Jump(height);}public void Undo(){player.Move(-Vector3.up * height); // 反向跳跃}
}
4、实现请求者类
我们实现一个请求者类,用于调用命令。
using System.Collections.Generic;
using UnityEngine;public class CommandInvoker : MonoBehaviour
{private Stack<ICommand> commandHistory = new Stack<ICommand>();public void ExecuteCommand(ICommand command){command.Execute();commandHistory.Push(command);}public void UndoCommand(){if (commandHistory.Count > 0){ICommand lastCommand = commandHistory.Pop();lastCommand.Undo();}}
}
5、创建游戏管理器
最后,我们创建一个游戏管理器类,处理用户输入并执行命令。
using UnityEngine;public class GameManager : MonoBehaviour
{public PlayerCharacter playerCharacter;private CommandInvoker commandInvoker;void Start(){commandInvoker = gameObject.AddComponent<CommandInvoker>();}void Update(){if (Input.GetKeyDown(KeyCode.W)){var moveCommand = new MoveCommand(playerCharacter, Vector3.forward);commandInvoker.ExecuteCommand(moveCommand);}else if (Input.GetKeyDown(KeyCode.S)){var moveCommand = new MoveCommand(playerCharacter, Vector3.back);commandInvoker.ExecuteCommand(moveCommand);}else if (Input.GetKeyDown(KeyCode.Space)){var jumpCommand = new JumpCommand(playerCharacter, 2.0f);commandInvoker.ExecuteCommand(jumpCommand);}else if (Input.GetKeyDown(KeyCode.U)){commandInvoker.UndoCommand(); // 撤销上一个命令}}
}
Unity%20%E4%B8%AD%E6%B5%8B%E8%AF%95">6、在 Unity 中测试
- 创建一个带有 Collider 的 Cube 作为玩家角色,并附加
PlayerCharacter
脚本。 - 创建一个空的 GameObject,命名为 GameManager,并附加
GameManager
脚本。 - 运行游戏,使用
W
和S
键控制前后移动,使用Space
键跳跃,使用U
键撤销上一个命令。
7、示例分析
- 命令(ICommand):定义执行和撤销的基本操作。
- 接收者(PlayerCharacter):实现角色的具体操作。
- 具体命令(MoveCommand 和 JumpCommand):封装角色的移动和跳跃逻辑。
- 请求者(CommandInvoker):负责执行和撤销命令。
这个示例展示了如何在 Unity 中实现 命令模式,通过封装角色的操作为命令对象,使得用户能够灵活地控制角色,同时支持撤销操作。这种模式在需要记录和管理用户输入的场景中非常有用。
四、责任链模式(Chain of Responsibility Pattern)
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它通过将请求的发送者和接收者解耦,使得多个对象都有机会处理请求。将这些对象连接成一条链,并沿着这条链传递请求,直到有对象处理它为止。该模式通常包含四个主要角色:请求(Request)、处理者(Handler)、具体处理者(ConcreteHandler)和客户端(Client)。
1、什么时候使用责任链模式
- 需要多个对象处理请求时:当一个请求需要被多个对象处理时,可以使用责任链模式。
- 处理请求的逻辑不明确时:当不确定哪个对象能够处理请求,或者希望多个对象有机会处理请求时,可以采用责任链模式。
- 请求处理的顺序可能变化时:如果请求的处理顺序可能发生变化,责任链模式可以提供更好的灵活性。
- 需要支持可扩展性时:当系统可能需要增加新处理者而不影响现有代码时,责任链模式是一个良好的选择。
2、使用责任链模式的好处
- 降低耦合度:发送者和接收者之间的关系被解耦,发送者不需要知道哪个具体的处理者来处理请求。
- 灵活性:可以动态添加或修改处理者,使得系统更加灵活和可扩展。
- 简化代码:通过将请求处理逻辑分散到多个处理者中,可以使得每个处理者的代码更加简洁,提高代码的可读性。
- 动态处理请求:可以通过改变责任链的结构和顺序来改变请求的处理方式。
4、使用时的注意事项
- 请求处理的顺序:责任链模式可能导致请求处理的顺序变得不易理解,因此需要明确责任链的顺序。
- 链的长度:过长的责任链可能导致性能下降,尤其是在链中的处理者都不处理请求的情况下。
- 处理失败的情况:需要设计好处理失败的情况,以免请求在链中被无效地传递。
- 调试困难:由于请求在链中可能被多个处理者处理,调试请求的流程可能会比较困难。
责任链模式通过将请求和处理者解耦,实现了请求的灵活处理和动态配置。适用于多个对象可能处理请求的场景,但在使用时需要考虑链的顺序、长度及调试难度等因素。
五、在 Unity 中使用 责任链模式
在 Unity 中实现 责任链模式 的示例,我们可以创建一个简单的游戏事件处理系统,其中多个处理者可以处理玩家的不同输入事件,例如跳跃、攻击和防御。这个示例将演示如何使用责任链模式来管理这些事件。
使用责任链模式处理玩家输入事件
参考类图如下:
1、定义请求类
首先,我们定义一个请求类,表示输入事件。
public class InputRequest
{public string Action { get; }public InputRequest(string action){Action = action;}
}
2、定义处理者接口
接下来,我们定义一个处理者接口,包含处理请求的方法。
public interface IInputHandler
{void SetNextHandler(IInputHandler nextHandler);void Handle(InputRequest request);
}
3、实现具体处理者类
然后,我们实现几个具体的处理者类,分别用于处理不同的输入事件。
using UnityEngine;public class JumpHandler : IInputHandler
{private IInputHandler nextHandler;public void SetNextHandler(IInputHandler nextHandler){this.nextHandler = nextHandler;}public void Handle(InputRequest request){if (request.Action == "Jump"){Debug.Log("Handling Jump action");}else{nextHandler?.Handle(request);}}
}public class AttackHandler : IInputHandler
{private IInputHandler nextHandler;public void SetNextHandler(IInputHandler nextHandler){this.nextHandler = nextHandler;}public void Handle(InputRequest request){if (request.Action == "Attack"){Debug.Log("Handling Attack action");}else{nextHandler?.Handle(request);}}
}public class DefendHandler : IInputHandler
{private IInputHandler nextHandler;public void SetNextHandler(IInputHandler nextHandler){this.nextHandler = nextHandler;}public void Handle(InputRequest request){if (request.Action == "Defend"){Debug.Log("Handling Defend action");}else{nextHandler?.Handle(request);}}
}
4、创建请求者类
我们创建一个请求者类,负责发送输入请求。
using UnityEngine;public class InputInvoker : MonoBehaviour
{private IInputHandler inputHandlerChain;void Start(){// 设置责任链JumpHandler jumpHandler = new JumpHandler();AttackHandler attackHandler = new AttackHandler();DefendHandler defendHandler = new DefendHandler();jumpHandler.SetNextHandler(attackHandler);attackHandler.SetNextHandler(defendHandler);inputHandlerChain = jumpHandler;}void Update(){if (Input.GetKeyDown(KeyCode.Space)){InputRequest jumpRequest = new InputRequest("Jump");inputHandlerChain.Handle(jumpRequest);}else if (Input.GetKeyDown(KeyCode.A)){InputRequest attackRequest = new InputRequest("Attack");inputHandlerChain.Handle(attackRequest);}else if (Input.GetKeyDown(KeyCode.D)){InputRequest defendRequest = new InputRequest("Defend");inputHandlerChain.Handle(defendRequest);}else if (Input.GetKeyDown(KeyCode.X)){InputRequest unknownRequest = new InputRequest("Unknown");inputHandlerChain.Handle(unknownRequest);}}
}
Unity%20%E4%B8%AD%E6%B5%8B%E8%AF%95">5、在 Unity 中测试
- 创建一个空的 GameObject,命名为 InputManager,并附加
InputInvoker
脚本。 - 运行游戏,按
Space
、A
、D
和X
键测试不同的输入事件。
6、示例分析
- 请求(InputRequest):封装输入事件的信息。
- 处理者(IInputHandler 和具体处理者类):定义如何处理不同的输入事件,形成责任链。
- 请求者(InputInvoker):负责接收输入并将其传递给责任链中的处理者。
这个示例展示了如何在 Unity 中实现 责任链模式,通过将输入事件处理逻辑分散到多个处理者中,使得系统灵活且可扩展。每个处理者负责处理特定的输入事件,并可以根据需求轻松添加或修改处理者。