游戏编程模式之命令模式

命令模式

命令就是一个对象化(实例化)的方法调用(摘自《游戏编程模式》)

  • 上述引用是将命令模式概念进行精简的版本,未精简的概念为:命令模式就是"将一个请求(request)封装成一个对象,从而允许你使用不同的请求、队列或日志将客户端参数化,同时支持请求操作的撤销与恢复" 。(你大概也不想理解这一句)
  • 这两个概念与“回调(callback)”具有共性。

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。(摘自百度百科)

  • 命令就是面向对象化的回调
理解

抛开上面的概念,我们去通过一些特定的情形去理解命令模式。在书中的第一个情景为游戏中的输入配置。在游戏开发中,游戏的输入模块可以简化成如下图:

体现命令模型就在于游戏逻辑之输入判定部分的设计上

  • 简单的实现(硬编码)
        void InputHandler : handeInput
        {
            if(is_press1)           action1();
            else if(is_press2)      action2();
            else if(……)
                ……
        }
    
    • 缺陷:将输入输出与游戏动作绑定在了一起(我们无法动态修改 if 之后的事件)
  • 为了支持自定义功能,我们要对直接调用进行“开刀”,因为直接调用是照成无法自定义的原因。游戏逻辑应该这样设计:

这就是命令模式。可替换的对象用来实现对多种输入事件的可修改。输入与事件之间不再是一个直接调用的关系,而是利用其他对象进行间接调用

  • 利用继承与虚函数实现
    class Command
    {
        public:
            virtual ~Command() {}
            virtual void excute() = 0;
    }
    
    class Action1 : public Command
    {
        public:
            virtual void excute(){    action1();    }
    }
    
    class Action2 : public Command
    {
        public:
            virtual void excute(){    action2();    }
    }
    
    
    class InputHandler
    {
        public:
        void handleInput();
        
        //写一些方法来对下列指针和键位进行绑定……
        //基本方法是改变指针指向的对象(类型)
        
        private:
           Command* button_X;
           Command* button_Y;
           Command* button_Z;
    }
    
    void InputHandler::handleInput()
    {
        if(isPress_X)        button_X->excute();
        else if(isPress_Y)   button_Y->excute();
        else if(isPress_Z)   button_Z->excute();
    }
    
  • 关于输入配置的进一步探索:解放固定的被操作角色(修改上述的代码)
    lass Command
    {
        public:
            virtual ~Command() {}
            virtual void excute(GameActor& actor) = 0;
    }
    
    class Action1 : public Command
    {
        public:
            virtual void excute(GameActor& actor){    action1();    }
    }
    
    Command* InputHandler::handleInput()
    {
        if(isPress_X)        return button_X;
        else if(isPress_Y)   return button_Y;
        else if(isPress_Z)   return button_Z;
    }
    
    //游戏逻辑中
    Command* command = inputHandler.handleInput();
    if(command)
    {
        command->excute(actor);
    }
    
  • 上述的探索结果在游戏中人工智能中非常有意义。针对不同的角色执行不同的AI代码以达到不同的AI层级,这使得在游戏难度中具有更好的灵活度。
其他例子:撤销和重做
  • 这里不再贴出书中的解析代码,只给出整个代码利用命令模式的思想体现:
    • 撤销功能的要点在于:①记录前几步的对象属性②需要一个独立的选择对象来对其进行操作
    • 前面的探索中,我们独立出了对象:引用参数(actor) —— 这记录了我们需要撤销的对象
    • 新增部分:撤销时读取记录前一步的对象属性,用栈的数据结构特点可以很好的实现。

    关于这一部分详细代码可以看原书

总结
  • 命令模式极大程度上的避免了对灵活部分进行硬编码

  • 基本思想方法是间接法。正如在两个人的交流中,为了避免产生冲突,两者之间需要一个可以调和的人,进行处理。

  • 再一次给出联系图

  • 命令模式的应用重点在于上图中可替换对象的设计。

知识点全部源于《Game Programming Patterns》,中文名《游戏编程模式》,图片来源于网络

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值