本文部分内容出自游戏编程模式一书,游戏编程模式,有兴趣的小伙伴可以去看看,虽然不是unity x c#写的 但是思路挺好的
目录
目录
0.先说结论
命令模式的好处:
解耦 扩展 延迟 撤销
命令模式是将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化; 对请求排队或记录请求日志,以及支持可撤销的操作
说白了,所谓命令模式就是将操作脚本分为一大堆单个命令 然后用一个类统一管理,所以 命令是具现化的方法调用
而其一般包含如下几个部分也可以有其他的部分,我只取其中一种
- 命令接口:定义命令的执行方法
- 具体命令类:实现命令接口,封装具体操作
- 命令管理类: 管理所有的具体命令类
- 实现命令类:实现上述所有的行为
发现问题
首先 不使用命令模式来实现角色的几个行为代码如下,这是非常非常常用的方法
cs
public class CommandPart : MonoBehaviour
{
private void Update() {
if(Input.GetKeyDown(KeyCode.Q)){
jump();
}
if (Input.GetKeyDown(KeyCode.W)) {
move();
}
if (Input.GetKeyDown(KeyCode.E)) {
hit();
}
}
public void jump() {
Debug.Log("跳跃");
}
public void move() {
Debug.Log("移动");
}
public void hit() {
Debug.Log("击打");
}
}
但是,似乎有几个问题
1.当我角色的行为一旦多起来,岂不是要在update写一大堆东西?
2.输入检测和行为方法强耦合,改键容易影响到其他代码
3.试想一下如果我有10种动作 ,CommandPart 个类说不定会扩展到一万多行,当我需要修改其中一个动作的时候
就需要在有一万多行代码的类中找一个函数中的几行关键代码,感觉很不好
命令模式如何解耦
为了演示方便 我都将其写到一个脚本中了,通常来说是写到很多个不同脚本之中的,请注意到这一点!!!
我需要一个命令基类
cs
// 命令基类 提供一个可重写的执行方法
public abstract class Command {
public abstract void Execute();
}
我给将三个行为分成三个具体命令类,并继承基类,对其方法进行封装
cs
// 角色行为的几个方法 需要去继承
public class JumpCommand : Command {
public override void Execute() {
Jump();
}
private void Jump() {
Debug.Log("跳跃");
}
}
public class MoveCommand : Command {
public override void Execute() {
Move();
}
private void Move() {
Debug.Log("移动");
}
}
public class HitCommand : Command {
public override void Execute() {
Hit();
}
private void Hit() {
Debug.Log("击打");
}
}
之后 写一个管理脚本,传入需要执行的参数
cs
// 命令管理器类
public class CommandCtrl{
private Command command;
public void ExecuteCommand(Command cmd) {
command = cmd;
cmd.Execute();
}
}
最后因为里氏替换原则,通过命令管理类去实现命令
cs
// 玩家的 实现命令类
public class CommandPart : MonoBehaviour {
private CommandCtrl cCtrl;
private void Awake() {
cCtrl =new CommandCtrl();
}
private void Update() {
if (Input.GetKeyDown(KeyCode.Q)) {
cCtrl.ExecuteCommand(new JumpCommand());
}
if (Input.GetKeyDown(KeyCode.W)) {
}
if (Input.GetKeyDown(KeyCode.E)) {
}
}
}
打个断点更利于分析
按下Q之后
可以清楚的看到JumpCommand的实例被传进来 执行了重写后的Execute方法
当我添加了HitCommand之后,按下对应按键
扩展的话就只需要多写几个具体命令类并继承命令类就行了
怎么实现延迟命令?
修改一下命令管理类 将命令存入一个List表里面就行了,别忘了清空命令表
如果还有其他需求 可以用栈或队列去存储
cs
// 命令管理器类
public class CommandCtrl{
private List<Command> cmdList = new List<Command>();
//执行命令
public void ExecuteCommand(Command cmd) {
this.cmdList.Add(cmd);
cmd.Execute();
}
//延迟执行的函数
public void DelayCommand(Command cmd){
this.cmdList.Add(cmd);
//满足一定条件后 执行逻辑 单纯延时用定时器 协程 Time类等
cmd.Execute();
}
//清空命令表
public void ClearComman(){
cmdList.Clear();
}
}
cs
if (Input.GetKeyDown(KeyCode.Q)) {
cCtrl.DelayCommand(new JumpCommand());
}
如何撤销命令?
实现思路如图
代码我就抛砖引玉了
cs
//移除最后一个命令
public void UndoCommand(){
if(cmdList.Count>0){
cmdList.RemoveAt(cmdList.Count - 1);
//这里执行游戏回溯 比如位置回溯 金币回溯等等
}
}
脚本整体一览
cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 命令基类 提供一个可重写的执行方法
public abstract class Command {
public abstract void Execute();
}
// 角色行为的几个方法 需要去继承
public class JumpCommand : Command {
public override void Execute() {
Jump();
}
private void Jump() {
Debug.Log("跳跃");
}
}
public class MoveCommand : Command {
public override void Execute() {
Move();
}
private void Move() {
Debug.Log("移动");
}
}
public class HitCommand : Command {
public override void Execute() {
Hit();
}
private void Hit() {
Debug.Log("击打");
}
}
// 命令管理器类
public class CommandCtrl{
private List<Command> cmdList = new List<Command>();
//执行命令
public void ExecuteCommand(Command cmd) {
this.cmdList.Add(cmd);
cmd.Execute();
}
//延迟执行的函数
public void DelayCommand(Command cmd){
this.cmdList.Add(cmd);
//满足一定条件后 执行逻辑 单纯延时用定时器 协程 Time类等
cmd.Execute();
}
//移除最后一个命令
public void UndoCommand(){
if(cmdList.Count>0){
cmdList.RemoveAt(cmdList.Count - 1);
//这里执行游戏回溯 比如位置回溯 金币回溯等等
}
}
//清空命令表
public void ClearComman(){
cmdList.Clear();
}
}
// 玩家的 实现命令类
public class CommandPart : MonoBehaviour {
private CommandCtrl cCtrl;
private void Awake() {
cCtrl =new CommandCtrl();
}
private void Update() {
if (Input.GetKeyDown(KeyCode.Q)) {
cCtrl.DelayCommand(new JumpCommand());
}
if (Input.GetKeyDown(KeyCode.W)) {
cCtrl.ExecuteCommand(new HitCommand());
}
if (Input.GetKeyDown(KeyCode.E)) {
cCtrl.ExecuteCommand(new MoveCommand());
}
}
}
不足分析(AI)
命令模式的基础框架比较简单的,但是要完善起来还是需要具体问题具体分析
以下是这段代码存在的一些不足之处: