游戏编程模式-命令模式

本文介绍了如何运用命令模式来解耦游戏控制逻辑,通过创建不同的命令对象来执行游戏中的动作,如角色的跳跃、移动等。此外,还探讨了如何实现撤销与重做功能,通过维护一个命令列表来支持这些操作。命令模式使得代码更加灵活,便于调整按键映射和角色行为,同时也易于扩展以支持更多游戏行为。
摘要由CSDN通过智能技术生成

命令模式

  • 命令(command) 将请求封装为对象,从而使用不同的请求将客户端参数化,同时支持请求撤销与恢复。
  • 在这里插入图片描述
#include <iostream>

using namespace std;

class Receiver;

class Inveoker;

class BaseCommand;

/*接收者 执行者 */
class Receiver
{
public:
    void CommandA()
    {
        cout << "CommandA" << endl;
    }

    void CommandB()
    {
        cout << "CommandB" << endl;
    }
};


/*命令 */
class BaseCommand
{
protected:
    Receiver *receiver;
public:
    BaseCommand(Receiver *rec)
    {

        receiver = rec;

    }

    virtual void Action() = 0;

};

/*发放 */
class Invoker
{
private:
    BaseCommand *command;
public:

    void SetCommand(BaseCommand *command1)
    {
        command = command1;
    }

    void Invoke()
    {
        command->Action();
    }
};


class CommandA : public BaseCommand
{
public:
    CommandA(Receiver *re) : BaseCommand(re) {};

    void Action() override
    {
        receiver->CommandA();
    }
};

class CommandB : public BaseCommand
{
public:
    CommandB(Receiver *re) : BaseCommand(re) {};

    void Action() override
    {
        receiver->CommandB();
    }
};

int main()
{
    Receiver *re = new Receiver();
    CommandA *a = new CommandA(re);
    CommandB *b = new CommandB(re);
    Invoker *invo = new Invoker();
    invo->SetCommand(a);
    invo->Invoke();
    invo->SetCommand(b);
    invo->Invoke();

}

在这里插入图片描述

按键映射

  • 读取用户一系列输入,转换为游戏中的Action
class BaseRole
{
public:
    void Jump()
    {
        cout << "Jump" << endl;
    }

    void MoveL()
    {
        cout << "MoveL" << endl;
    }

    void MoveR()
    {
        cout << "MoveR" << endl;
    }

    void Down()
    {
        cout << "Down" << endl;
    }

};

class Command
{
protected:
    BaseRole *role;
public:
    Command(BaseRole *role1)
    {

        role = role1;
    }

    virtual void Action() = 0;


};

class JumpCommand : public Command
{
    void Action() override
    {
        role->Jump();
    }
};

class DownCommand : public Command
{
    void Action() override
    {
        role->Down();
    }
};

class MoveLCommand : public Command
{
    void Action() override
    {
        role->MoveL();
    }
};

class MoveRCommand : public Command
{
    void Action() override
    {
        role->MoveR();
    }
};

class InputHandle
{
private:
    Command *w;
    Command *s;
    Command *a;
    Command *d;
public:
    void Input()
    {
        char i;
        cin >> i;

        if (i == 'w')
            w->Action();
        else if (i == 's')
            s->Action();
        else if (i == 'a')
            a->Action();
        else if (i == 'd')
            d->Action();

    }
};

此时我们成功增加了一个间接调用函数层,修改按键映射时 修改指针指向即可
但是 此时调用 Input前 需要对命令进行绑定 稍显麻烦了些 修改如下

class Command
{
public:
    virtual void Action( Baserole *role) = 0;
};
/*对InputHandle 如下*/
class InputHandle
{
private:
    Command *w;
    Command *s;
    Command *a;
    Command *d;
public:
    Command* Input()
    {
        char i;
        cin >> i;

        if (i == 'w')
            return w;
        else if (i == 's')
            return s;
        else if (i == 'a')
            return a;
        else if (i == 'd')
            return d;
        
        return nullptr;
    }
};

/*使用如下*/
Command* command=InputHandle.Input();
if(Command)
{
	command->Action(role);
}

如果role是玩家操控的对象 基于玩家的输入执行相应role的命令
但是我们可以将 InputHandle替换为 NPCHandle 照搬上面的命令模式 通过相应逻辑返回对应每次的命令
通过命令模式我们摆脱了直接调用函数这种解耦合 我们的代码将更加灵活
在这里插入图片描述
我们可以通过替换role 对不同角色进行操作 也可以替换输入对同一角色定义不同状态的操作

撤销与重做

撤销与重做常见于策略游戏,例如在方格地图中对对象作为移动行为,需要支持撤销操作,与重新执行命令重做。
比如在方格地图中进行上下左右四个方位的移动
撤销与重做需要命令流对其支持,或者说我们需要维护一个命令列表与当前命令的一个指针或者引用。
例如使用顺序表作为命令流 执行撤销操作时我们将指针向后移动,回到上一个命令,当执行重做操作时,我们将指针向前移动,重新执行。当执行了一个新命令时,将当前命令前面的命令全部删除。
对于支持撤销的命令来讲我们需要保留执行前的状态,以及撤销函数回到执行前状态

class UnitCommand
{
    /*执行 */
    virtual void Action() = 0;
    /*撤销*/
    virtual void UnDo() = 0;
};

class MoveCommand : public UnitCommand
{
private:
    BaseRole *object;
    int MapX;
    int MapY;
    int BeforeX;
    int BeforeY;
public:
    MoveCommand(BaseRole *role, int x, int y) : object(role), MapX(x), MapY(y), BeforeX(role->X), BeforeY(role->Y)
    {

    }

    void Action() override
    {
        object->MoveTo(MapX, MapY);
    }
    void UnDo() override
    {
        object->MoveTo(BeforeY,BeforeY);
    }
};

对于备忘录模式来讲并不有效,命令试图去修改对象状态的一小部分,而创建对象其他数据浪费内存,手动存储被修改的部分就节省的很多
我们也可以使用持久化数据,我们每次修改对象返回一个新的对象,保留原对象不变,新对象与原对象共享数据,比拷贝整个对象的代价小的多

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值