自制ECS库-MyECS
0.前言
因受到entt启发,以学习为目的,我仿照entt制作了一个纯头文件ecs库,目前许多功能还在实验中。其大部分接口与逻辑与entt相同,但在底层实现上一些差别。该博客主要是对ECS的介绍,对MyECS库的介绍并附带一些对entt的一些思考之类的。最后本人水平知识有限,请大佬轻喷(doge),非常欢迎大家来交流学习。
github网址 ↓
MyEcs: https://github.com/Flaaax/MyECS
entt: https://github.com/skypjack/entt
1.什么是ECS?
ECS是一种游戏架构,全称Entity Component System(实体组件系统),通常用于游戏开发,并显著区别于经典的 OOP(面向对象编程) 架构。其定义分为三个部分Entity,Component和System。
1.Entity
每个Entity仅为一个唯一标识符 ,通常是一个无符号整数,不包含逻辑或者额外的东西,而不是像 OOP Style 一样的有着繁杂接口,超多成员的基类。
在OOP中,一个Entity可能长这样:
1 | class Entity{ |
然而,ECS中的Entity仅作为一个标识符,看起来就简单多了:
1 | using Entity = size_t; //unsigned integer |
(当然,这只是个示例,实际上还要考虑版本控制等)
那么它的数据和逻辑放哪呢?在ECS,一个Entity会关联一些Component,它们负责存储数据,而System负责处理这些数据逻辑。请继续往下看。
2.Component
Component是一些仅存储数据的容器,例如位置,速度,血量等。每个Entity都可以关联多种Component来为其添加属性,换言之,对于一类Component,每个Entity都唯一对应了这一类的一个实例(如果它们有关联的话)
在MyECS的实现中,你不需要为Components做额外操作。直接定义它们,然后立刻使用。
1 | struct Position{ //无需任何修饰 |
就像entt一样,我在MyECS舍弃了继承关系和一部分安全性,换来了非常大的便利。
为什么我们不像OOP一样把所有东西都放在一个类里呢?因为你可以选择一个Entity持有哪些Components,不再需要负担你不需要的东西。在OOP,很多时候你必须在基类添加过多数据成员,而它们很多都被子类浪费了。例如,大部分实体都需要“位置”和“渲染”,但不是所有都需要“生命”。Component的设计很好地解决了这一问题。
3.System
有了数据,现在只需为它们添加逻辑。在OOP,这部分通过继承Entity的虚函数来处理,但在ECS这正是System做的事。一个ECS可以有多个System,比如下面这些:
1 | class PhysicsSystem{ |
在纯粹的ECS中,所有逻辑都应该交给System来做,这些System不持有任何数据,仅负责处理数据。如果你有额外需求,那么就多设计几个System。当然,MyECS库并不会在乎这一点,你仍可以在任何地方添加你的逻辑,只要确保你知道自己在做什么。
2.为什么选择ECS而不是OOP?
因为写着很爽(划掉
ECS 能够抛开繁琐的继承关系,将实体化为组件的组合,这样结构更清晰,对代码编写者更友好。事实上,这也是我选择学习ECS的一个重要原因。
其它原因也包括比OOP效率更高,缓存更友好等 (这在后面会解释)
总之,在某些场合下,ECS确实能比OOP发挥更大的优势
3.ECS的优缺点
优点:
1.代码结构清晰,更好编写,别提多爽了
2.缓存命中率高,更好发挥cpu性能
缺点:
1.适用范围有限。与许多知乎“大佬”的意见不同的是,并不是所有情况都应该使用ECS,ECS也绝无可能取代OOP。不适合的例子就如卡牌游戏,UI系统等,而适合的例子有类似《Noita》的复杂弹幕游戏。
2.学习难度略大,因为ECS的架构与传统OOP差别过大,需要一定努力来适应新的模式并抛弃以前的继承式思维,当然这点因人而异了。
总之,希望以上内容帮助你判断你是否需要学习ECS并在你的新游戏里实践它。
感觉这次也写的够多了,关于MyECS的介绍就放到下期吧() 感谢阅读,最后再贴一遍网址:
自制ECS库-MyECS