我有一份现有的C++游戏库,使用实体-组件-系统(ECS)。我的库的使用者想要创建一些组件,例如
他可以通过以下方式修改每个猫的hp:
因此,每个访问
为了解决这个问题,用户可以重构他的代码为:-
Cat
:class Cat{ public:
int hp;
float flyPower;
};
他可以通过以下方式修改每个猫的hp:
for(SmartComponentPtr<Cat> cat : getAll<Cat>()){
cat->hp-=5; //#1
}
几天后,他想将 Cat
分割成 HP
和 Flyable
:-
class HP{ public:
int hp;
};
class Flyable{ public:
float flyPower;
};
因此,每个访问
hp
的 cat
都会编译错误(例如在上面代码中的 #1
处)。为了解决这个问题,用户可以重构他的代码为:-
for(MyTuple<HP,Flyable> catTuple : getAllTuple<HP,Flyable>()){
SmartComponentPtr<HP> hpPtr=catTuple ; //<-- some magic casting
hpPtr->hp-=5;
}
它能够工作,但需要用户代码中的大量重构(调用cat->hp
的各个位置)。
如何编辑框架/引擎以解决在ECS中拆分组件时的可维护性问题?
我从未发现任何不受此问题困扰的方法,例如:
- https://github.com/skypjack/entt
(开源-搜索vel.dx = 0.;
行) - https://medium.com/@savas/nomad-game-engine-part-2-ecs-9132829188e5
(博客-搜索int currentHealth;
行) - https://www.randygaul.net/2013/05/20/component-based-engine-design/
(博客-搜索comp->DoStuff( dt );
行) - (C#,Unity3D)http://www.sebaslab.com/learning-svelto-ecs-by-example-the-unity-survival-example/
(由https://codereview.stackexchange.com/questions/48536/an-ecs-model-for-game-development引用的博客;
搜索playerGunComponent.timer += _time.deltaTime;
)
悬赏原因
Yuri的答案是一种很酷的技术,但仍需要进行一些重构。
我当前糟糕的解决方案(pimpl)
如果我想创建Cat
,我将创建6个组件:
Hp_
,Hp_OO
Flyable_
,Flyable_OO
Cat_
,Cat_OO
下面是一个代码示例:
class Hp_ : public BaseComponent{
int hp=0;
};
class Hp_OO : public virtual BaseComponent{
Hp_* hpPimpl;
public: void damage(float dmg){ hpPimpl->hp-=dmg;}
};
class Flyable_ : public BaseComponent{ public:
float flyPower;
};
class Flyable_OO: public virtual BaseComponent{
Flyable_* flyPimpl;
//other function
};
class Cat_: public virtual BaseComponent{};
class Cat_OO: public virtual Hp_OO , public virtual Flyable_OO{
Cat_* catPimpl;
};
现在,可以这样调用:
SmartComponentPtr<Cat_OO> catPtr;
catPtr->damage(5); //: so convenient - no need to refactor
具体实现:
- 如果用户将
Cat_OO
添加到实体中,我的游戏引擎将自动将其父类添加到实体中,例如Hp_
、Hp_OO
、Flyable_
、Flyable_OO
和Cat_
。 必须分配正确的pimpl指针/句柄。
^ 两个操作都可以使用回调函数。
缺点是:
- 需要创建大量组件(浪费内存)。
- 如果存在一个公共的基类,例如
BaseComponent
,我需要虚继承(浪费大量内存)。
优点是:
- 如果用户查询
getAll<Hp_OO>()
,则每个Cat_OO
的Hp_OO
也将包含在返回的列表中。 - 不需要进行重构。
class Cat{ public:
->struct Cat {
- Quentin