Accessing an object managed by the engine through a global variable

Started by
4 comments, last by GuyWithBeard 8 years, 4 months ago

Hey,

I recently started porting my engine over to AngelScript and I have a couple of questions. Scripts are added to game objects by attaching a script component to the object type, kind of how unity works. I want the script writers to be able to declare global variables freely without touching other scripts, so I am planning to use one module per script component. The contexts are pooled and there is only ever one script engine (or two, if I go with one engine per network node, of which there may be two per process).

Ideally I would like to expose global functions on a per-module basis, so I could call the "getParentName()" global function and it would give me the name of the game object the script component belongs to. However, as global functions seem to be registered on a per-engine basis I figured I would just expose the interface to the game object through a class that is only ever created and destroyed by the C++ code. So instead I could do something like "gParentGameObject.getName()" to get the name of the parent.

Seem good so far? Anything that seems crazy? If not let's get on with the questions:

I can expose the game object class with the flags set to "asOBJ_REF | asOBJ_NOCOUNT" and leave out the factory behavious registration, right? In the script, will I still pass around handles to the object or should I use references?

And how should I go about assigning the value of the "gParentGameObject" global variable? the assignment should happen right after the script component has been created as part of the game object instantiation. I have seen some examples using GetAddressOfGlobalVar() and casting to the correct type on the C++ side, but I am unsure of the exact syntax with objects registered with the NOCOUNT flag. The object is always guaranteed to be created before any script code for script component is run, and it is also guaranteed to outlive any script execution.

Thanks! And thank you four AngelScript. Hands down the nicest-to-use Application<->Script interface I have seen.

Advertisement

Well, I played around with the system for a while and I managed to get it working. Not sure if this is the right way to do it though.

I register the script component type like so:


scriptEngine->RegisterObjectType("ScriptComponent", 0, asOBJ_REF | asOBJ_NOCOUNT);

Before adding the code section that is the actual script code of the component I inject this one line to make a global handle to the object:


builder.AddSectionFromMemory("Global component variable", "ScriptComponent@ " SCRIPT_THIS_REFERENCE_NAME "; ");

Finally, after compiling the script code, but before executing it, I hook up the pointer to the C++ component instance with this ugly-ass double pointer hack:


asUINT index = mScriptModule->GetGlobalVarIndexByName(SCRIPT_THIS_REFERENCE_NAME);
ScriptComponent** pointer = (ScriptComponent**)mScriptModule->GetAddressOfGlobalVar(index);
*pointer = this;

That seems to work fine. Is this the correct way to register an object reference? Anything else I should know about?

What you've done is perfectly fine if you trust the script writers not to do something unexpected with the ScriptComponent reference. For example, if the script store a reference to the ScriptComponent in some other variable for use at a later time. That might end up causing crashes due to dangling pointers if the script tries to access the object after it has been destroyed in the game engine.

If you do not trust the script writers, e.g. if you plan to allow end users to write their own scripts, then you may want to use a little more secure way of managing your pointers, e.g. add proper reference counting in the objects to make sure they aren't destroyed while there are still active references to them, and probably also use weak-references to avoid keeping objects alive longer than necessary.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Allright, thanks for the warning. I do plan to make certain types reference counted. For the script component though, using its reference is of little use outside its own script.

What about setting the value of the global parameter through GetAddressOfGlobalVar() like I showed you above? Is that okay? And is the global variable still allowed to be a handle even though the object type is not garbage collected?


What about setting the value of the global parameter through GetAddressOfGlobalVar() like I showed you above? Is that okay? And is the global variable still allowed to be a handle even though the object type is not garbage collected?

Yes. This is the correct way of doing it.

asOBJ_NOCOUNT just means that the type doesn't rely on reference counting for controlling the lifetime of the objects, it doesn't mean that the types cannot be references through handles (which in this case behave pretty much the same way as ordinary pointers in C++).

Had the type been reference counted, you would still use GetAddressOfGlobalVar() to set the value, but you would have to take care to call Release on the previous object and AddRef on the new object when changing the address.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Awesome, thank you!

This topic is closed to new replies.

Advertisement