Unity---游戏设计模式(13)之命令模式
概述参考请看 参考博客
将一个请求封装为一个Command对象,从而使你可用不同的请求对客户进行参数化; 对请求排队或记录请求日志,以及支持可撤销的操作。
比如RTS游戏中的基地升级功能。升级是需要时间的,当我们增加好几次升级时,它就会先等待第一次升级完成后才会执行后面的升级。
如果我们此时想要减少升级次数也可以。
我们就可以把每个升级之类的操作当作一个Command请求,每个请求需要排队执行,我们也可以撤销请求。
1、命令模式原型
命令模式原型UML图
命令模式原型代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 命令模式
/// </summary>
public class CommandMode : MonoBehaviour
{
private void Start()
{
CommandInvoker invoker = new CommandInvoker();
ConcreteCommand1 cmd1 = new ConcreteCommand1(new Receiver1());
ConcreteCommand1 cmd2 = new ConcreteCommand1(new Receiver1());
invoker.AddCommand(cmd1);
invoker.AddCommand(cmd2);
invoker.ExecuteCommand();
}
}
/// <summary>
/// 命令管理类
/// 根据invoker执行Command命令
/// </summary>
public class CommandInvoker
{
//命令集合
private List<ICommand> mCommands = new List<ICommand>();
public void AddCommand(ICommand command)
{
mCommands.Add(command);
}
/// <summary>
/// 使用params参数一次添加多个命令
/// </summary>
public void AddCommand(params ICommand[] commands)
{
foreach (ICommand command in commands)
{
mCommands.Add(command);
}
}
/// <summary>
/// 执行所有命令,执行完并清除
/// </summary>
public void ExecuteCommand()
{
foreach (ICommand command in mCommands)
{
command.Execute();
}
mCommands.Clear();
}
}
/// <summary>
/// 抽象命令类
/// </summary>
public abstract class ICommand
{
public abstract void Execute();
}
public class ConcreteCommand1 : ICommand
{
//每个命令类中有一个接收者,接收者对象绑定一个具体的动作
private Receiver1 mReceiver1;
public ConcreteCommand1(Receiver1 receiver1)
{
mReceiver1 = receiver1;
}
//通过调用接收者来执行具体的命令
public override void Execute()
{
mReceiver1.Action("ConcreteCommand1");
}
}
/// <summary>
/// 接收者类,绑定一个具体的执行操作,任何类都可能作为一个接收者
/// </summary>
public class Receiver1
{
public void Action(string cmd)
{
Debug.Log("Receiver1执行了命令:" + cmd);
}
}
3、命令模式实例
实例UML图
GameSceneState场景类,和状态模式中的实例相接。请看状态模式(1)实例
实例代码
GameSceneState
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameSceneState : ISceneState
{
public GameSceneState(SceneStateController controller)
: base("03GameScene", controller)
{
}
private Button mUpgradeButton;
private Button mCancelUpgradeButton;
//当前等级表示兵营的当前等级
//提升等级表示将要升级的次数
//升级时间表示每升一级的时间,会一直减
private Text mNowLevelTextNum;
private Text mUpLevelTextNum;
private Text mUpgradeTimeNum;
private Home mHome;
public override void EnterScene()
{
Init();
}
public override void UpdateScene()
{
UpdateHomeMessage();
if (mHome != null)
{
mHome.Update();
}
}
/// <summary>
/// 初始化
/// </summary>
public void Init()
{
mUpgradeButton = GameObject.Find("UpgradeButton").GetComponent<Button>();
mCancelUpgradeButton = GameObject.Find("CancelUpgradeButton").GetComponent<Button>();
mNowLevelTextNum = GameObject.Find("NowLevelTextNum").GetComponent<Text>();
mUpLevelTextNum = GameObject.Find("UpLevelTextNum").GetComponent<Text>();
mUpgradeTimeNum = GameObject.Find("UpgradeTimeNum").GetComponent<Text>();
mUpgradeButton.onClick.AddListener(HomeUpgrade);
mCancelUpgradeButton.onClick.AddListener(CancelHomeUpgrade);
mHome = new Home();
}
/// <summary>
/// 基地升级
/// </summary>
public void HomeUpgrade()
{
mHome.AddCommand(new UpgradeHomeCommand());
}
/// <summary>
/// 取消基地升级
/// </summary>
public void CancelHomeUpgrade()
{
mHome.RemoveCommand();
}
/// <summary>
/// 更新基地显示信息
/// </summary>
public void UpdateHomeMessage()
{
mNowLevelTextNum.text = mHome.mNowLevel.ToString();
mUpLevelTextNum.text = mHome.mUpLevel.ToString();
mUpgradeTimeNum.text = mHome.mUpgradeTimer.ToString("0.00");
//当正在升级为0,不能再点击取消升级按钮。
if (mHome.mUpLevel == 0)
{
mCancelUpgradeButton.interactable = false;
}
else
{
mCancelUpgradeButton.interactable = true;
}
}
}
Home
/// <summary>
/// 基地类---相当于CommandInvoker
/// </summary>
public class Home
{
/// <summary>
/// 命令集合
/// </summary>
private List<IHomeCommand> mHomeCommandList;
public int mNowLevel = 0;
public int mUpLevel = 0;
public float mUpgradeTimer = 2;
public float mUpgradeTime = 2;
public Home()
{
mHomeCommandList = new List<IHomeCommand>();
}
/// <summary>
/// 添加升级命令
/// </summary>
public void AddCommand(IHomeCommand command)
{
mHomeCommandList.Add(command);
mUpLevel++;
}
/// <summary>
/// 移除一个升级命令
/// </summary>
public void RemoveCommand()
{
mHomeCommandList.RemoveAt(mHomeCommandList.Count - 1);
mUpLevel--;
//如果提示等级为空,时间要重置
if (mUpLevel == 0)
{
mUpgradeTimer = mUpgradeTime;
}
}
public void Update()
{
UpdateHomeMessage();
}
/// <summary>
/// 更新升级时间信息
/// </summary>
public void UpdateHomeMessage()
{
if (mUpLevel > 0)
{
if (mUpgradeTimer > 0)
{
mUpgradeTimer -= Time.deltaTime;
}
else
{
//成功升一级:执行一个命令,命令集合改变,显示改变
mHomeCommandList[0].Execute();
mHomeCommandList.RemoveAt(0);
mUpLevel--;
mNowLevel++;
mUpgradeTimer = mUpgradeTime;
}
}
}
}
IHomeCommand
/// <summary>
/// 基地的命令---命令模式
/// </summary>
public abstract class IHomeCommand
{
public abstract void Execute();
}
UpgradeHomeCommand
/// <summary>
/// 基地升级命令
/// </summary>
public class UpgradeHomeCommand : IHomeCommand
{
public override void Execute()
{
Debug.Log("成功升1级");
}
}
效果
3、命令模式优缺点
优点
- 降低了请求者和实现者之间的耦合度。
- 对请求排队或记录请求日志,支持撤销操作。
- 可以容易地设计一个组合命令。
- 新命令可以容易地加入到系统中。
缺点
- 类个数较多
4、新知识
4.1 ToString()保留小数位
float num = 1.30f;
Console.WriteLine(num.ToString("#0.00"));
//输出1.30
Console.WriteLine(decTemp.ToString("#.##"));
//输出1.3
使用第二种方式保留小数位时,#会自动去掉0。