Thanks SiCrane, I don't think I could have given a better answer myself
I'm not quite sure either what exactly you wish to do in your second question. But I guess it has to do with implementing callbacks in the scripts, that can then be invoked from the application upon certain events.
I'll give you a little snippet on how I implemented this myself in the gui for my game engine:
My gui consists basically of two entities, the gui manager, and the gui elements. The gui manager is pretty self explanatory so I won't give much detail on it. The gui elements on the other hand is a generic entity that the script can use to implement buttons, windows, text labels, or just groups of other elements with. In order to implement a button for example, the script will implement some event handlers that they then attach to the gui element. Examples of the event handlers are onClick, onMouseOver, and onTimer.
Here's how the gui entities are registered with the script in my engine:
[source]
// Register the gui element type
r = engine->RegisterObjectType("CGuiElement", 0, asOBJ_REF); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("CGuiElement", asBEHAVE_ADDREF, "void f()", asMETHOD(CGuiElement, AddRef), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("CGuiElement", asBEHAVE_RELEASE, "void f()", asMETHOD(CGuiElement, Release), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "string text", offsetof(CGuiElement, text)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "vector2 center", offsetof(CGuiElement, center)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "int srcImageIndex", offsetof(CGuiElement, srcImageIndex)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "vector2 srcBottomLeft", offsetof(CGuiElement, srcBottomLeft)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "vector2 srcTopRight", offsetof(CGuiElement, srcTopRight)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "float rotation", offsetof(CGuiElement, rotation)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "int zPosition", offsetof(CGuiElement, zPosition)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "vector2 scale", offsetof(CGuiElement, scale)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "int fontIndex", offsetof(CGuiElement, fontIndex)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "uint align", offsetof(CGuiElement, align)); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGuiElement", "void SetTimer(float, int)", asMETHOD(CGuiElement, SetTimer), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGuiElement", "void KillTimer(int)", asMETHOD(CGuiElement, KillTimer), asCALL_THISCALL); assert( r >= 0 );
// Register the event handler
r = engine->RegisterFuncdef("void OnClickHandler(CGuiElement @)"); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "OnClickHandler @onClick", offsetof(CGuiElement, onClickHandler)); assert( r >= 0 );
r = engine->RegisterFuncdef("void OnMouseOverHandler(bool, CGuiElement @)"); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "OnMouseOverHandler @onMouseOver", offsetof(CGuiElement, onMouseOverHandler)); assert( r >= 0 );
r = engine->RegisterFuncdef("void OnTimerHandler(int, CGuiElement @)"); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "OnTimerHandler @onTimer", offsetof(CGuiElement, onTimerHandler)); assert( r >= 0 );
// Register the gui manager as a singleton
r = engine->RegisterObjectType("CGui", 0, asOBJ_REF | asOBJ_NOHANDLE); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGui", "int LoadScript(const string &in)", asMETHOD(CGuiMgr, LoadScript), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGui", "int LoadImage(const string &in)", asMETHOD(CGuiMgr, LoadImage), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGui", "int LoadFont(const string &in)", asMETHOD(CGuiMgr, LoadFont), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGui", "CGuiElement @CreateElement(const vector2 &in, const vector2 &in, CGuiElement @+ parent = null)", asMETHOD(CGuiMgr, CreateElement), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGui", "void SetMouseElement(CGuiElement @+)", asMETHOD(CGuiMgr, SetMouseElement), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterGlobalProperty("CGui gui", app->guiMgr); assert( r >= 0 );
[/source]
And this is how the application invokes an event handler, in this case the onMouseOver
[source]
void CScriptMgr::CallGuiOnMouseOverHandler(asIScriptFunction *onMouseOverHandler, bool isMouseOver, CGuiElement *el)
{
int r;
asIScriptContext *ctx = PrepareContext(onMouseOverHandler->GetId());
if( ctx )
{
r = ctx->SetArgByte(0, isMouseOver); assert( r >= 0 );
r = ctx->SetArgObject(1, el); assert( r >= 0 );
ExecuteCall(ctx);
ReturnContextToPool(ctx);
}
}
asIScriptContext *CScriptMgr::PrepareContext(int funcId)
{
asIScriptContext *ctx = 0;
if( contextPool.size() )
{
ctx = *contextPool.rbegin();
contextPool.pop_back();
}
else
ctx = engine->CreateContext();
// TODO: Error handling
int r = ctx->Prepare(funcId); assert( r >= 0 );
return ctx;
}
void CScriptMgr::ReturnContextToPool(asIScriptContext *ctx)
{
contextPool.push_back(ctx);
// Unprepare the context as we don't know when it will be used again
ctx->Unprepare();
}
int CScriptMgr::ExecuteCall(asIScriptContext *ctx)
{
int r = ctx->Execute();
if( r != asEXECUTION_FINISHED )
{
LOG(("Script failed to execute (%d)", r));
if( r == asEXECUTION_EXCEPTION )
{
LOG(("Exception: %s", ctx->GetExceptionString()));
LOG(("Function: %s", engine->GetFunctionDescriptorById(ctx->GetExceptionFunction())->GetDeclaration()));
LOG(("Line: %d", ctx->GetExceptionLineNumber()));
char buf[256];
sprintf_s(buf, 256, "Exception: %s", ctx->GetExceptionString());
app->console->WriteLine(buf);
sprintf_s(buf, 256, "Function: %s", engine->GetFunctionDescriptorById(ctx->GetExceptionFunction())->GetDeclaration());
app->console->WriteLine(buf);
sprintf_s(buf, 256, "Line: %d", ctx->GetExceptionLineNumber());
app->console->WriteLine(buf);
}
}
return r;
}
[/source]
And finally, here's what a script might look like:
[source]
#include "common.as"
CGuiElement @mouse;
// This handler is called whenever the menu should be shown
void onShowMenu()
{
// Set up the gui elements for the start menu
CreateMouse();
// Create background image
CGuiElement @background = gui.CreateElement(vector2(0,0), vector2(1024,768), null);
background.srcImageIndex = gui.LoadImage("background.png");
// Create the menu options
if( game.isGameOn )
CreateButton(512,480,200,50, 'Resume game', onResume);
CreateButton(512,410,200,50, 'Start new game', onStart);
CreateButton(512,340,200,50, 'Quit game', onQuit);
}
void onMouseOver(bool isMouseOver, CGuiElement @bt)
{
if( isMouseOver )
{
bt.KillTimer(0);
bt.SetTimer(0, 1);
}
else
{
bt.KillTimer(1);
bt.SetTimer(0, 0);
}
}
void onStart(CGuiElement @)
{
game.StartNewGame();
}
void onQuit(CGuiElement @)
{
game.QuitGame();
}
void onResume(CGuiElement @)
{
game.ResumeGame();
}
void onTimer(int id, CGuiElement @el)
{
if( id == 1 && el.scale.x < 1.1 )
{
el.scale += vector2(0.05, 0.05);
el.SetTimer(0.01, 1);
if( el.scale.x > 1.1 )
el.scale = vector2(1.1, 1.1);
}
else if( id == 0 && el.scale.x > 1.0 )
{
el.scale -= vector2(0.025, 0.025);
el.SetTimer(0.01, 0);
if( el.scale.x < 1.0 )
el.scale = vector2(1.0, 1.0);
}
}
void CreateMouse()
{
@mouse = @gui.CreateElement(vector2(32,32), vector2(32,32));
mouse.center = vector2(16,24);
gui.SetMouseElement(mouse);
mouse.srcImageIndex = gui.LoadImage("arrow.png");
mouse.zPosition = -1000; // Make sure the cursor is in front of all other elements
}
CGuiElement @CreateButton(float x, float y, float w, float h, const string &in text, OnClickHandler @handler)
{
CGuiElement @bt = gui.CreateElement(vector2(x,y), vector2(w,h), null);
bt.fontIndex = gui.LoadFont("menu.fnt");
bt.text = text;
@bt.onClick = handler;
@bt.onMouseOver = onMouseOver;
@bt.onTimer = onTimer;
return @bt;
}
[/source]
I know it might be a bit difficult to understand snippets like this, so don't hesitate to ask whatever you don't understand.
I hope it will give you some ideas on how to implement your own event handlers.
Regards,
Andreas