Triggering Events with Lua- and C++-Objects

Started by
7 comments, last by Angelic Ice 7 years, 7 months ago

Hell forum!

Usually I separate graphics from physics: One goes into a file with the description on how to render the screen and the rest goes into the physic calculation component, where I do all the needed physic tests.

However, this time it feels like my game would benefit from a more tightly knit bond.

Therefore, I want objects that know how they look (a sprite-class as member) and who they surround (references to other objects).

Additionally they should have "scripteable" behaviour. This is where it gets a bit tricky for me:

The game field is being described in a Lua-file. All sprite-sheets and possible triggers together with their event reside in them, object by object.

Now... if I run through each described object and call a C++ function, I can create those above mentioned objects in the C++ part.

Nonetheless, how would I link them to their Lua equivalent?

Nearly every object shall have a trigger on mouse click and many will have triggers once the player touches a nearby referenced object (we can assume a tile-grid here).

But how would the Lua-objects push their triggers onto the C++ components?

I imagine something like this:

A Lua-object registers for a on-click event on its own C++ equivalent.

C++ will only bother evaluating mouse click coordinates to registered possible triggers. As a result, if only one object has a trigger and this one is set to itself, C++ will only compare the mouse click's coordinates to this object. If there are more, it will iterate through the list of registered objects.

Once the coordinates of a click and its C++ object hit box met, the checking function will return true to the trigger-handler-component.

But how would this find the Lua-object that once called it? How would it store the type of its trigger in order to tell the Lua-object it actually has been triggered?

If you have any recommendations, I would be really happy to hear of them : )

Also, thanks for taking your time to read through this topic. If something has been unclear, please inform me!

Advertisement

What library are you using to write your C++/Lua binding code, or are you rolling your own solution? It's hard to give good answers when we don't know what your binding code is capable of (can it bind classes, and can it recognize class inheritance?).

In my project we use Luabind, which is pretty damn powerful. If I were trying to register event handlers, I'd likely take one of two approaches.

1) Every event handler is a Lua function. The sprite object has as a member a pointer to the Lua function (possible in Luabind). The sprite class I'd write a "RegisterEventHandler(std::string function_name)" class, that takes the name of a function and sets the handler pointer. I'd call RegisterEventHandler() when my script is running its load/setup code, and if I wanted to change handlers mid-execution, just call the same function again with a different name.

2) Create a small class called "ScriptFunctionHandler" that is bound to Lua. The Lua script can create and initialize objects of this class, then pass them to the sprite objects, which keep a pointer to the handler object.

I haven't found a need to register handlers in my own game yet, but I do handle events and triggers on maps in a different way. We create several event classes in C++ which share a common base class: PathMoveSpriteEvent, PlaySoundEvent, ChangePropertySpriteEvent, and so on. All events have a Start() and Update() function. The former is run only once when the event begins. The latter is run every iteration of the game loop until it returns a true value, indicating that it has finished and the event should be terminated. Events can be linked to one another, meaning that I can start event A, and after it finishes it starts events B and C, and event C starts event D 500ms after it start. This way we can chain events together into an event sequence to create scenes, like where the camera follows a sprite walking to a door, playing a knocking sound, then having another sprite appear on the screen behind the door. It is a super flexible system and pretty easy to use. I have a little documentation written about it here, although its pretty basic and I plan to write a more detailed explanation about this system. Our code is open source and online so if you're interested in taking a look, I can link you to it.

Hero of Allacrost - A free, open-source 2D RPG in development.
Latest release June, 2015 - GameDev annoucement

Pretty open to every binding, not using one in particular at the moment.

1) Every event handler is a Lua function. The sprite object has as a member a pointer to the Lua function (possible in Luabind). The sprite class I'd write a "RegisterEventHandler(std::string function_name)" class, that takes the name of a function and sets the handler pointer. I'd call RegisterEventHandler() when my script is running its load/setup code, and if I wanted to change handlers mid-execution, just call the same function again with a different name.

This goes into a direction I like. But how would I progress from there?

It sounds like there is an event-checker-class, that would iterate over all my objects and call the Lua function via the pointer and that Lua function would run its if-requirement.

But what is about the Lua objects? Can Luabind refer to object's classes? Or can Luabind actually give the sprite class a pointer to the right object as well?

Another thing, this would actually cause Lua to run every single frame as long as some even-trigger has been set, right?

Isn't there a way to use Lua only when its required to run?

If Luabind supports sending single Lua-objects pointers to C++, couldn't the Lua-objects listen to certain happenings in the C++ engine?

Let's say, a Lua-objects says, it needs information about incoming mouse clicks that actually hit a game object. Whenever a mouse click happens, C++ will contact all Lua-objects that registered themselves for this event, giving them the information of which object the has been hit.

Though, how would my Lua-object know what C++-object it should edit?

Yes, Luabind can handle the passing around of pointers to class instances as function arguments or return values. If you only want Lua to run when there is a trigger, you have two options. 1) Do your checking of whether you need to call the event handler on the C++ side (for example, if you're looking for a mouse click event, figure out if any sprites were clicked, and if so then you call their event handler). 2) Don't register any event handler until its ready to be called, and remove the event handler when you've determined that you don't need to call it anymore.. The advantage of (1) is that its easier to share trigger checking code among classes, but at the cost of flexibility. If you did your trigger checking in Lua, you could easily customize different sprite objects to handle things in different ways.

You shouldn't worry too much about calling into Lua every update loop though. I mean, eventually you're probably going to be doing so anyway. For our maps we are always guaranteed to call into Lua functions at least twice per game loop: one to update the map state, and the other to handle any custom drawing code. Function calls into Lua (and vice versa) are not that much more expensive than just a standard call to a C++ function.

Yes, Lua code can totally listen to events happening from C++. We handle this in our Update() functions to tell if something has happened such as a sprite entered a zone that triggers an event sequence. You're thinking incorrectly about this incorrectly with your question "how would the Lua object know which C++ object to use?". There is only one object, and it exists both in C++ and Lua (unless you explicitly make two different objects representing the same thing, which I wouldn't know why you would do that). For example, all sprite objects in my game are constructed in Lua. We store all sprite objects we create in a table in Lua called "sprites" and use a unique string identifier as the table key for each sprite (ex: "npc_villager02"). At the same time we create the object in Lua, we also make a call to a C++ function with a pointer to the sprite object so that the C++ code can know which sprites exist on the map, and do update/draw calls to them as needed. So the same sprite object is created in memory, and two pointers to it exist: one in C++ and one in Lua.

I strongly suggest you read through the Luabind documentation so you can get a better understanding of how this is all possible, and how this library can be used to make your data and functions available in Lua. I personally really like the library, although it no longer is actively maintained. It is well documented and solid as a rock, and we've had very few issues with it after setting it all up in our build process.

Hero of Allacrost - A free, open-source 2D RPG in development.
Latest release June, 2015 - GameDev annoucement

Thanks a lot! Yeah, the contra is something that was lurking in the corners for me as well, losing the flexibility.

Just afraid of running Lua every cycle as that is what they are all preaching to avoid. Maybe it is more about avoiding heavy calculations.

I just found out that Luabind needs Boost, is there an alternative binding sufficing the things needed?

Pointers of Lua-Objects to C++ and then calling their functions?

One really important thing is, that I can compile it on Linux, Windows and Android.

IIRC Luabind only needs boost in order to compile. If you have a pre-built library, you don't need to worry about boost. There are of course other Lua binding solutions (use google to find them, I can't remember them all). I think most (all?) of them are less capable in their binding than Luabind is, but the last time we compared Lua/C++ binding solutions was many years ago, so I don't know what's available now.

Hero of Allacrost - A free, open-source 2D RPG in development.
Latest release June, 2015 - GameDev annoucement

Thanks, I just went through all interesting bindings.

Nonetheless, I realised, that all these bindings usually present the idea of referencing C++ objects to Lua, but not the other way around.

Is it common practise to actually put the C++ objects into the Lua objects? I thought that this might be dangerous, Lua might hold references to dead C++ objects, this sounds like ugly work to manage.

Nonetheless, I guess this refers to the idea of running through a list of triggers in some Lua-table, checking whether they would be triggered by accessing the referenced C++ object and directly comparing the variables of this C++ object with the requirements in the Lua trigger.

Is this right?

Yes, representing C++ objects in Lua is much more common than the other way around. Typically, all your engine and core functionality you want to be built in a faster and more structured language like C++ and have those entities available to be used anywhere. It's not dangerous as long as you are careful about which side "owns" the object and makes sure to destroy it when they're finished. We haven't had any problems with Lua referencing dead C++ objects, because Lua is the one that creates those objects and they are only destroyed when that map/battle/whatever is finished.

I don't quite follow your last paragraph. Our triggering checking is done entirely in Lua. If a trigger condition is met, we write Lua code to enact the change, whether its starting a sequence of events or changing something about the map state. It's pretty straightforward.

Hero of Allacrost - A free, open-source 2D RPG in development.
Latest release June, 2015 - GameDev annoucement

I decided to go with Sol 2 as binding, as it seems to cover all the necessities I have.

However, I'm not entirely sure how to implement everything yet.

My current "sprite"-class, or let's say tile-class, owns following:

A SFML vertex array (these might be organised differently at some point of development)

A hash key for the texture factory

Multiple height/width values

Now, I could define a user-type in Sol for my tile-class. However, how would I insert the customised functions to it?

Here is a reference to user-types: http://sol2.readthedocs.io/en/latest/api/usertype.html

Let's say: Check_Triggers() or an Update() could be


if (player.y_position == 2)
{
gameover()
}

or


if (player.y_position == 4)
{
player.lower_health(30)
}

and even complete different things.

But if I would do this all via C++ objects that get translated into user-types... I cannot think of a way doing these.

Also, how would I tell C++ where a list of the Lua objects are? Insert them all in a table and passing this as a reference back to C++, I assume?

In Lua, I could just simply create one file per typical object that I will use.

One for enemy of type a and one for type b and c...

But how would I let Lua iterate through their update function?

I mean, I could let the editor hard-code calling the object's update/trigger function. It would probably be a wiser decision to just store them all in a table and for-loop over them. Lua should not care too much about the different classes as this is a not statically typed, right? As long as they have the same function that I want to call.

Only problem would be the SFML vertex array and the lack of being a drawable SFML class. Luckily, I planned on moving the vertex arrays to another place of code already, anyway.

In the end, I might go way too complicated with this. Maybe someone could push their ideas to me.

Thanks for reading : )

This topic is closed to new replies.

Advertisement