自己动手写插件框架(13)

现在,让我们来把前面章节中缺失的部分补上。在我们这部分内容的最后,我们会得到一个完整的代码。

怪物插件

基于前面我们介绍的插件框架,我故意创建了四种不同类型的插件:

  • 纯 C++ 插件
  • 纯 C 插件
  • 部署为动态/共享库的混合插件
  • 应当直接链接到可执行文件中的 C++ 静态插件

所有这些插件都会将其怪物对象作为 actor 注册到PluginManager。另外,游戏本身提供了Hero 类,作为实现了IActor 接口的对象。由游戏控制的Hero 类和其他怪物对象的绝大多数IActor 接口的代码都是一致的。游戏也会提供一些内建的怪物。

动态 C++ 插件

动态 C++ 插件注册了KillerBunny 和 StationarySatan 两种怪物。下面的代码是 KillerBunny.h 头文件。KillerBunny 直接实现了 C++IActor 接口(这使得它成为一个纯 C++ 插件)。它实现了create() 和 destroy()static 函数,用于PluginManager创建和销毁对象。StationarySatan 以及其他纯 C++ 插件对象基本类似(除了那些 private 数据)。

#ifndef KILLER_BUNNY_H
#define KILLER_BUNNY_H

#include <object_model/object_model.h>

struct PF_ObjectParams;

class KillerBunny : public IActor
{
public:
    // static plugin interface
    static void * create(PF_ObjectParams *);
    static apr_int32_t destroy(void *);
    ~KillerBunny();

    // IActor methods
    virtual void getInitialInfo(ActorInfo * info);
    virtual void play(ITurn * turnInfo);

private:
    KillerBunny();
};

#endif

下面的代码包含了create() 和 destroy()的实现。这些都很简单。create() 函数创建了一个新的KillerBunny 对象并将其返回(以void *的形式)。destroy() 函数需要一个void *作为参数,该指针必须是 create() 函数返回的。它会将void * 转换成KillerBunny * 然后 delete 掉。这里最重要的部分是,这些函数允许PluginManager不需要“知道”任何关于 KillerBunny 类的信息,就可以创建KillerBunny 对象。返回的实例则可以通过IActor 接口使用(即使它返回的是一个void *)。

void * KillerBunny::create(PF_ObjectParams *)
{
    return new KillerBunny();
}

apr_int32_t KillerBunny::destroy(void * p)
{
    if (!p)
        return -1;
    delete (KillerBunny *)p;
    return 0;
}

下面一段代码包含IActor 接口的实现。这些函数也很简单。getInitialInfo() 函数用于使用一些数据组装ActorInfo 结构。play() 函数才是KillerBunny 实际提供的功能:移动、攻击或回避敌人等。这里,它仅仅从ITurn 接口获取到友军的列表。这只是因为我们不需要提供一个完整的实现,事实上,我们的这些怪物什么都做不了。只有 Hero 才能真正的攻击。在怪物受到攻击时,它们需要保护自己并反击。

void KillerBunny::getInitialInfo(ActorInfo * info)
{
    ::strcpy((char *)info->name, "KillerBunny");
    info->attack = 10;
    info->damage = 3;
    info->defense = 8;
    info->health = 20;
    info->movement = 2;

    // Irrelevant. Will be assigned by system later
    info->id = 0;
    info->location_x = 0;
    info->location_y = 0;
}

void KillerBunny::play(ITurn * turnInfo)
{
    IActorInfoIterator * friends = turnInfo->getFriends();
}

我们写这些代码的目的是演示,编写纯 C++ 插件是多么容易。除了公式化的create() 和 destroy()static 函数,你只需要实现一个标准 C++ 类,没有任何神秘的代码需要添加。

下面是插件初始化代码。这些代码并不复杂,但是很繁琐并且容易出错:在PF_initPlugin 函数中定义一个退出函数以及一个PF_RegisterParams 结构体,然后将它们组装到一起,并且要注册所有插件对象。确保在初始化失败时返回 NULL。除此之外就没有别的什么了。这就是编写纯 C++ 怪物插件所需要的(在下面的代码中,我们注册了两个怪物)。

#include "cpp_plugin.h"
#include "plugin_framework/plugin.h"
#include "KillerBunny.h"
#include "StationarySatan.h"

extern "C" PLUGIN_API apr_int32_t ExitFunc()
{
    return 0;
}

extern "C" PLUGIN_API PF_ExitFunc PF_initPlugin(const PF_PlatformServices * params)
{
    int res = 0;

    PF_RegisterParams rp;
    rp.version.major = 1;
    rp.version.minor = 0;
    rp.programmingLanguage = PF_ProgrammingLanguage_CPP;

    // Register KillerBunny
    rp.createFunc = KillerBunny::create;
    rp.destroyFunc = KillerBunny::destroy;
    res = params->registerObject((const apr_byte_t *)"KillerBunny", &rp);
    if (res < 0)
        return NULL;

    // Regiater StationarySatan
    rp.createFunc = StationarySatan::create;
    rp.destroyFunc = StationarySatan::destroy;
    res = params->registerObject((const apr_byte_t *)"StationarySatan", &rp);
    if (res < 0)
        return NULL;

    return ExitFunc;
}

Leave a Reply