您的位置:首页 > 编程语言

游戏编程模式:命令模式(Part II)

2016-07-05 23:31 344 查看

2、对Actors的指挥(Directions for Actors)

        我们刚刚定义的命令类适用于前面的例子,但是它们有很大的局限性。问题在于,它们假设了存在着jump()、fireGun()等等这样的隐含地知道如何找到玩家的角色并像操纵傀儡般操纵它的顶层函数。

        这种假定的耦合限制了这些命令的用途。JumpCommand可以使其跳跃的东西只有玩家。让我们来放宽这个限制。我们将我们想要对其发号施令的对象作为一个参数传递进去,而非调用一些靠自己去找到被命令的对象的函数:

class Command
{
public:
virtual ~Command() {}
virtual void execute(GameActor& actor) = 0;
};


       这里,GameActor是我们代表游戏世界中的一个角色的“游戏对象”类。我们把它传递进execute()中,以便继承的命令可以对我们自己选择的actor调用这些方法,就像这样:

class JumpCommand : public Command
{
public:
virtual void execute(GameActor& actor)
{
actor.jump();
}
};


        现在,我们可以用这个类来让游戏中的任何角色跳来跳去了。我们现在只缺少连接输入处理器和取得这个命令(就是前面的输入处理器——译者注)并在正确的对象上调用它的命令的一个环节。首先,我们修改handleInput()使得它返回命令:

Command* InputHandler::handleInput()
{
if (isPressed(BUTTON_X)) return buttonX_;
if (isPressed(BUTTON_Y)) return buttonY_;
if (isPressed(BUTTON_A)) return buttonA_;
if (isPressed(BUTTON_B)) return buttonB_;

// Nothing pressed, so do nothing.
return NULL;
}


        现在还不能立即执行命令,因为还不知道应该传递进哪个actor。这里就是我们利用了“命令是一个实体化的函数调用”这一优势的地方——我们可以延迟执行调用的时机。

        然后,我们需要代码来取得这个命令,然后在代表着玩家的actor身上运行它。比如像这样:

Command* command = inputHandler.handleInput();
if (command)
{
command->execute(actor);
}

        假设actor是指向玩家的角色的一个引用,那么上述代码就会基于用户的输入而正确地驱使他,所以我们就回到了上一个例子中的同样行为。但是在命令和执行该命令的actor之间增加一个间接层给了我们一个整洁的小功能(a neat little ability):我们现在可以通过改变我们对其执行命令的actor而让玩家控制游戏中的任何actor。

        在实践中,这不是一个常见的特性,但是有一个确实经常出现的相似的使用案例。到目前为止,我们仅仅考虑了玩家驱动的角色,但是游戏世界中所有那些其他的actor呢?这些是被游戏的AI所驱动的。我们可以使用同样的命令模式作为连接AI引擎和这些actor的接口;这些AI代码只不过是发送出(emit)Command对象罢了。

        在选择命令的AI和执行这些命令的actor代码之间的解耦给了我们很多的灵活性。我们可以为不同的actor使用不同的AI模块。或者,我们可以为不同种类的行为混合、匹配AI。想要一个更具有攻击性的对手吗?那就插入一个更加具有攻击性的AI来为其生成命令吧。事实上,我们甚至可以将AI运用于玩家的角色,而这在演示模式(其中游戏需要靠自动驾驶的方式来运行)等事情中会派上用场。

        通过把控制一个actor的命令变成第一类的对象,我们去掉了直接的方法调用这样的紧密耦合。相反,把它想象为命令的一个队列(queue)或者流(stream)吧:

    关于队列化(queueing)能够为你做什么的详细信息,参看事件队列(Event Queue)。



    为什么当初我感觉需要为你们画一个“流”的图呢?为什么它看起来像是一个管道呢?

        一些代码(输入处理器或者AI)产生命令并将其放入流中。另一些代码(发送者(dispatcher)或者actor本身)消费(consume)命令并调用之。通过将这个队列固定在中间,我们解耦了产生者和消费者,让它们位于两端。

        如果我们取得这些命令并使其可序列化(serializable),我们可以将它们组成的流发送到网络上。我们取得用户的输入、通过网络将其转到另外一台机器上、然后对其重演(replay)。这是制作一个基于网络的多人游戏的一个关键部分。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息