免责声明:我从未在任何“真实”项目中使用过 Angelscript,这就是为什么下面的答案应该持保留态度。它在我为它设置的小测试代码中工作,并且大多数代码片段在官方手册中都有一些详细的解释,但这绝对不能保证它是一个合理的设计并且可以在你的游戏中合理地使用。
我相信,实现目标的相关部分在官方手册中得到了很好的描述:
使用这些信息,我们可能会编写一个简单的包装类,它在构造时创建某个类的实例并在销毁时释放它,同时提供调用脚本类的各个成员的成员函数:
(注:为简洁起见,我省略了所有错误处理和其他一些重要实践,如树规则(下面的类不能复制而不破坏很多......))
标题:
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 对象的指针,该对象是实现所需接口的某种任意类型(目前强制执行的唯一方法是在出现问题时崩溃,这有点糟糕)。
- 在构造时,创建(从传入的引擎、上下文和名称)并获取指向其成员函数的指针,在销毁时释放它。
- 每当调用一个成员时,它都会转发给 Angelscript 对象的相应成员。
正如一开始提到的,我对 Angelscript 没有任何经验,所以这可能确实是一种非常次优的方法,而且很可能确实存在更好的解决方案。