ojoj kolol ojoj kolol - 1 year ago 34
C++ Question

Game engine design issue

I am working on a game engine using ECS. My issue is how i spawn an entity. My idea is that i have a method that takes in an entity as an argument, creates a clone of this entity, retrives all the pointer components from the clone and put them in their respective systems for updating:

Entity & Scene::spawnEntity(Entity entity) {

Entity clone = Entity(entity);

Transform* transform = clone.getComponent<Transform>();
Drawable* drawable = clone.getComponent<Drawable>();
Collidable* collidable = clone.getComponent<Collidable>();
Scriptable* scriptable = clone.getComponent<Scriptable>();

if (transform != nullptr) {
_transformSystem.add(*transform, _currentId);
}
if (drawable != nullptr) {
_drawableSystem.add(*drawable, _currentId);
}
if (collidable != nullptr) {
_collidableSystem.add(*collidable, _currentId);
}
if (scriptable != nullptr) {
scriptable->assignCallbacks([&](Entity entity) -> Entity& { spawnEntity(entity); },
[&](Entity entity) { destroyEntity(entity); },
[&](std::vector<std::string> tags) -> Entity& { findEntity(tags); },
[&](std::vector<std::vector<std::string>> tags) -> std::vector<Entity>& { findEntities(tags); });
_scriptableSystem.add(scriptable, _currentId);
}

_entities.push_back(clone);

_currentId++;
}


The issue here is that one of the components, namely a scriptable is a pure abstract class (it has an initiate method and update method that the developer uses to create behaviour in derived classes). This makes it so that the engine cant automatically clone the scriptable class, the cloning has to be done in the derived class, for example like this:

class PlayerScript : public Scriptable
{
public:
void init() override;
void update() override;
PlayerScript* clone() override;
};


PlayerScript * PlayerScript::clone()
{
return new PlayerScript(*this);
}


I dont want the user to have to create a clone method for each script he or she creates, i think it should be handled by the engine automatically. But i cant figure out how to do it differently.

Answer Source

As long as players' scripts are copy constructible and you can clone them by means of the copy constructor, you can use CRTP and an intermediate class like this (minimal, working example):

struct BaseScriptable {
    virtual ~BaseScriptable() = default;
    virtual BaseScriptable * clone() = 0;
    virtual void update() = 0;
};

template<typename D>
struct Scriptable: BaseScriptable {
    BaseScriptable * clone() override final {
        return new D{*static_cast<D *>(this)};
    }
};

struct PlayerScript: Scriptable<PlayerScript> {
    void update() override {}
};

int main() {
    BaseScriptable *script = new  PlayerScript;
    BaseScriptable *clone = script->clone();
}

If the copy constructor isn't enough to clone a player script, well, that means that you don't have enough information to clone it actually. Because of that it's mandatory that the developer that defines the script also defines a clone routine to give you a proper copy.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download