原工程链接:
https://github.com/sschmid/Match-One
Match One
This is a simple and interactive Unity3d example project to show how to use Entitas. Entitas is a super fast Entity Component System (ECS) Framework specifically made for C# and Unity
Get Entitas here: https://github.com/sschmid/Entitas-CSharp
Match One is a very simple CandyCrush-like Match 3 example, except it's Match One.
Watch the talk from Unite Europe 2015 to get an in-depth tutorial.
Match One shows
- systems list in GameController
- how you can use reactive system to only process changed entities
- the usage of EntityIndex for super fast entity access
- how you can use multiple pools to reduce the memory footprint of each entity (Input, Core, Score)
虽然Unity官方推出了ECS,但仍处于非常早期的阶段,因此推荐先使用较为成熟的Entitas来提升自己对ECS的理解。虽然二者对于ECS的实现方式不同,但整体上的结构是类似的,不熟悉的ECS的小伙伴,请先搜索相关的知识。
强烈推荐先阅读:
如何使用Entitas开发游戏(FNGGames)
功能 Features
工程的Features文件下包含了所有关于该游戏的代码(Generated文件内是自动生成的代码)。这些代码定义了Component,System还有Service(如Unity的渲染、随机发生器、引擎输入等)。
面向数据编程跟数据库技术有想通的地方,可惜我并没有系统学习过数据库方面的知识,但这里还是借用表的形式来展开。
ALL COMPONENTS:
Components | Values | About |
---|---|---|
PositionComponent | Vector2Int pos | 2D位置 |
PieceComponent | 用以标识“棋子”的标签 | |
MovableComponent | 用以标识“可移动物体”的标签 | |
SelectedComponent | 用以标识“被选中”的标签 | |
InteractiveComponent | 用以标识“可交互”的标签 | |
DestroyedComponent | 用以标识“被销毁”的标签 | |
BurstModeComponent | 用以标识“Burst Mode”的标签 | |
BoardComponent | Vector2Int size | 2D棋盘大小 |
GameConfigComponent | Two getter function for boardSize & blockerProbability | 这个组件比较特殊,其实是个interface,定义了两个方法 |
InputComponent | Vector2Int mousePos | 鼠标输入坐标 |
ScoreComponent | Int score | 分数 |
AssetComponet | string path | 资源路径 |
ViewComponent | IView view | 包含一个Link方法Interface,将entity和Unity的gameObject关联 |
ECS中,System是没有状态的,只负责行为,因此游戏中所有状态都由Component负责。在过往的编程中,我们常使用bool变量来表征物体是否处于某个状态,在ECS中我们使用tag component(空Component)。
All ENTITIES:
需要提醒的是: 与面向对象编程每种物体定义一个类再实例化不同,ECS是创建一个Entity(可能是类,也可能是ID,依赖于不同的实现方式),之后将Components与之关联,根据不同Component的组合来实现差异化。
Entities | Owning Components | About |
---|---|---|
Game Config Entity | GameConfigComponent | 记录游戏开始前在game config资源中配置的数据,即棋盘大小,和blocker出现概率 |
Score Entity | ScoreComponent | 记录得分 |
Score Listenner Entity | AnyScoreListenerComponent | 这个component在上面列表中没提到,这个component有点类似语法糖,它记录了一系列方法,当ScoreComponet发生改变时,会自动调用这些方法。当然我们其实也可以写一个System来实现这个功能。 |
Input Entity | InputComponent | 当鼠标点击时动态创建,记录鼠标点击位置,经过System处理后自动销毁 |
Burst Mode Entity | AnyBurstModeListener & AnyBurstModeRemoveListener | 监听burst mode component的变化 |
Gameboard Entity | BoardComponent | 棋盘,记录大小 |
Piece Entity | Asset, Destroyed Listener,Interactive,Movable,Piece,Position,Position Listener,View Component | 棋子,最复杂的Entity |
我们看到除了上面的列表中所列的Component之外,多了不少自动生成的xxxListener Component,这些组件保存了方法的入口,需要提醒的是,这在Unity ECS中是禁止的。我们可以写system来实现类似的功能。
还有一个与面向对象编程不同的是,创建和销毁entity的开销是非常小的,因此我们可以“疯狂”地动态创建和销毁entity(销毁关联的component可能要一些开销,但比起面向对象中对象的操作要高效得多),将其作为刺激System运作的pulse脉冲输入。
ALL SYSTEMS:
Systems | Related Components | Behavior |
---|---|---|
Board System | Piece , Position ,Board , GameConfig | 创建棋子,并将其放在正确的位置上,响应棋盘大小的变化 |
Fall System | Destroyed , Piece,Position | 当有棋子被标记为销毁,移动剩余棋子 |
Fill System | Destroyed , Piece ,Board ,Position | 当有棋子被标记为销毁,添加新的棋子 |
GameCleanupSystem | Destroyed | 销毁所有标记为销毁的entity |
InputSystem | 执行Unity的输入检测,并动态创建entity | |
ProcessInputSystem | Input | 根据input component中的值找到对应位置的entity,为其添加destroyed component |
InputCleanupSystem | Input | 销毁所有带有input componet的entity |
其实还有一部分是关于View部分的system,大家还是下载工程来看吧,很容易理解的。
关于系统顺序,有的时候并不需要严格按照思路中的逻辑排序的。比如这里,是先标记destroy tag,在帧的末尾才真正执行销毁。更有甚者,许多逻辑是不必要在一帧内执行的,可以容忍1帧的延迟,比如物体的生成和表现。之所以提这个,主要是考虑到Unity ECS中的Job System多线程并发。我们没必要添加多余的依赖关系。
Primary Index
工程中使用PositionComponent的值来检索在该位置上的entity。
其中的
(e,c) => (c as PositionComponent)?.value??e.position.value
我并不太清楚为什么要这样写,目的是为了得到vector2Int类型值作为Key。
我们还要注意这里的group中排除了DestroyComponent,所以即使我们不删除entity,只要添加了DestroyComponent,用vector2int值为key,调用
也会返回null。因此,就算不删除entity,Fall、Fill System都能正常运作。在Unity编辑器中,会看到许多具有相同position的entity,但有且只有一个没有DestroyComponent。
这里就引出了一个问题,如何通过component的值查找对应的entity?
最简单的方式当然是遍历具有该component的entity,逐一比对。Entitas中可以利用键值对。Unity ECS,还没研究。
网友评论