游戏引擎设计问题
Game engine design issue
我正在使用 ECS 开发游戏引擎。我的问题是我如何生成一个实体。我的想法是我有一个方法,它接受一个实体作为参数,创建这个实体的克隆,从克隆中检索所有指针组件并将它们放入各自的系统中进行更新:
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++;
}
这里的问题是其中一个组件,即可编写脚本的组件是纯抽象的 class(它有一个启动方法和更新方法,开发人员使用这些方法在派生 class 中创建行为es)。这使得引擎无法自动克隆脚本 class,克隆必须在派生的 class 中完成,例如:
class PlayerScript : public Scriptable
{
public:
void init() override;
void update() override;
PlayerScript* clone() override;
};
PlayerScript * PlayerScript::clone()
{
return new PlayerScript(*this);
}
我不希望用户必须为他或她创建的每个脚本创建一个克隆方法,我认为它应该由引擎自动处理。但我无法弄清楚如何以不同的方式做到这一点。
只要玩家的脚本是可复制构造的并且您可以通过复制构造函数克隆它们,您就可以像这样使用 CRTP 和中间体 class(最小的工作示例):
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();
}
如果复制构造函数不足以克隆播放器脚本,那么,这意味着您没有足够的信息来实际克隆它。因此,定义脚本的开发人员还必须定义一个克隆例程来为您提供适当的副本。
我正在使用 ECS 开发游戏引擎。我的问题是我如何生成一个实体。我的想法是我有一个方法,它接受一个实体作为参数,创建这个实体的克隆,从克隆中检索所有指针组件并将它们放入各自的系统中进行更新:
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++;
}
这里的问题是其中一个组件,即可编写脚本的组件是纯抽象的 class(它有一个启动方法和更新方法,开发人员使用这些方法在派生 class 中创建行为es)。这使得引擎无法自动克隆脚本 class,克隆必须在派生的 class 中完成,例如:
class PlayerScript : public Scriptable
{
public:
void init() override;
void update() override;
PlayerScript* clone() override;
};
PlayerScript * PlayerScript::clone()
{
return new PlayerScript(*this);
}
我不希望用户必须为他或她创建的每个脚本创建一个克隆方法,我认为它应该由引擎自动处理。但我无法弄清楚如何以不同的方式做到这一点。
只要玩家的脚本是可复制构造的并且您可以通过复制构造函数克隆它们,您就可以像这样使用 CRTP 和中间体 class(最小的工作示例):
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();
}
如果复制构造函数不足以克隆播放器脚本,那么,这意味着您没有足够的信息来实际克隆它。因此,定义脚本的开发人员还必须定义一个克隆例程来为您提供适当的副本。