<>命令模式的描述:

命令模式(Command
Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

意图: 将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

需求描述:
给定两种不同的工具:锤子、刷子;锤子可以用来砸开泥土表层,刷子可以用来清理泥土表面的灰尘。要求:当选中的工具为锤子时,鼠标点击屏幕的任意位置,锤子移动到鼠标点击的位置。当选中的工具为刷子时,鼠标点击屏幕的任意位置,刷子移动到鼠标点击的位置,并且如果不放开鼠标,可以继续拖动刷子(即刷子跟随鼠标移动)。下面分别使用常规方法和命令模式来实现。

首先给出一种最直接的、没有应用任何设计模式的实现方法,如下:

(代码仅作演示)
void Update () { if (CurrentToolType == "刷子") { if (Input.GetMouseButtonDown (0
)) { //计算鼠标相对刷子的偏移量 } if (Input.GetMouseButton (0)) { //根据偏移量改变刷子的偏移量,并让刷子跟随鼠标移动
} } else if (CurrentToolType == "锤子") { if (Input.GetMouseButtonDown (0)) {
//将锤子的位置改变为鼠标的位置 } } ///******* */ }

这种实现方法并没有什么错误,但是不利于扩展,比如说我现在需要加入镰刀、剪刀、菜刀等工具;且、这些工具的使用方法和刷子、锤子有相同和不同之处,那我就需要疯狂的在if语句里加判断,导致if语句臃肿;而且如果我需要为其中一些工具添加单独的限制条件,比如限制可拖动边界和可点击范围,那将是巨大的灾难。幸好,命令模式可以很好的帮我们解决这个问题。


首先对问题进行分析,我们有两个工具,用户可通过操作UI在工具之间进行切换;不同的工具有不同的使用方法;锤子需要通过点击鼠标来进行操作,刷子需要拖动鼠标来进行操作。两者都需要通过鼠标操作,那么我们可以为这个操作提供一个入口,抽象出一个基类来,代码如下:
public abstract class BaseCommand { /// <summary> /// 执行 /// </summary> public
abstract void Execute(Transform t); /// <summary> /// 边界限制 /// </summary> public
abstract bool BoundClamp(); }
接下来实现具体的操作工具的命令类。

操作锤子工具的命令类,如下:
public class ClickCommand : BaseCommand { public override void Execute (
Transform t) { if (Input.GetMouseButtonDown (0)) { Vector3 pos = Camera.main.
ScreenToWorldPoint(Input.mousePosition); pos = new Vector3 (pos.x, pos.y, Camera
.main.nearClipPlane); //Debug.Log(pos); t.transform.position = pos; } } ///
<summary> /// 锤子工具没有边界限制,可以全屏点击 /// </summary> /// <returns></returns> public
override bool BoundClamp () { return true; } }
操作刷子工具的命令类,如下:
public class SliderCommand : BaseCommand { private Vector3 offset = Vector3.
zero; private bool isDown = false; public override void Execute (Transform t) {
if (Input.GetMouseButtonDown (0)) { isDown = true; var pos = Camera.main.
ScreenToWorldPoint(Input.mousePosition); offset = t.transform.position - pos;
offset= new Vector3 (offset.x, offset.y, 0); if (!ClickClamp (new Vector3 (pos.x
, pos.y, pos.z), t)){ if (!BoundClamp())return; t.transform.position=new Vector3
(pos.x,pos.y,t.position.z); offset=Vector3.zero; } } if (isDown) { if (Input.
GetMouseButton(0)) { if (!BoundClamp())return; var pos = Camera.main.
ScreenToWorldPoint(Input.mousePosition); pos = new Vector3 (pos.x, pos.y, t.
transform.position.z); t.transform.position = pos + offset; } } if (Input.
GetMouseButtonUp(0)){ isDown=false; } } private bool ClickClamp (Vector3 pos,
Transform t) { pos = new Vector3 (pos.x, pos.y, Camera.main.nearClipPlane); if (
t.GetComponent<BoxCollider> ().bounds.Contains (pos)) { return true; } return
false; } /// <summary> /// 为刷子添加一些拖动范围的限制,例如, /// 限制可拖动范围为距离屏幕边缘上200px、下100px
/// </summary> /// <returns></returns> public override bool BoundClamp(){ var
pos=Input.mousePosition; //Debug.Log(pos); Rect rect=new Rect(200,100,Screen.
width-200,Screen.height-100); if (pos.x<rect.x||pos.x>rect.width){ return false;
} if (pos.y<rect.y||pos.y>rect.height){ return false; } return true; } }

有的时候我们需要为每个工具做一些单独的配置,比如更换不同的图片,选择使用不同的操作命令之类的;那么,我们可以为工具增加一个配置类用来实例化不同的操作命令,配置类如下:
///利用C#的反射机制,通过给定的字符串来实例化相应的的命令 public class ToolConfig : MonoBehaviour {
public BaseCommand command; public CommandType commandType; void Start () { Type
type= Type.GetType (commandType.ToString ()); if (type != null) { var temp =
Activator.CreateInstance (type); command = temp as BaseCommand; } else { Debug.
LogError("未找到要实例化的类,请检查类名是否和枚举值相同"); } } } //通过枚举来定义命令类型 public enum CommandType
{ ClickCommand,//锤子工具的命令 DragCommand//刷子工具的命令 }
接下来就是具体的调用工具命令的类:
(控制器根据当前选择的工具来实例化相应的命令)
public class ToolController : MonoBehaviour { public BaseCommand
_currentBasecommand; public Transform currentTran; public ToggleGroup
_toggleGroup; public Toggle[] _toggles; void Start () { foreach (var item in
_toggles) { item.onValueChanged.AddListener ((ison) => { _currentBasecommand =
ison?item.GetComponent<ToolConfig> ().command : null; }); } } void Update () {
if (_currentBasecommand!=null){ _currentBasecommand.Execute(currentTran); } } }
Demo演示如下:




扫描关注->当前文章末尾->获取工程地址:



友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信