Наследование Angelscript от класса C++

Я почесал голову над этим в течение нескольких дней, подумал, что у вас, ребята, может быть есть идея получше.

По сути, я хочу определить стандартный интерфейс, который затем может быть унаследован в классах Angelscript. Например, скажем, у меня есть карточная игра, такая как Magic The Gathering, мой базовый класс может выглядеть так:

class Card
{
public:
    virtual void PreDrawPhase() = 0;
    virtual void PostDrawPhase() = 0;
    // etc....
};

Затем я хочу иметь возможность определять новые карты и их соответствующее поведение в Angelscript, в то же время имея возможность обрабатывать их в C++ (путем обработки интерфейса). Как мне этого добиться?

1 ответ

Отказ от ответственности: я никогда не использовал Angelscript ни в одном "реальном" проекте, поэтому следующий ответ должен быть взят с крошкой соли. Он работал в небольшом тестовом коде, который я настроил для него, и большинство фрагментов кода были подробно объяснены в официальном руководстве, но это абсолютно не гарантирует, что это продуманный дизайн и его можно разумно использовать в вашей игре.

Полагаю, что соответствующие части для достижения вашей цели довольно хорошо описаны в официальном руководстве:

  • Здесь описывается, как определить и использовать интерфейс.

  • Здесь описывается, как создавать и использовать "классы сценариев" в C++.

Используя эту информацию, мы могли бы потенциально написать упрощенный класс-обертку, который при создании создает экземпляр определенного класса и освобождает его при уничтожении, одновременно предоставляя функции-члены, которые вызывают соответствующие члены класса сценария:

(Примечание: для краткости я пропустил всю обработку ошибок и некоторые другие важные приемы, такие как правило дерева (класс, приведенный ниже, не может быть скопирован, не нарушая много...))

Заголовок:

class Card
{
    public:
    Card(asIScriptEngine *engine, asIScriptContext *ctx, const std::string &module_name, const std::string &class_name);
    ~Card();

    void PreDrawPhase();
    void PostDrawPhase();

    private:
    asIScriptContext *ctx;
    asIScriptObject *obj;
    asIScriptFunction *PreDrawPhaseFunc, *PostDrawPhaseFunc;
};

Реализация:

Card::Card(asIScriptEngine *engine, asIScriptContext *ctx, const std::string &module_name, const std::string &class_name):
    ctx(ctx)
{
    asIScriptModule *module = engine->GetModule(module_name.c_str());
    auto type_id=module->GetTypeIdByDecl(class_name.c_str());
    asIObjectType *type = engine->GetObjectTypeById(type_id);

    PreDrawPhaseFunc=type->GetMethodByDecl("void PreDrawPhase()");
    PostDrawPhaseFunc=type->GetMethodByDecl("void PostDrawPhase()");

    asIScriptFunction *factory = type->GetFactoryByDecl((class_name+" @"+class_name+"()").c_str());
    ctx->Prepare(factory);
    ctx->Execute();
    obj=*(asIScriptObject**)ctx->GetAddressOfReturnValue();
    obj->AddRef();
}

Card::~Card()
{
    obj->Release();
}

void Card::PreDrawPhase()
{
    ctx->Prepare(PreDrawPhaseFunc);
    ctx->SetObject(obj);
    ctx->Execute();
}                                                                                                                    

void Card::PostDrawPhase()
{                                                                                                                    
    ctx->Prepare(PostDrawPhaseFunc);
    ctx->SetObject(obj);
    ctx->Execute();
}

Я считаю, что код довольно понятен (поправьте меня, если я ошибаюсь, и я постараюсь уточнить), поэтому я просто опишу основную идею:

  • Каждый экземпляр Card содержит указатель на некоторый объект Angelscript, который имеет некоторый произвольный тип, реализующий желаемый интерфейс (единственный способ, которым это применяется в настоящее время, - сбой, если что-то пошло не так, что отчасти плохо).
  • По построению это создается (из переданных в engine, context и names) и указатели на его функции-члены накапливаются, при уничтожении оно освобождается.
  • Каждый раз, когда вызывается член, он пересылается соответствующему члену объекта Angelscript.

Как уже упоминалось в начале, я не очень разбираюсь в Angelscript, так что это действительно может быть очень неоптимальный метод, и вполне вероятно, что существуют лучшие решения.

Другие вопросы по тегам