I'm trying to use Lua with my game engine prototype but I'm stuck with odd error.
My target is to create X objects in loop with Lua and render them.
sprite = Sprite("icon.jpg", 300, 300, 0)
sprite2 = Sprite("icon.jpg", 100, 100, 0)
b1 = BoxObject(sprite)
b2 = BoxObject(sprite2)
sprite3 = Sprite("circle.png", 200, 100, 0)
sprite4 = Sprite("circle.png", 300, 100, 0)
b3 = CircleObject(sprite3)
b4 = CircleObject(sprite4)
n = Node()
n:AddChild(b1)
n:AddChild(b2)
n:AddChild(b3)
n:AddChild(b4)
for i = 0, 10, 1 do
x = math.random(700)
y = math.random(500)
n:AddChild(BoxObject(Sprite("icon.jpg", x, y, 0)))
end
for i = 0, 10, 1 do
x = math.random(700)
y = math.random(500)
local s = Sprite("circle.png", x, y, 0)
local o = CircleObject(s)
n:AddChild(BoxObject)
end
If I do it this way, code works without errors, but game crashes in random time from instant to few secs. If I use this code with only objects created in loops, not those created manually, game crashes instantly.
However if I write Lua code equivalent in C++, it runs without any problem.
for(int i = 0; i < 20; i++){
float x = rand() % 700;
float y = rand() % 500;
n->AddChild(new BoxObject(new Sprite("icon.jpg", x, y)));
}
for(int i = 0; i < 20; i++){
float x = rand() % 700;
float y = rand() % 500;
n->AddChild(new CircleObject(new Sprite("circle.png", x, y)));
}
This is my Lua binding
static void Lua(lua_State *lua){
luabind::module(lua)
[
luabind::class_<Node>("Node")
.def(luabind::constructor<>())
.def(luabind::constructor<float, float, float>())
.def("Render", &Node::Render)
.def("Move", &Node::Move)
.def("Rotate", &Node::Rotate)
.def("AddChild", &Node::AddChild)
.def("RotateAroundPoint", &Node::RotateAroundPoint)
];
}
Every method accepts and returns void except AddChild
virtual void AddChild(Node *child);
Sprite and Box aand Circle Objects are inherited from Node class.
Does any one have an idea what can cause this odd error? I would be glad for any help.
You have serious ownership problems.
In C/C++, a piece of code "owns" a pointer or other resource if that code or object takes on the responsibility for destroying it. It keeps it alive while it needs it, and it ensures that it is destroyed when it is finished using it.
Maintaining an understanding of who owns what is vital for any even mildly complex C or C++ program.
When Luabind creates a C++ object from Lua, then the Lua script owns that object. This means that Lua's garbage collector will decide when to delete it.
So this:
Will create a
Sprite
object who's lifetime is governed by the Lua state. When the Lua state no longer has any active references to it, then the Lua state will be free to delete it.I don't know how your code works, but this line:
Suggests that
CircleObject
's constructor is supposed to claim ownership over the object it is given (thus theCircleObject
decides when to delete it). If that's true, then you need to actually state that when you bind this constructor with Luabind.If
CircleObject
's constructor is not supposed to take ownership, then... well, quite frankly your design is suspect; long-term storage of a pointer that you don't own is just begging for something to get screwed up. But if it's really supposed to store a pointer that's owned by someone else, then you need to use Lua mechanisms to ensure that theSprite
remains alive so long asCircleObject
does.Similarly, the
Node::AddChild
function looks like it's claiming ownership of whatever node it is given. This would be done like this:The
_1
means the first parameter of the function. This means that theNode
claims ownership of the first parameter; if it's owned by Lua, this ownership chain is now broken, and C++ now owns the object.Personally, I would say that this API is... not good for Lua. You seem to want all of the minor object ownership to be done in C++ code. So Lua should call C++ functions that set all of these minor objects up without Lua having to interfere. Lua should call a member function of a
NodeManager
in order to get/create aNode
, then it should call a function that will create aSprite
within thatNode
.Lua shouldn't have to deal with the minutiae of
BoxObject
and such. It shouldn't have to directly create aSprite
object and put it into aNode
.Also, again personally, this API is not good for C++ either. If
Sprite
needs to be stored in some kind of container, and that container needs to be stored in some kind ofNode
, then it should not be possible to create aSprite
without storing it in a container. This is what factory functions are for. I'm not even sure whatBoxObject
andCircleObject
are for, sinceSprite
seems to be doing all the work.