上篇中介绍了包装一个类来执行脚本文件,下面我们就要让我们的脚本更好的为我们工作,那就是使用函数交互调用。
1、c++中调用脚本函数;
首先需要新建一个.gm脚本文件,在脚本文件中编写一个函数代码,如下:
global sayHello = function()
{
print( "Hello, World!");
};
幸运的是GM提供了方便的函数绑定和函数调用工具类,我们需要添加gmsrc/src目录中的所有c++文件到工程中,并且将该目录添加头文件引用目录。使用gmCall进行脚本函数的调用皆可。
void TestB(gmMachine* pMachince)
{
gmCall call;
if(call.BeginGlobalFunction(pMachince, "sayHello"))
{
call.End();
}
}
从C++中调用脚本函数原始的方法就是使用gmFunctionObject类来进行,大致流程就是在gmMachine中索引到函数名称对应的函数指针,然后Push到gmThread中执行,更加详细的描述可以参考《Introduction to GameMonkey Script Part 2》http://www.gamedev.net/reference/programming/features/gmscript2/page6.asp
int main()
{
gmMachine gm;
// Execute a simple script file
gmLoadAndExecuteScript ( &gm, "scripts/function_test.gm" );
gmVariable funcName, funcFound;
funcName.SetString( gm.AllocStringObject("sayHello") );
// find in global table
funcFound = gm.GetGlobals()->Get(funcName);
if ( funcFound.m_type != GM_FUNCTION )
{
std::cout << "Failed to find 'sayHello'";
return 0;
}
// Get function object from variable
gmFunctionObject *func = (gmFunctionObject *)funcFound.m_value.m_ref;
gmThread *thread = gm.CreateThread();
// Push empty 'this' value
thread->Push( gmVariable::s_null );
thread->PushFunction( func );
// No params needed
// Get a blank variable to grab the return
gmVariable returnVar;
// execute the thread
int state = thread->PushStackFrame( 0 );
if (state != gmThread::KILLED )
{
state = thread->Sys_Execute( &returnVar );
}
return 0;
}
2、脚本中调用c++函数。
脚本调用c++函数稍复杂一点就是需要将预先提供GM_CDECL函数原型和实现的c++代码,然后使用gmFunctionEntry将函数注册到GM虚拟机中,才能在脚本中调用对应的c++版本函数。还是来看代码:
// C++ GM_CDECL 函数
int GM_CDECL gm_Add(gmThread* a_thread)
{
GM_CHECK_NUM_PARAMS(2);
GM_CHECK_INT_PARAM(x, 0);
GM_CHECK_INT_PARAM(y, 1);
a_thread->PushInt( x+y );
return GM_OK;
}
/**
* 注册C++函数绑定
*/
void TestRegisterScriptBindings(gmMachine* pMachince)
{
// 需要绑定的函数table
static gmFunctionEntry MathFunBinds[] =
{
/*gm
/lib Math
/brief Add
*/
{"Add", gm_Add},
};
// 注册函数到虚拟机中
GM_ASSERT(pMachince!=NULL);
pMachince->RegisterLibrary(MathFunBinds, sizeof(MathFunBinds) / sizeof(MathFunBinds[0]), "Math");
}
这样就成功的将一个c++函数注册到了GM的虚拟机中,其库名称为Math,当然可可以不填写库名称,库名称将在后面的介绍类的绑定和namespace的绑定中发挥不可替代的作用,不用再像LUA那样担心命名的污染。接下来我们需要在脚本中调用Math.Add函数啦。首先在.gm脚本中新建一个脚本函数,代码如下:
global myAdd = function( a_x, a_y )
{
res = Math.Add(a_x, a_y);
print("Add Result: ", res );
return res;
};
编写测试代码检测调用的结果,还是来看代码:
void TestA(gmMachine* pMachince)
{
// 调用脚本函数
gmCall call;
if(call.BeginGlobalFunction(pMachince, "myAdd"))
{
// 传递脚本函数参数
call.AddParamInt( 10 );
call.AddParamInt( 2 );
call.End();
// 处理返回值
int myReturn = 0;
if (call.GetReturnedInt( myReturn ) )
{
std::cout << "myAdd returned " << myReturn << std::endl;
}
else
{
std::cout << "myAdd returned an incorrect value" << std::endl;
}
}
}
输出结果:
Add Result: 12
myAdd returned 12
看起来还是有些复杂的,GM没有提供C++函数的自动绑定工具,也没有提供任何函数库的绑定,如果需要脚本函数调用C++ API或者其他函数库则需要费一番功夫,GameMonkey网站上提供将提供More bindings for useful tools and applications。但愿GM能早日有像LUAPlus一样强大的bind工具。
作者:吴紫鄂(zewu@live.cn)