命令模式在游戏开发中的应用

命令模式的描述:

命令模式(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演示如下:


在这里插入图片描述

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


在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/JianZuoGuang/article/details/86529426
今日推荐