(自用)基于unity的指令(命令)模式

发布时间 2023-12-02 23:45:22作者: 衣衣也想变可爱QAQ

指令模式

1.配置输入

  所有游戏中都包含玩家输入指令的部分(这些部分通常写在游戏循环中 如unity中的UpData() ) 游戏会每一帧都进行一次读取,当玩家按下相应按键时 则会进行对应方法
  为了可以时刻检测并记录玩家进行的操作,或者对某个对应的操作的指令进行更改,我们需要将这些输入封装为一个类似于变量的对象

  1. 定义一个基类代表游戏中可触发的行为
public Interface Command
{
    public void excute(){}

}
  1. 为不同游戏行为定义相应子类
public class JumpCommand : Command
(
    public void excute()
    {
        jump();
    }
)
public class FireCommand : Command
{
    public void excute()
    {
        Fire();
    }
}


  1. 在代码部分对定义的子类进行实例化 并放入输入部分
public class InputHandler
{
    JumpCommand jumpCommand;
    jumpCommand = new JumpCommand(){};
    FireCommand fireCommand;
    fireCommand = new fireCommand(){};
    if(Input.GetKeyDown(KeyCode.X))
    {
        jumpCommand;
    }
    else if(Input.GetKeyDown(KeyCode.Y))
    {
        fireCommand;
    }
}


相比起直接将控制内容写入游戏循环 在新的代码中 玩家每进行一次输入 都会访问一次对应的指令类

2.用户类

  当我们已经进行了一些操作时,需要对游戏中的某些对象进行操作 大部分情况是玩家所控制的角色,但有时需要改变角色类型或是更广泛的内容 在这种情况下 需要我们对游戏中的对象进行进一步封装,当玩家进行操作时,游戏内会对对象发出一定命令流 并找到接收命令的对象 再执行相关操作

public class Singleton<T>where T : MonoBehaviour
{
    protected Singleton(){};
    private static T _inst = null;
    public static T instance => _inst ?? (_inst = new T());
    public static void Clear()
   {
    _inst = null;
   }

}

首先我们要单例化指令类

public class JumpCommand : Command , Singleton<JumpCommand>
{
    void Excute()
    {
        //执行跳跃(执行的函数中包含对接收者的操作)
    }

}
public class FireCommand : Command , Singleton<FireCommand>
{
    void Excute()
    {
        //执行操作(执行的函数中包含对接收者的操作)
    }

}

在用户类中引用指令类

public class User : MonoBehaviour 
{
    GameObject Player =new GameObject(); //实例化接收者类
    public void Jump()
    {
        if(Input.GetKeyDown(KeyCode.R))
        {
            JumpCommand.instance.Excute();
        }
    }
    public void Fire()
    {
        if(Input.GetKeyDown(KeyCode.F))
        {
            FireCommand.instance.Excute();
        }
    }

}


最后在游戏循环中检测玩家操作

public void Start()
{
    User user1 = new User();
}
public void UpData()
{
    user1.Jump();
    user1.Fire();
}


通过这种方式,我们就可以更加灵活地更改玩家操作的内容和操作的对象 并且我们也可以使用AI代码对一些对象进行自动操作

3.指令撤销与重做

  当玩家进行一个更改操作之后 需要回到更改之前的状况 如果我们每帧都记载一次游戏的状况 然后当玩家按下返回按键时回到上一次记载的状况时 会导致游戏中出现大量的内存占用 但当我们在指令类中记录玩家的每次操作 当玩家按下返回按键时 只需要返回上一次操作即可 就可以大大降低内存占用并且达到返回指令的效果

  1. 首先我们在指令类中加入返回操作
public Interface Command
{
    public void Excute(){}
    public void UnExcute(){}
}


  1. 在控制类中完成返回操作
public class JumpCommand : Command 
{
    void Excute()
    {
        //执行跳跃(执行的函数中包含对接收者的操作)
    }
    void UnExcute()
    {
        //返回操作(返回操作中包含玩家执行操作前的数据)
    }

}
public class FireCommand : Command 
{
    void Excute()
    {
        //执行操作(执行的函数中包含对接收者的操作)
    }
    void UnExcute()
    {
        //返回操作(返回操作中包含玩家执行操作前的数据)
    }
}


  1. 在用户类中进行返回操作
public class User : MonoBehaviour 
{
    GameObject Player =new GameObject(); //实例化接收者类  

    int jumpCurrent = 0; //记录操作的次数
    int FireCurrent = 0; //记录操作的次数

    List<JumpCommand> jumpList = new List<JumpCommand>();
    //定义对应指令的列表 来储存当前指令的次数和本次指令中玩家的状态  
    List<FireCommand> fireList = new List<FireCommand>();
    //定义对应指令的列表 来储存当前指令的次数和本次指令中玩家的状态  

    public void Jump()
    {
        if(Input.GetKeyDown(KeyCode.R))
        {
            jumpCurrent++;  
            JumpCommand newJump = jumpList[jumpCurrent];
            newJump.Excute();
        }
        if(Input.GetKeyDown(KeyCode.Ctrl) && Input.GetKeyDown(KeyCode.R))
        {
            jumpCurrent--;
            jumpCommand reJump = jumpList[jumpCurrent];
            reJump.UnExcute();
        }
    }
    public void Fire()
    {
        if(Input.GetKeyDown(KeyCode.F))
        {
            fireCurrent++;
            FireCommand newFire = fireList[fireCurrent];  
            newFire.Excute(); 
        }
        if(Input.GetKeyDown(KeyCode.Ctrl) && Input.GetKeyDown(KeyCode.F))
        {
            fireCurrent--;
            fireCommand reFire = fireList[fireCurrent];
            reFire.UnExcute();
        }
    }

}    
 

最后在游戏循环中检测玩家操作

void Start()
{
    User user1 = new User();
}
void UpData()
{
    user1.Jump();
    user1.Fire();
}