7

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.

2 Answers 2

6

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, that's what I need! Seems my research went in the completely wrong direction :)
2

I don't have time to give you any code, but keep in mind orc.health is the same as orc["health"]; that is, orc is a table and "health" is an element.

With that in mind, you can change the index and newindex metamethods of your table to have your own specific behavior. Store your "real" orc instance as some private metadata, then use it to update.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.