In the simple 2D Game Engine I am working. I want to define and script my entities using Lua and do all the heavy work in C++ (rendering, physics, etc.)
To do this I have basic Entity class:
class Entity {
public:
Entity() {
alive = true;
livingCount++;
}
void destroy() {
alive = false;
}
bool isAlive() { return alive; }
~Entity(){
// Gets called if I manually call delete entity in EntityManager or if I use smart pointers, but causes seg faults in either case
std::cout << "Entity destructor called." << std::endl;
livingCount--;
}
private:
bool alive;
};
And the EntityManager singleton which groups all active Entities and is used in main game loop to refresh, update and render entities in this order, here is the EntityManager class:
class EntityManager {
public:
static EntityManager* Instance() {
if(s_pInstance == 0) {
s_pInstance = new EntityManager();
return s_pInstance;
}
return s_pInstance;
}
// Main game loop goes through these and calls refresh(), then update() and finally render() on each frame
void update(float deltaTime) {
for (auto& e: entities)
e->update(deltaTime);
}
void render(float deltaTime) {
for (auto& e: entities)
e->draw(deltaTime);
}
// This method causes segfault after entity gets destroyed, it doesn't matter if I use raw pointers or smart, I am still getting segfault here
void refresh() {
for (auto& e: entities) {
if (!e->isAlive()) {
entities.erase(std::remove(entities.begin(), entities.end(), e), entities.end());
// Uncommenting this also causes segfault, but if this is commented, Entity* is not freed from the memory, only gets removed from vector
// delete e;
}
}
// Tried doing the same using shared_ptr or even unique_ptr, but every time entity is destroyed this causes segfault as well
// entities.erase(std::remove_if(std::begin(entities), std::end(entities),
// [](std::shared_ptr<Entity> &e) {
// return !e->isAlive();
// }),
// std::end(entities));
}
// Add new entity
// This will become legacy, I am only using this to create entities in C++, but soon they will only be created by Lua and passed to this class
Entity* addEntity() {
Entity* entity = new Entity();
entities.push_back(entity);
return entity;
}
// Add existing entity: for lua
// I have a feeling this method is not correct
void addEntity(Entity* entity) {
entities.push_back(entity);
}
const std::vector<Entity*>& getEntities() { return entities; }
private:
EntityManager() {}
static EntityManager* s_pInstance;
// Probably should use std::shared_ptr<Entity> instead
// I tried this on Ubuntu and it does seem to solve memory problem and entities get deleted
// but also getting segfault on Linux and I'm not sure if it's because of this class or
// my use of unique pointers
std::vector<Entity*> entities;
};
Now everytime I want to create new Entity, I can do this from Lua script, because I have implemented __index and __gc metamethods for Entity, so this works perfectly:
testEntity = Entity.create()
testEntity:move(400, 400)
testEntity:scale(2, 2)
testEntity:addSprite("slime", "assets/sprite/slime.png", 32, 32)
testEntity:addAnimation("default", "0", "4")
testEntity:addCollider("enemy", 48, 48)
print("Slime ID: "..testEntity:id())
And finally, this is Lua binder for then we want to create new entity, notice how I pass pointer to EntityManager as well, because otherwise game will not update it:
int Script::lua_createEntity(lua_State *L) {
Entity* entity = (Entity*)lua_newuserdata(L, sizeof(Entity));
// Placement new operator takes already allocated memory and calls constructor
new (entity) Entity();
// This Entity created in here must be added to EntityManager vector so that game knows about it
EntityManager::Instance()->addEntity(entity);
luaL_getmetatable(L, "EntityMetaTable");
lua_setmetatable(L, -2);
return 1;
}
And this method is bound to __gc metatable, but does not seem to get called at all, I'm not even sure if I want Lua to take care of this:
// Will be called once lua state is closed
int Script::lua_destroyEntity(lua_State *L) {
Entity* entity = (Entity*)lua_touserdata(L, -1);
std::cout << "Lua __gc for Entity called" << std::endl;
entity->destroy();
return 0;
}
Now, let's say I have an Entity which is also a Projectile, every time Projectile goes off screen, I call entity->destroy() on that Projectile and it's member "isAlive" is marked as "false". This is all done in C++, but projectile gets created by Lua script
So on the next frame, EntityManager refresh() method is invoked and that projectile gets removed correctly from the Vector, but if I try to delete it then to free the memory, it causes segfault, otherwise destructor never gets called.
As I said before, I have tried std::vectorstd::unique_ptr<Entity> and std::vectorstd::shared_ptr<Entity>, but that also causes segfault in EntityManager refresh method, which makes me think something fundamentally is wrong with how I am dealing with Entity lifetime.
__gc metamethod might not even be needed for me, as I want EntityManager to take care of all active Entities in the game, Lua will only be able to tell it which entity have been marked as destroyed and let C++ take care of deleting it.