游戏设计模式:命令模式(以Unity开发坦克大战为例)

本文介绍了如何运用命令模式在Unity游戏开发中实现游戏行为和角色的解耦,便于代码维护和游戏行为记录。通过创建坦克类、命令接口和移动命令类,展示了如何实现游戏对象的移动控制,同时也讨论了命令模式在游戏回放、撤销功能和AI控制中的应用。此外,还给出了一个Player类的示例,说明如何响应玩家输入并驱动坦克移动。
摘要由CSDN通过智能技术生成

命令模式

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

我们在游戏设计中使用命令模式有以下好处:

​ 1.可以把游戏行为和游戏角色解耦合,易于分别维护游戏角色和游戏行为的代码,更新其一时不需要或只需要少量改动另一的代码

​ 2.可以通过记录产生过的游戏指令序列来记录整个游戏过程,生成体积很小的游戏回放文件

​ 3.易于实现游戏中的撤销功能

​ 4.易于编写AI操控游戏角色


示例

以坦克大战为例(这里使用Unity做示范):

1.首先我们得有一个坦克类的脚本,挂载在坦克的预制件上

  class Tank : MonoBehaviour  
    {
        GameObject t;
        Rigidbody2D rb;
        public float speed;
        void Start()
        {
            rb = transform.GetComponent<Rigidbody2D>();
        }
        public void Move(Vector2 v)
        {
            rb.velocity = v;
        }
    }

这个类负责提供操控一个坦克实例的函数,以及存储该坦克实例的各种参数。

2.然后我们设置一个命令类,以最基本的移动命令为例(大部分游戏物体都具有移动这一游戏行为)

 interface Command //给坦克下达的命令
    {
        void Execute(ref Tank t);
    };
    class MoveCommand : Command  //移动指令
    {
        private Vector2 v;
        private static MoveCommand instance = new MoveCommand();
        private MoveCommand(){   //阻止生成实例
            v = Vector2.zero;
        }
        public static ref MoveCommand getInstance(){  //返回实例的引用
            return ref instance;
        }
        public void Setv(Vector2 _v)  //设置速度
        {
            v = _v;
        }
        public void Execute(ref Tank t)
        {
            t.Move(v);
        }
    }

这里又使用了所谓单例模式:将移动命令类的构造函数声明为private,可以阻止其被初始化为多个实例,在内部生成一个实例然后通过getInstance返回,这样可以保证全局只有一个移动命令类的实例,好处是免去了在各个地方生成和销毁实例的开销,并且可以避免多个命令类实例同时对一个实例下达命令导致其行为冲突的可能性。

我们没有在命令类中绑定某一个实例,而是在Execute中直接传入,这样可以用同样的配置(比如这里是速度的配置)批量对任意数量的游戏实例执行命令。

而对于特定的某一个实例如果需要单独记录其配置,则可以在Tank类中增加相关的成员来记录,并在命令类中增加一个开关:使用实例中的配置还是指令中的配置。需要注意更改其配置也应通过相应命令类来实现。

在Execute中记录当前的命令类实例,执行的对象实例,以及执行时的时间,即可生成指令序列形式的游戏回放,也可以实现Undo(撤销)操作

我们再设置一个Player类,这个类用于接收玩家从外设输入的指令。

  public class Player : MonoBehaviour
    {
        KeyCode[] keymap;  //0:上 1:下 2:左 3:右
        Vector2[] dv;
        Tank t;
        MoveCommand mc;
        void Start()
        {
            //初始化按键列表
            keymap = new KeyCode[] { KeyCode.W, KeyCode.S, KeyCode.A, KeyCode.D };
            //初始化单位方向向量列表
            dv = new Vector2[] { Vector2.up,Vector2.down,Vector2.left,Vector2.right};  
            mc = MoveCommand.getInstance();
            
        }
   
        public void Bind(ref Tank _t)
        {
            t = _t;
            this.transform.parent=t.transform;  //让Player跟随绑定的坦克移动
        }
        /// <summary>
        /// 处理移动控制的外设输入
        /// </summary>
        void MoveControl() 
        {
            for(int i=0;i<4;i++)
            {
                if (Input.GetKey(keymap[i]))
                {
                    mc.Setv(t.speed*dv[i]);
                    mc.Execute(ref t);
                    return;
                }
            }
            mc.Setv(Vector2.zero);
            mc.Execute(ref t);
        }
        private void FixedUpdate()
        {
            MoveControl();
        }
    }

在本游戏中,玩家要控制一个坦克,我们可以在Player类里绑定一个游戏场景中的坦克的实例,这样当玩家需要重生或者更换坦克时就非常方便.

如果要支持多人游戏,可以生成多个Player实例绑定不同的坦克实例来实现,不需要添加额外的代码。

当Player实例接收到玩家输入的指令时, 就通过命令类把绑定的坦克实例作为参数传入,让命令类驱动坦克类执行命令。

接下来只要生成一个Tank实例然后调用Player的Bind函数绑定即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值