在游戏原型的开发当中,会经常用到命令模式,对于命令模式的定义则是“将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作”,对于此定义,感觉有点晦涩难懂,简单来说就是把指令封装成对象然后对游戏物体发送指令从而改变该游戏物体的表现形式,以及对已发送的指令撤销。
下面举一个例子作为理解
首先创建一个ICommand的接口,用于给物体下命令
在ICommand接口种定义两个方法,分别是Excute和Undo方法

public interface ICommand
{
    void Execute(GameObject player);
    void Undo();
}

Excute方法用于对游戏物体发送指令
Undo方法则是用于撤销对该物体已经下达过的指令
然后在让移动方法的类实现ICommand接口的方法,我这边创建了上下左右的移动方法的方法,都实现了ICommand接口的方法
以MoveForward类做示范,其他移动方法以此类推

 public class MoveForward : ICommand
{
    private GameObject m_Player;
    public void Execute(GameObject player)
    {
        m_Player = player;
        player.transform.Translate(Vector3.forward);
    }

    public void Undo()
    {
        m_Player.transform.Translate(Vector3.back);
    }
}

然后在InputController的脚本中实例化 实现移动的方法

private readonly MoveForward m_Forward = new MoveForward();
private readonly MoveBack m_Back = new MoveBack();
private readonly MoveLeft m_Left = new MoveLeft();
private readonly MoveRight m_Right = new MoveRight();

之后在InputHandle方法中执行移动中的方法,然后在Update函数中调用

 if (Input.GetKeyDown(KeyCode.W))
        {
            m_Forward.Execute(m_Player);
        }

        if (Input.GetKeyDown(KeyCode.A))
        {
            m_Left.Execute(m_Player);
        }

        if (Input.GetKeyDown(KeyCode.D))
        {
            m_Right.Execute(m_Player);
        }

        if (Input.GetKeyDown(KeyCode.S))
        {
            m_Back.Execute(m_Player);
        }

因为我们需要撤销命名使得之前的操作可以回滚,所以我们就要创建一个CommandManager脚本管理我们的命令。
因此我们需要在CommandManager脚本里面定义一个List用于存储我们所有已经执行过的命名(当然我这边是为了方便才使用List,如果想用Queue,Stack都没问题)

private readonly List<ICommand> m_Commands = new List<ICommand>();

然后在定义一个公开的方法用于把操作都添加进List中,我这边就叫做AddCommands

public void AddCommands(ICommand command)
{
   m_Commands.Add(command);
}

然后在定义一个协程的用于撤销已发送的命令

public IEnumerator UndoStart()
{
   m_Commands.Reverse();
   foreach (var command in m_Commands)
    {
      yield return new WaitForSeconds(0.2f);
       command.Undo();
    }

       m_Commands.Clear();
 }

然后在回到InputController脚本,在按键操作事件中添加刚刚的AddCommands的公开方法(注意我这边一开始就把CommandManager做成了一个单例,用于全局调用)

if (Input.GetKeyDown(KeyCode.W))
{
  m_Forward.Execute(m_Player);
  CommandManager.Instance.AddCommands(m_Forward);
}//(以此类推)

然后在创建一个按键事件用于实现我们UndoStart的协程

if (Input.GetKeyDown(KeyCode.Space))
{
   StartCoroutine(CommandManager.Instance.UndoStart());
}

之后回到Unity3D中把InputController与CommandMangaer挂靠在一个游戏物体之后就可以进行操作了

完整代码:InputController

  public class InputController : MonoBehaviour
{
    private readonly MoveForward m_Forward = new MoveForward();
    private readonly MoveBack m_Back = new MoveBack();
    private readonly MoveLeft m_Left = new MoveLeft();
    private readonly MoveRight m_Right = new MoveRight();


    public GameObject m_Player;

    void Start()
    {
        m_Player=GameObject.Find("Player");
    }

    private void Update()
    {
        InputHandle();
    }


    private void InputHandle()
    {
        if (Input.GetKeyDown(KeyCode.W))
        {
            m_Forward.Execute(m_Player);
            CommandManager.Instance.AddCommands(m_Forward);
        }

        if (Input.GetKeyDown(KeyCode.A))
        {
            m_Left.Execute(m_Player);
            CommandManager.Instance.AddCommands(m_Left);
        }

        if (Input.GetKeyDown(KeyCode.D))
        {
            m_Right.Execute(m_Player);
            CommandManager.Instance.AddCommands(m_Right);
        }

        if (Input.GetKeyDown(KeyCode.S))
        {
            m_Back.Execute(m_Player);
            CommandManager.Instance.AddCommands(m_Back);
        }

        if (Input.GetKeyDown(KeyCode.Space))
        {
            StartCoroutine(CommandManager.Instance.UndoStart());
        }
    }
}

完整代码:MoveMentInput

 public class MoveForward : ICommand
{
    private GameObject m_Player;
    public void Execute(GameObject player)
    {
        m_Player = player;
        player.transform.Translate(Vector3.forward);
    }

    public void Undo()
    {
        m_Player.transform.Translate(Vector3.back);
    }
}


public class MoveLeft:ICommand
{
    private GameObject m_Player;
    public void Execute(GameObject player)
    {
        m_Player = player;
        player.transform.Translate(Vector3.left);
    }

    public void Undo()
    {
     m_Player.transform.Translate(Vector3.right);   
    }
}

public class MoveRight : ICommand
{
    private GameObject m_Player;
    public void Execute(GameObject player)
    {
        m_Player = player;
        player.transform.Translate(Vector3.right);
    }

    public void Undo()
    {
        m_Player.transform.Translate(Vector3.left);
    }
}

public class MoveBack : ICommand
{
    private GameObject m_Player;
    public void Execute(GameObject player)
    {
        m_Player = player;
        player.transform.Translate(Vector3.back);
    }

    public void Undo()
    {
        m_Player.transform.Translate(Vector3.forward);
    }
}

完整代码:CommandManager

 public class CommandManager : MonoBehaviour
{
    public static CommandManager Instance { set; get; }
    private readonly List<ICommand> m_Commands = new List<ICommand>();

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
        }
        else
        {
            Destroy(Instance);
        }
    }


    public void AddCommands(ICommand command)
    {
        m_Commands.Add(command);
    }

    public IEnumerator UndoStart()
    {
        m_Commands.Reverse();
        foreach (var command in m_Commands)
        {
            yield return new WaitForSeconds(0.2f);
            command.Undo();
        }

        m_Commands.Clear();
    }
}