c++ - Entity-Component-System 中的拆分组件需要过多的重构
问题描述
我有一个使用实体组件系统 (ECS) 的现有 C++ 游戏库。
我的图书馆的用户想创建一些组件,例如Cat
:-
class Cat{ public:
int hp;
float flyPower;
};
他可以修改hp
每一个cat
,例如:-
for(SmartComponentPtr<Cat> cat : getAll<Cat>()){
cat->hp-=5; //#1
}
几天后,他想Cat
分手HP
:Flyable
-
class HP{ public:
int hp;
};
class Flyable{ public:
float flyPower;
};
因此,每次cat
访问hp
都会编译错误(例如,#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>()
,Hp_OO
of eachCat_OO
也将在返回的列表中。 - 无需重构。
解决方案
会员指点救援:
#include <tuple>
template <typename... Components>
struct MultiComponentPtr {
explicit MultiComponentPtr(Components*... components)
: components_{components...}
{}
template <typename Component, typename Type>
Type& operator->*(Type Component::* member_ptr) const {
return std::get<Component*>(components_)->*member_ptr;
}
private:
std::tuple<Components*...> components_;
};
struct Cat {
int hp;
float flyPower;
};
struct HP {
int hp;
};
struct Flyable {
float flyPower;
};
int main() {
{
Cat cat;
MultiComponentPtr<Cat> ptr(&cat);
ptr->*&Cat::hp += 1;
ptr->*&Cat::flyPower += 1;
}
{
HP hp;
Flyable flyable;
MultiComponentPtr<HP, Flyable> ptr(&hp, &flyable);
ptr->*&HP::hp += 1;
ptr->*&Flyable::flyPower += 1;
}
}
从技术上讲,您仍然需要重构,但是&Cat::hp
用&HP::hp
等自动替换是微不足道的。
推荐阅读
- c++ - 如何在 Eclipse 中正确使用 gmp-mpir-mpfr?
- reactjs - 为什么我在 componentDidMount 中的函数没有被触发?
- linux - 系统重启后重新连接到控制台输出
- python - Sympy:指定函数的导数
- javascript - 使用引导程序显示带有来自 javascript 的两个按钮的确认对话框
- javascript - 为什么在编写函数式组件时 React 应该在作用域内?
- css - 禁用 Angular Material 按钮的不同布局的紧凑代码
- flutter - 如何在颤振中使用像 Wrap 一样的 TextField
- excel - 如何计算包含特定文本字符串的表格列中的单元格(COUNT.IF 不起作用)
- python - Python不和谐机器人发送到多个渠道问题