I am working on a game engine in C++ using Lua to define NPCs.
I can define a prototypic NPC like this:
orc =
{
name = "Generic Orc",
health = 100
}
function orc:onIdle()
print("Orc idles...")
end
and then spawn an instance of "Orc" with entitySpawn(orc)
. This is a C++ function that reads values like health and name from the given table, creates an Entity object in C++ with the given values and in addition creates a Lua table for the specific NPC.
Now, I would like to have a direct connection between the orc.health
variable in Lua and the mHealth
member variable of the corresponding Entity object in C++, so I could assign a value in Lua and instantly use it in C++ and vice versa.
Is this even possible? Or do I have to make use of setter / getter functions? I have taken a look at light userdata and got to the point of storing a pointer to the C++ variable in Lua, but could not assign a value.
This is possible. I'm assuming that entitySpawn
returns the table that C++ created for the entity. I'll also assume that you can expose a function from C++ that takes an entity's table and returns the current health, and similarly for setting. (You can use a light userdata pointer to the C++ object as a member of this table to implement this.)
So the ugly way would look like this:
local myOrc = entitySpawn(orc)
local curHealth = getEntityHealth(myOrc)
setEntityHealth(myOrc, curHealth + 10)
To make this prettier, we can have some fun with metatables. First, we'll put the accessors for all the properties we care about.
entityGetters = {
health = getEntityHealth,
-- ...
}
entitySetters = {
health = setEntityHealth,
-- ...
}
Then we'll create a metatable that uses these functions to handle property assignments.
entityMetatable = {}
function entityMetatable:__index(key)
local getter = entityGetters[key]
if getter then
return getter(self)
else
return nil
end
end
function entityMetable:__newindex(key, value)
local setter = entitySetters[key]
if setter then
return setter(self, value)
end
end
Now you need to make entitySpawn
assign the metatable. You would do this with the lua_setmetatable
function.
int entitySpawn(lua_State* L)
{
// ...
// assuming that the returned table is on top of the stack
lua_getglobal(L, "entityMetatable");
lua_setmetatable(L, -2);
return 1;
}
Now you can write it the nice way:
local myOrc = entitySpawn(orc)
myOrc.health = myOrc.health + 10
Note that this requires that the table that is returned by entitySpawn
does not have a health property set. If it does, then the metatable will never be consulted for that property.
You can create the entityGetters
, entitySetters
, and entityMetatable
tables, as well the __index
and __newindex
metamethods, in C++ instead of Lua if that feels cleaner to you, but the general idea is the same.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With