Articles of 实体系统

对ECS系统实施的困惑

作为参考,我主要是模仿本教程中的体系结构,即“实体系统”部分: http : //www.raywenderlich.com/24878/introduction-to-component-based-architecture-in-games 注意:本教程是在Objective-C中,但我正在使用C ++。 另外,我现在只是编写“快乐案例”,因为我不确定这是否是我所要的架构。 我只是在试验。 快速背景:每次系统的update()函数被调用时,相关的系统调用一个EntityManager中的助手方法getEntitiesWithProperty(),该方法返回一个拥有系统运行的组件types的所有实体的向量。 template <class T> std::vector<unsigned int> getEntitiesWithProperty() { std::vector<unsigned int> ret; auto key = std::type_index(typeid(T)); std::unordered_map<unsigned int, PProperty*> map = data[key]; for(auto i = map.begin(); i != map.end(); i++) { ret.push_back(i->first); } return ret; } 然后系统遍历这个向量,在实体向量中的每个元素中调用另一个EntityManager中的辅助方法。 此方法返回一个指向与正在迭代的特定实体关联的组件的指针,系统在该实体上执行其逻辑: void SRender::update(EntityManager& entM, float frameTime) { auto ids = entM.getEntitiesWithProperty<Image>(); […]

实体/组件系统 – 唯一行为

所以,我决定看看游戏开发的实体/组件系统是什么。 我真的很喜欢它。 所以,我决定用它来实现我目前的游戏。 我的问题:与系统的唯一行为。 从我所看到的,为实体实现唯一行为的方法是创建一个唯一的组件,以及一个只接受具有该组件的实体的唯一系统。 这似乎是很多代码,可能是一个非常小的function。 我的解决scheme:让实体具有“钩子”,以便他们可以在其中定义自己独特的行为。 他们仍然有系统为他们完成的主要事情,比如健康,渲染和运动。 例如: public abstract class EntityLiving extends Entity{ /** * Called when a entity is spawned in * @param map */ public void onSpawn(Map map) {this.map = map;} /** * Called when an entity is killed * @param map */ public void onDeath(Map map) {} 我的问题:这是不好的编程习惯? 这是否可以以“更好”的方式完成? 我意识到这个问题是非常主观的,但是我想请一些专业的意见。 […]

实体组件系统:系统和组件的关系

我打算用C#开发一个游戏(但是我不认为这个语言很重要),我将会使用实体组件系统,因为我通过devise知道我的游戏将会有很多不同的游戏项目和不同的扭曲行为和逻辑。 我有一些使用Unity ECS的经验,我觉得非常简单直观。 过去我也使用旧的“inheritance方法”对游戏进行编程,发现这种模式非常不可扩展。 据我所知,一个纯ECS(这不是Unity实现的)是这样工作的: 游戏对象只是组件上的一个集合,它可能被看作是一个包装类,或者是一些平行的组件数组中的共享索引。 组件是一个数据集合(基本上是一个logging),它保存相关系统处理它所需的数据。 它并没有实现游戏逻辑本身。 系统是一个世界对象,通过每个游戏对象循环,每个游戏对象有一个组件,它可以处理和更新该组件的游戏逻辑。 我的观点是:如果系统负责实际的游戏逻辑更新(实际上游戏逻辑代码是写在系统内部的话),那么每个组件和每个系统之间是否存在1:1的关系呢? 让我用一个例子来解释一下:假设我有两个组件: RenderSpriteComponent RenderLightSourceComponent 第一个做一个简单的2D精灵渲染(使用OpenGL或任何); 第二个向渲染着色器发送光源。 他们做类似的行动,但有一些实际的差异。 我的问题是:因为他们的行为是不一样的,我应该执行两个不同的系统来分离处理它们吗? 如果没有,我应该添加一堆IF语句或重载来处理同一个系统内的这两个组件? 从我的角度来看,这两种方式似乎都是不必要的,特别是如果ECS应该改进代码的可扩展性。 使用这些方法之一会迫使我为每个组件实现一个不同的系统 (暗含很多GameObjects循环),或者让我的系统填充if和overload,使它们在某个点之后不可能理解。 此外,从我所看到和理解的情况来看,Unity实现了这种模式的不同版本,其中每个组件都实现了自己的逻辑(在Update方法中),而GameObject是一个包装类,它有一个内部的组件更新列表。 系统根本不存在(或者如果他们这样做,他们这样做是出于其他原因)。 从我的角度来看,这个实现看起来更简单,更“优雅”,但是我不明白为什么它不那么普及,每个人都坚持上面描述的那些“ 问题 ”的“经典”ECS实现。 同时,我不能控制组件更新的优先级,这在某些情况下是有用的。 我明白,devise游戏结构并不是一种“绝对的”更好的方式,但我想知道如何实现。 或者哪个因素会比另一个更好。

基于组件的实体系统中的各种运动

我正在用基于组件的实体系统写一个pong克隆。 但是我在游戏中遇到了各种不同的运动。 问题是:我的桨会上下移动,所以我只需要一个Y-速度。 但球可以在每个方向移动。 所以球需要一个角度和一个速度。 现在我的问题是:通常把数据移动到另一个组件而不是移动桨的数据? 因为如果我这样做的话,我需要两个系统来运动。 这是一个好方法吗? 关于我的系统的一点信息:我的entites只有ID。 我有一个实体经理,持有所有的实体和链接的组件。 组件只保存某些系统运行的数据。 基本上我使用这种方法: 教程

在实体管理器中将时间戳记作为ID?

我已经建立了我自己的实体管理器,就像我前几天发现的那样,它和Artemis框架非常相似。 现在,我只是使用整数作为实体ID。 在幕后,每创建一个新实体时就会有一个简单的整数,这样经理就可以按照递增的顺序发送这个ID。 但是最近我遇到了无法将多个savegame文件加载到相同场景的问题。 这里的问题是,两个savegames可以使用相同的实体id来处理不同的事情,当被加载到相同的场景时会引起冲突。 无论如何,加载多个存档游戏的能力对于保持场景数据(岩石,树木,城镇中的玩家)与分组玩家数据(他的技能,库存,自定义项目和工具)以及来自mods的数据(很酷的新武器包)。 所以我需要每个游戏文件中都有唯一的ID,这些文件将会被创建,而不需要彼此了解。 我想到的想法是使用时间戳作为实体ID而不是连续的整数。 (因为多个实体可能在同一个毫秒内创建,所以时间戳应该包含微秒,为了安全起见,如果在相同的微秒内创建了多个实体,也可能会有一个后缀连续数的机制。知道现代CPU有多快,如果他们能做到这一点,但我想他们可以。) 由于我没有听说过其他人这样做,我认为这是一个权衡。 使用时间戳作为实体ID有什么缺点? 我认为这个performance会很轻微,可以忽略这一点。

着色器和灯光应该在基于组件的实体系统中在哪里?

我应该在哪里放置着色器和光照/阴影计算? 这是否也是一个组件呢? 渲染系统是否知道如何处理它们,还是应该有一个单独的灯光系统? 我具体谈论的是一个2D系统,但我认为它应该与3D相同。

ECS和实体的生命管理

我完成了我的游戏框架,我试图实现一个生命管理系统。 我的框架到目前为止有: 持有指向他们感兴趣的实体的系统。 拥有不同系统的引擎类。 拥有实体的EntityManager。 现在,每次向/从实体添加/删除组件时,实体都会通知引擎类运行一个(n)(un)注册例程,该例程将实体传递给系统,系统又决定是否要(不)注册实体或不。 假设我有一个DestroyerSystem,logging所有实体的HealthComponent,并负责删除没有生命的实体: void DestroyerSystem::update(float dt = 0) { for (auto& entity : registered_entities) { if (entity.getComponent<HealthComponent>().hp == 0) { EntityManager.destroy(entity); } } } 问题是,系统应该保持对实体的有效指针,我不知道如何能够实现这个干净。 目前唯一的解决scheme是使系统具有一个unregister()函数,该函数将指针指向已删除的元素,并将其从registered_entities容器中删除。 Engine类还有一个可以调用系统的unregister()函数的unregister()函数。 这适用于其他系统,但在应用到DestroyerSystem时会产生问题,因为它会中断迭代。 为了解决这个问题,我复制容器,并在其上迭代: void DestroyerSystem::update(float dt = 0) { auto entities = registered_entities; for (auto& entity : entities) { if (entity.getComponent<HealthComponent>().hp == 0) { entityManager.destroy(entity); […]

ECS性能问题

我正在按照“经典”的意义实施ECS,就像我们拥有实体,组件和系统的精彩文章所定义的那样。 而且我有严重的(对我来说)性能问题。 他们来自于System可能需要访问实体的几个组件的事实: IList<int> es = _ecsManager.GetComponentEntities(ComponentType.RENDER); int cacheRender = -1; int cacheMovement = -1; foreach (var entityId in es) { var rc = _ecsManager.GetEntityComponent<RenderComponent>(entityId, ComponentType.RENDER, ref cacheRender); var mc = _ecsManager.GetEntityComponent<MovementComponent>(entityId, ComponentType.MOVEMENT, ref cacheMovement); rc.Sprite.Position = mc.Position; QueueSprite(rc.Sprite); } QueueSprite是我的精灵批处理,它由长期运行的常规数组和List 。 而GetEntityComponent是: public T GetEntityComponent<T>(int entityId, int componentId, ref int idxCache) where T : […]

带有模型视图控制器的实体组件系统

模型视图控制器devise模式是否可以与非OOP编码风格一起使用,特别是与实体组件系统?

基于组件/实体的devise=>没有inheritance?

[我明白,这可以被看作是一个相当理论性的问题,但我认为它有实际应用的影响]。 “学术”基地 通过基于组件的devise,人们希望摆脱大游戏中已知的长期inheritance链( “致命的死亡钻石” )。 所以我们创建一个BaseComponent类来处理所有的管理杂事(ID和types处理,可能是一个通用的属性列表…)。 现在,每个实际的Component都应该从那个基地inheritance[学术/教条风格的开始] , 只有那个基础[end] 。 具体情况/例子: 假设我有三个不同属性的组件: Person :年龄,名字,性别,是累,isSick,… Guest :满意,欲望,… Employee :当前值class,class次结束,… 现在在传统的面向对象的devise中, Person将作为另外两个的基类。 正如我目前“禁止”在我的项目中的组件types的inheritance,每次我例如处理我的员工和检查/更新他们的状态属性,我不得不使用一些辅助函数来访问兄弟组件Person (如果存在的话)或发出一个信息,希望Person将被注册并将处理。 或者我可以简单地假设每个Guest和Employee总是拥有组件Person。 但是,我不妨让这两个组件inheritance自Person ,所以他们例如都有成员函数来操纵一些特定的属性。 注意:永远不会有一个Entity具有组件Employee ,但不是组件Person (同样适用于Guest )。 他们是一个固定的要求,这就是为什么我在这里考虑inheritance。 问题:现在我的问题是,是否有人能够看到任何真实的问题,这些问题是通过(重新)将一些遗传join到我的组成部分来引入的,而不是违反原始基础/教条式概念的程度,过度使用),我最终会遇到基于组件的devise首先试图解决的问题。 参考文献: 线程最终导致我问这个问题,显示这样的inheritance。