Scripting C++ object instances with Lua

Started by
2 comments, last by Maxamor 14 years, 7 months ago
I am new to using Lua and have never implemented scripting before. I recently decided to use a data-driven approach for creating my game objects for the 2D platformer I'm working on. Rather than writing a class for each of my possible game objects, my plan is to let scripting take care of most of the differences in behavior, animation, etc., using Luabind. This means I would have only a handful of C++ classes that are specialized via a script. The problem I'm having specifically is how each instance of a game object maintains its state within Lua. How does I write a script, say "Zombie.lua", that all instances of a "Zombie" enemy can use? Example:
-- Zombie.lua
function update()
  -- Do some Zombie-like updating here...
end

-- Bear.lua
function update()
  -- Do some Bear-like updating here...
end
Since there isn't a Zombie class, but only an Enemy class, how do I make the functions in my Lua script reference the current instance? From the C++ perspective, how do I call the "Zombie"-specific methods? I've been having a hard time finding game-specific examples of binding C++ object instances to Lua online. I did find Chapter 6 of "Programming Game AI by Example" a good read but it seems to indicate that on the Lua side of things I would need to make unique functions for each type of game object... Where can I go for guidance on this subject?
Advertisement
Sorry, I can't remember the exact syntax for this (i did it a while ago), but...

You don't want to think of it as "here is the .lua file for zombies" or "zombies will run this lua file". (The same way that in C++ we don't just make zombie.cpp with "void update()" and say zombies run this source file). We have to make a zombie class (not file), with a virtual update function.


See the part of luabind docs on deriving from your C++ classes in LUA. Then inside lua you can define a class that derives from a C++ class. When you call the virtual Update function in C++, your Lua function will be called instead.

Quote:Since there isn't a Zombie class, but only an Enemy class, how do I make the functions in my Lua script reference the current instance? From the C++ perspective, how do I call the "Zombie"-specific methods?
Enemy will be your C++ class that you do some LuaBind magic with.
Then in a Lua script, you can define the Zombie class (which extends the C++ class).
In my opinion this is as much an architecture thing as it is implementation.

You can use an ID system to refer to a specific entity or thing in C++.

You can also expose a set of functions that can act on a specific thing given an ID.

Different "things" can have different ID Pools and functions that act on them.

This is ideal because it limits your interaction to lua to just ints, minimizing the lua to C++ interaction.

In my case the only thing my C++ engine does is graphics and movement related. everything else is game logic and is done in lua.

EDIT: to be more detailed (maybe less clear but i'll try for it to make sense haha), each type of enemy can have a table that describes its default attributes.... and then everytime you create a zombie in lua, you create a table and assign its defaults to the default zombie table, and then set that zombies current hp to defaults.MaxHP etc. This can be done in a Creation function that gets called from the defaults table.

------------------------------

redwoodpixel.com

Hodgman makes a good point about how I was thinking the scripts would be used. The method he describes is what I want to do, I just couldn't wrap my head around making it work.

Speaking of wrapping, I threw together a small sample of what I'm trying to do. Unfortunately I ran into some problems accessing properties in Lua from my wrapped object.

Below is the entire source, it's fairly short. An access violation is produced when trying to access the "name" property from Lua. I have a feeling my derived class' constructor is not calling the Enemy constructor correctly which could have ramifications for the rest of my code.

main.cpp
#include <string>#include <iostream>#include <lua.hpp>#include <luabind/luabind.hpp>class Enemy {private:  std::string name;  int id;public:  Enemy(const std::string& n, int id)     : name(n)    , id(id) {   }  const std::string& getName() const {     return name;   }  void setName(const std::string& n) {     name = n;   }  const int getId() const {    return id;  }  void setId(int newId) {    id = newId;  }  virtual void update() {     std::cout << "Enemy::update() called.\n";   }};class EnemyWrapper : public Enemy, public luabind::wrap_base {public:  EnemyWrapper(const std::string& n, int id)     : Enemy(n, id) {   }  virtual void update() {     call<void>("update");   }  static void default_update(Enemy* ptr) {    ptr->Enemy::update();  }};int main(const int& arg) {  using namespace luabind;  lua_State *L = luaL_newstate();  luaL_openlibs(L);  open(L);  module(L)  [    class_<Enemy, EnemyWrapper>("Enemy")      .def(constructor<const std::string&, int>())        .property("name", &Enemy::getName, &Enemy::setName)        .property("id", &Enemy::getId, &Enemy::setId)        .def("update", &Enemy::update, &EnemyWrapper::default_update)  ];  luaL_dofile(L, "zombie.lua");  lua_close(L);    return 0;}

zombie.lua
class 'Zombie' (Enemy)function Zombie:__init(name, id)  Enemy(name, id)endfunction Zombie:update()    print('Zombie:update() called.')enda = Zombie('example zombie', 1)a:update()print(a.id)print(a.name)


When executed the program prints out the following:
Zombie:update() called.4

And then the exception is throw when trying to access a.name.

Edit:

So, it looks like I was able to resolve this by fixing my derived constructor. It now looks like this:
function Zombie:__init(name, id)  Enemy.__init(self, name, id)end

I had tried something similar before but was missing the self reference.

Thanks again for your suggestions, I'll try and get cooking with this!

[Edited by - Maxamor on September 11, 2009 6:48:57 PM]

This topic is closed to new replies.

Advertisement