Unity实现设计模式——命令模式
推荐一个Unity学习设计模式很好的GitHub地址:https://github.com/QianMo/Unity-Design-Pattern 有非常多的Star
一、介绍
命令模式使得请求的发送者与请求的执行者之间消除耦合,让对象之间的调用关系更加灵活。在命令模式中,会将一个命令封装成一个对象,同时命令模式也支持可撤销的操作。
命令里面通常聚合接收者,初始化命令时传入接收者,调用者直接调用命令即可
二、实现
这里使用Unity上下左右控制物体移动同时记录移动信息方便撤回为例子演示命令模式
1.ICommand
命令接口基类
public interface ICommand
{
void Execute();
void UnExecute();
}
2.MoveCommand
具体的移动命令
class MoveCommand : ICommand
{
private MoveDirection _direction;
private MoveCommandReceiver _receiver;
private float _distance;
private GameObject _gameObject;
//Constructor
public MoveCommand(MoveCommandReceiver reciever, MoveDirection direction, float distance,
GameObject gameObjectToMove)
{
this._receiver = reciever;
this._direction = direction;
this._distance = distance;
this._gameObject = gameObjectToMove;
}
//Execute new command
public void Execute()
{
_receiver.MoveOperation(_gameObject, _direction, _distance);
}
//Undo last command
public void UnExecute()
{
_receiver.MoveOperation(_gameObject, InverseDirection(_direction), _distance);
}
//invert the direction for undo
private MoveDirection InverseDirection(MoveDirection direction)
{
switch (direction)
{
case MoveDirection.up:
return MoveDirection.down;
case MoveDirection.down:
return MoveDirection.up;
case MoveDirection.left:
return MoveDirection.right;
case MoveDirection.right:
return MoveDirection.left;
default:
Debug.LogError("Unknown MoveDirection");
return MoveDirection.up;
}
}
//So we can show this command in debug output easily
public override string ToString()
{
return _gameObject.name + " : " + MoveDirectionString(_direction) + " : " + _distance.ToString();
}
//Convert the MoveDirection enum to a string for debug
public string MoveDirectionString(MoveDirection direction)
{
switch (direction)
{
case MoveDirection.up:
return "up";
case MoveDirection.down:
return "down";
case MoveDirection.left:
return "left";
case MoveDirection.right:
return "right";
default:
return "unkown";
}
}
}
3.MoveCommandReceiver
命令接收者
class MoveCommandReceiver
{
public void MoveOperation(GameObject gameObjectToMove, MoveDirection direction, float distance)
{
switch (direction)
{
case MoveDirection.up:
MoveY(gameObjectToMove, distance);
break;
case MoveDirection.down:
MoveY(gameObjectToMove, -distance);
break;
case MoveDirection.left:
MoveX(gameObjectToMove, -distance);
break;
case MoveDirection.right:
MoveX(gameObjectToMove, distance);
break;
}
}
private void MoveY(GameObject gameObjectToMove, float distance)
{
Vector3 newPos = gameObjectToMove.transform.position;
newPos.y += distance;
gameObjectToMove.transform.position = newPos;
}
private void MoveX(GameObject gameObjectToMove, float distance)
{
Vector3 newPos = gameObjectToMove.transform.position;
newPos.x += distance;
gameObjectToMove.transform.position = newPos;
}
}
4.命令的发出者(或者说是图中Clinet)
这里将命令发出者和Client放到了一起
public class InputHandler : MonoBehaviour
{
public float moveDistance = 10f;
public GameObject objectToMove;
private MoveCommandReceiver moveCommandReciever;
private Stack<MoveCommand> commandStack1 = new Stack<MoveCommand>();
private Stack<MoveCommand> commandStack2 = new Stack<MoveCommand>();
void Start()
{
moveCommandReciever = new MoveCommandReceiver();
if (objectToMove == null)
{
Debug.LogError("objectToMove must be assigned via inspector");
this.enabled = false;
}
}
public void Undo()
{
if (commandStack1.Count > 0)
{
MoveCommand moveCommand = commandStack1.Pop();
moveCommand.UnExecute();
commandStack2.Push(moveCommand);
}
}
public void Redo()
{
if (commandStack2.Count > 0)
{
MoveCommand moveCommand = commandStack2.Pop();
moveCommand.Execute();
commandStack1.Push(moveCommand);
}
}
private void Move(MoveDirection direction)
{
MoveCommand moveCommand = new MoveCommand(moveCommandReciever, direction, moveDistance, objectToMove);
moveCommand.Execute();
commandStack1.Push(moveCommand);
}
//Simple move commands to attach to UI buttons
public void MoveUp() { Move(MoveDirection.up); }
public void MoveDown() { Move(MoveDirection.down); }
public void MoveLeft() { Move(MoveDirection.left); }
public void MoveRight() { Move(MoveDirection.right); }
void Update()
{
if (Input.GetKeyDown(KeyCode.UpArrow))
{
MoveUp();
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
MoveDown();
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
MoveLeft();
}
if (Input.GetKeyDown(KeyCode.RightArrow))
{
MoveRight();
}
if (Input.GetKeyDown(KeyCode.R))
{
Redo();
}
if (Input.GetKeyDown(KeyCode.U))
{
Undo();
}
}
}