Questions tagged «entity-system»

一种游戏范例,其中游戏对象(实体)由组件组成,并由系统对其进行操作。每个实体都是一个指向特定组件的ID。

4
如何在网络游戏中以可靠的方式分配实体ID?
我正在为网络游戏开发一个实体系统,并为每个实体分配一个唯一的32位整数ID,该ID可用于序列化对实体和实体本身的引用。 目前,我只是在每次创建实体时增加一个计数器。我想这些ID最终会用完,但我并不真正希望拥有40亿个实体。如果实体#5被销毁并且我们获得ID为5,这也避免了问题。这是指引用新的#5还是旧的已删除的#5? 问题是我不确定如何处理/避免冲突。当前,如果客户收到一个ID高于其当前“免费ID”的实体的更新,则该客户的免费ID会超过该值。但这似乎不是很可靠。 我考虑过为每个客户端分配范围,以便他们可以分配实体而不会发生冲突(例如,前n位是玩家编号),但是我担心如果范围随时间开始重叠会发生什么情况。 有没有更好的方法来解决这个问题?我是否应该担心ID溢出或超出允许范围的末尾?我可以添加代码来检测这些情况,但是如果发生这些情况而不是崩溃,该怎么办。 另一种选择是使用具有更高独特性的功能,例如128位GUID,但对于试图最小化网络流量的游戏而言,这似乎是重量级的。而且,实际上,我永远不会一次需要更多实体,而是适合一个32位甚至24位整数。 谢谢!

2
基于组件的实体系统中的游戏状态和输入处理
我的问题是: 如何在我的实体系统中处理游戏状态,而又不保留周围的游戏状态对象堆栈? 因此,我的实体系统的设计意味着,例如,当某个实体需要注册输入事件时,输入组件将调用输入系统,并说“为此输入注册此实体”。一切都很好,但是,如果您将游戏状态的概念(例如暂停屏幕)加入其中,那么确定实体是否处于当前状态并应接收输入就变得很困难。 我可以扩充输入组件/系统,使其说“在处于这些游戏状态时为此输入注册此实体”,但这要求每个实体都知道将在其中使用哪个状态,这可能并不明显。同样,在每个注册的输入(以及其他使用回调的系统)周围保留游戏状态列表听起来并不太有效。 我有另一个想法,因为将有一个代表游戏状态的实体,将其标记为已禁用,然后在生成输入事件时检查该实体不是已禁用游戏状态实体的后代。为每个回调计算父级似乎很昂贵。 另一个想法是让所有系统针对当前状态存储其数据密钥,这样,在生成输入时,目标实体甚至都不是候选对象。但是,这确实损害了允许处于不同状态的实体之间进行通信的能力(对于暂停屏幕而言,这并不是什么大问题,但请考虑Oblivion / Skyrim中的锁定选择)。 我唯一的另一个想法是让所有组件处理状态更改事件并与相关系统进行通信以禁用其已注册的任何内容,并在切换回此状态时重新启用它。 第二个(将对象标记为已禁用)和第四个(使每个组件都处理状态更改)似乎是我的最佳想法,但是没有一个想法特别出色。 还有其他人对此有其他想法吗? 编辑当我谈论输入专门在这个问题上,它可能意味着能够发送消息/事件的实体,如碰撞,定时器事件等的任何系统...

3
为什么将方法存储在实体和组件中是一个坏主意?(以及其他一些有关实体系统的问题。)
这是我对这个问题的回答,但是我回答了一个更具体的主题。 这个答案使我比本文更好地理解了实体系统。 我读了 有关实体系统的文章(是的),它告诉我以下内容: 实体只是一个id和一组组件(文章说,将实体存储在组件中不是一种好方法,但没有提供替代方法)。 组件是数据片段,它们指示可以对特定实体执行的操作。 系统是“方法”,它们对实体上的数据进行操作。 在许多情况下,这似乎确实可行,但是有关组件仅仅是数据类的部分让我感到困扰。例如,如何在实体系统中实现Vector2D类(位置)? Vector2D类保存数据:x和y坐标,但是它也具有方法,这对于其有用性至关重要,并且可以将类与仅包含两个元素的数组区分开。实施例的方法有:add(),rotate(point, r, angle),substract(),normalize(),和所有其他的标准,有用的和绝对需要的方法该位置(其是对的Vector2D类的实例)应该有。 如果该组件只是一个数据持有者,它将无法使用这些方法! 一种可能弹出的解决方案是在系统内部实现它们,但这似乎非常违反直觉。这些方法是我现在要执行的事情,要使它们完整并可以使用。我不想等待MovementSystem读取一些昂贵的消息,这些消息指示它对实体的位置进行计算! 并且,该文章非常清楚地指出,只有系统才应该具有任何功能,而对此的唯一解释是“避免OOP”。首先,我不明白为什么我应该避免在实体和组件中使用方法。内存开销实际上是相同的,并且当与系统耦合时,它们应该非常容易实现并以有趣的方式组合。例如,系统只能向知道实现本身的实体/组件提供基本逻辑。如果您问我-这基本上是从ES和OOP那里获得的好处,根据本文的作者所说,这是无法完成的,但是对我来说,这似乎是一种好习惯。 这样想吧;游戏中有许多不同类型的可绘制对象。普通的旧图像,动画(update(),getCurrentFrame()等),这些原始类型的组合以及所有这些都可以简单地为draw()渲染系统提供一种方法,然后该方法无需关心实体的sprite是如何实现的,关于界面(绘制)和位置。然后,我只需要一个动画系统,该系统将调用与渲染无关的特定于动画的方法。 还有另一件事……关于存储组件,真的有数组的替代方法吗?除了实体类中的数组,我看不到要存储组件的其他地方。 也许这是一个更好的方法:将组件存储为实体的简单属性。例如,位置组件将粘贴到entity.position。 的唯一其他方法是有某种奇怪的查找表的内部系统中,引用不同的实体。但是,这似乎非常低效的,更复杂的比在实体只需存储组件来开发。

1
如何从实体组件系统游戏引擎中的cpu缓存中受益?
我经常阅读ECS游戏引擎文档,这是明智使用cpu缓存的良好架构。 但是我不知道如何从cpu缓存中受益。 如果将组件保存在连续内存中的数组(或池)中,则只有在顺序读取组件的情况下,才是使用cpu缓存BUT的好方法。 当我们使用系统时,它们需要实体列表,这些列表是具有特定类型组件的实体列表。 但是这些列表以随机的方式而不是顺序地提供了组件。 那么如何设计ECS以最大化缓存命中率呢? 编辑: 例如,物理系统需要具有RigidBody和Transform组件的实体的实体列表(有一个RigidBody池和一个Transform组件池)。 因此,其更新实体的循环将如下所示: for (Entity eid in entitiesList) { // Get rigid body component RigidBody *rigidBody = entityManager.getComponentFromEntity<RigidBody>(eid); // Get transform component Transform *transform = entityManager.getComponentFromEntity<Transform>(eid); // Do something with rigid body and transform component } 问题在于,entity1的RigidBody组件可以在其池的索引2处,而entity1的Tranform组件在其池的索引0处(因为某些实体可以具有某些组件,而其他组件不能具有其他组件,并且是因为添加/删除了实体/组件)。 因此,即使组件在内存中是连续的,也会随机读取它们,因此它将有更多的缓存未命中,不是吗? 除非有一种方法可以预取循环中的下一个组件?

3
组成重的OOP与纯实体组件系统?[关闭]
按照目前的情况,这个问题并不适合我们的问答形式。我们希望答案会得到事实,参考或专业知识的支持,但是这个问题可能会引起辩论,争论,民意调查或扩展讨论。如果您认为此问题可以解决并且可以重新提出,请访问帮助中心以获取指导。 7年前关闭。 我承认,我犯了过度使用甚至滥用继承的罪过。我上OOP课程时所做的第一个(文字)游戏项目远比“门”和“一扇门的房间”,“两扇门的房间”中的“锁门”和“解锁门”有关,以此类推。 通过最近我从事的(图形)游戏,我认为自己已经学到了教训,并限制了继承的使用。但是我注意到问题很快开始出现。我的根类开始越来越膨胀,而我的叶类中充满了重复的代码。 我以为我还是在做错事,在网上查询之后,我发现我并不是唯一遇到此问题的人。经过一些深入的研究,我最终才发现了实体系统(请参阅:googlefu) 当我开始阅读它时,我能够看到它能够多么清晰地解决我的带有组件的传统OOP层次结构所遇到的问题。这些是一读。当我偶然发现更多……“激进”的ES方法时,例如T-machine上的方法。 我开始不同意他们使用的方法。一个纯粹的组件系统似乎要么过大,要么不直观,这可能是OOP的优势。作者甚至说ES系统与OOP相反,尽管它可以在OOP中使用,但实际上不应该。我并不是说这是错误的,但是我只是觉得自己不想实施一个解决方案。 因此,对我来说,为了解决我在帖子开头所遇到的问题,而又不违背我的直觉,那就是仍然使用层次结构,但是它不会像我以前使用的那样是一个整体的层次结构,而是一种多片的(我找不到与单片相反的单词),它由几棵较小的树组成。 下面的示例说明了我的意思(这受我在《游戏引擎体系结构》第14章中找到的示例的启发)。 我会有一棵小树供车辆。根车辆类别将具有渲染组件,碰撞组件,位置组件等。 然后,坦克(车辆的子类)会从中继承这些组件,并被赋予自己的“加农炮”组件。 角色也是如此。角色将拥有自己的组件,然后让Player类继承它,并被赋予Input控制器,而其他敌人类将从Character类继承并被赋予AI控制器。 我真的看不到这种设计有任何问题。尽管没有使用纯的实体控制器系统,但是冒泡效果的问题和大根类的问题是通过使用多树层次结构解决的,繁重的代码复制叶子的问题也消失了,因为叶子没有有任何代码开始,只有组件。如果需要对叶级别进行更改,则就像更改单个组件一样简单,而不是将代码复制粘贴到任何地方。 当然,像我一样没有经验的人,当我刚开始使用单一层次结构,繁重的继承模型时,我没有看到任何问题,因此,如果我当前正在考虑实现的模型存在问题,我不会能够看到它。 您的意见? PS:我正在使用Java,因此无法使用多重继承来实现此功能,而不是使用常规组件。 PPS:组件间通信将通过相互链接相关的组件来完成。这将导致耦合,但是我认为这是一个不错的权衡。

3
与外部组件管理者一起组织实体系统?
我正在为自上而下的多人2D射击游戏设计游戏引擎,我希望该引擎可以合理地重用于其他自上而下的射击游戏。目前,我正在考虑应如何设计其中的实体系统。首先,我想到了这一点: 我有一个叫做EntityManager的类。它应该实现一种称为Update的方法,以及另一个称为Draw的方法。之所以将Logic和Rendering分开是因为,如果运行独立服务器,则可以省略Draw方法。 EntityManager拥有BaseEntity类型的对象列表。每个实体都拥有一个组件列表,例如EntityModel(实体的可绘制表示),EntityNetworkInterface和EntityPhysicalBody。 EntityManager还拥有一个组件管理器列表,例如EntityRenderManager,EntityNetworkManager和EntityPhysicsManager。每个组件管理器都保留对实体组件的引用。有多种原因将此代码移出实体自己的类,而改为集体进行。例如,我使用游戏的外部物理库Box2D。在Box2D中,首先将实体和形状添加到一个世界(在本例中为EntityPhysicsManager拥有),然后添加碰撞回调(将在我的系统中分派给实体对象本身)。然后,您运行一个函数来模拟系统中的所有内容。我发现很难找到比在这样的外部组件管理器中更好的解决方案。 实体创建是这样完成的:EntityManager实现方法RegisterEntity(entityClass,factory),该方法注册如何创建该类的实体。它还实现了CreateEntity(entityClass)方法,该方法将返回BaseEntity类型的对象。 现在出现了我的问题:如何将对组件的引用注册到组件管理器?我不知道如何从工厂/关闭处引用组件管理器。

1
如何在实体/组件系统中处理物料
我的E / C实现是一种基本的实现,其中实体只是ID,组件是数据,系统根据数据进行操作。现在,我在处理对象材质和渲染时遇到了麻烦。对于简单的objetc,我有一个ModelComponent与绑定的RenderSystem,ModelComponent具有渲染系统使用的顶点缓冲区ID。一个简单的对象MaterialComponent可能具有颜色或镜面反射强度等,但是我希望它具有足够的灵活性,以允许多个渲染过程和一般的“效果”,而这些效果不如的简单变量那么容易MaterialComponent。 为了解决这些问题,我想出了两种解决方案: 1-超通用材料组件 像这样: struct Material : public Component { ShaderData* shader; std::vector<std::pair<std::string, boost::any>> uniforms; [...] }; 在渲染系统中,我将循环并将制服传递给着色器。我想这会很慢,但对于我的目的来说足够快了。 2-另一层抽象,MaterialData 有一个包装特定材料的类,该类可以由任何专门的材料继承,基类将具有类似的东西,void set_shader_constants(ShaderData* d)但是实现取决于每个类,并且MaterialComponent将具有一个指向MaterialData对象的指针。 我不确定我会选择哪种方法,但是这些方法都不涉及多次通过或其他复杂的渲染技术。 关于如何做到这一点的任何想法?

2
实体系统中的碰撞检测和响应
ES带来更多乐趣... 目前,我有一些系统: 渲染器(Renderable属性,Transform属性) 运动(可移动属性,变换属性,可渲染属性(用于边界框等)) 输入(InputReceiver属性) 等等 我要添加碰撞检测。我的第一个想法是添加一个执行碰撞的新系统。这对我来说很有意义,以防止这种分离的Motion系统,因为不是所有的东西,移动,或者动画不一定参与碰撞检测-摄像机,雾等-但似乎Collision和Motion相互依存。 当Motion移动实体,变换需要与被验证Collision,且移动或者取消,或调节(弹跳,停在一个壁等)。 一种替代方法是创建一个Collidable属性,该属性维护对碰撞对象(kd-tree,八叉树等)的引用,该引用在可以相互碰撞的实体之间共享。然后,Motion系统将检查该属性,并使用它来检查或调整移动。 从代码角度来看,这是一个可以接受的解决方案。但是,从ECS架构的角度来看,似乎是将逻辑推入Motion系统中,而该逻辑不适用于具有Movable属性的所有实体。 我还可以在Movable属性中存储一个运动矢量,并使Collider系统Transform根据需要进行调整,但这将涉及在Motion和之间复制功能Collider,或者从Collider到Motion进行回调,并提供一些有关碰撞位置和曲面数据的反弹/反射等信息。 。 这可能属于“特殊情况破解”的标题,但是我想从以前处理过此问题的人员那里得到一些输入而无需创建大量的边缘案例代码。 问题 当运动系统和碰撞系统似乎需要彼此了解时,如何避免它们之间的紧密耦合?

2
实体系统框架中的图块地图?
我一直在阅读实体系统框架,尤其是Artemis。我正在尝试确定是否适合我。我严格从事基于图块的2d像素艺术游戏,而且我认为它们永远不会占用大量资源。过去,我一直使用标准OOP进行大量继承。 我现在对实体系统框架的理解(我不确定是否完全掌握): 实体不过是ID的 组件不过是添加到实体组件池中的哑数据 系统是附加到世界的更新功能,用于处理与系统组件签名匹配的每个实体 如果我的理解是正确的,那么在概念上很难在此框架中添加图块图和AI行为树。我将来会问有关AI的问题。 是否应在此框架中内置图块地图?还是应该将其分开放置以使其易于使用tilemap编辑器生成? 如果应该将tilemap内置到此框架中,那么每个tile都是不同的实体吗?和tilemap一个系统?还是tilemap本身就是继承了继承关系的单个实体? 如果tilemap足够精细,那么与外部tilemap碰撞检测实体的最佳方法是什么? 我了解我列出的多个选项可能是正确的,但是如果有人过去这样做过,他们也许可以使我感到困惑。也许还有我没有想到的另一种选择? 谢谢。

3
基于组件的设计中的输入处理
我知道这个问题已经问过几次了,但是我仍然不确定如何在基于组件的引擎中实现输入处理。 我使用的基于组件的设计基于T = Machine的博客系列以及Artemis(其中实体只是id)。 实现输入处理时,我有三个主要想法: 输入组件将保存其感兴趣的事件。输入系统会将键和鼠标事件转换为游戏事件,并使用输入组件在实体之间循环,如果它们对事件感兴趣,则输入系统将采取适当的措施。该动作将被硬编码到输入系统。 没有输入组件。您可以将具有特定事件的实体注册到输入系统。输入系统然后将消息(具有实体ID和事件类型)发送给其他系统,以便它们可以采取适当的措施。或与第一种情况一样,这些动作将被硬编码到输入系统中。 与第一种方法类似,该组件将包含事件到函数(即std::map<std::function>)的映射,而不是对输入系统的动作进行硬编码,输入将调用该函数。这具有能够将同一事件耦合到不同动作的附加效果。 您会建议上述任何一种方法,还是对我实现灵活的输入处理系统有任何建议?另外,我还不熟悉多线程,但是也欢迎任何使实现线程友好的建议。 注意:我希望实现能够满足的另一个要求是,我能够将相同的输入传递给许多实体,例如,同时移动相机实体和播放器。

3
将相同组件集的实体分组到线性内存中
我们从基本的系统-组件-实体方法开始。 让我们创建组合(源自长期本文章)仅仅是出于对信息类型的组件。它是在运行时动态完成的,就像我们将组件一个接一个地添加/删除到实体中一样,但是让我们更精确地命名它是因为它仅涉及类型信息。 然后,我们构造为每个实体指定集合的实体。创建实体后,它的组合是不可变的,这意味着我们无法直接对其进行修改,但是仍然可以获得现有实体对本地副本的签名(以及内容),对其进行适当的更改,然后创建一个新实体它的。 现在是关键概念:每创建一个实体,就会将其分配给一个名为assemblage bucket的对象,这意味着具有相同签名的所有实体都将位于同一容器中(例如,在std :: vector中)。 现在,系统只是遍历他们感兴趣的每个环节并完成工作。 这种方法有一些优点: 组件存储在几个(精确地:存储桶数)连续的内存块中-这提高了内存友好性,并且更容易转储整个游戏状态 系统以线性方式处理组件,这意味着改进了缓存一致性-再见字典和随机存储器跳转 创建新实体就像将组合映射到存储桶并将所需组件推回其向量一样容易 删除一个实体就像调用std :: move一样容易,将最后一个元素与删除的元素交换,因为此时顺序并不重要 如果我们有很多具有完全不同的签名的实体,那么缓存一致性的好处就会减少,但是我认为大多数应用程序都不会发生这种情况。 重新分配向量后,指针失效也存在问题-可以通过引入以下结构来解决: struct assemblage_bucket { struct entity_watcher { assemblage_bucket* owner; entity_id real_index_in_vector; }; std::unordered_map<entity_id, std::vector<entity_watcher*>> subscribers; //... }; 因此,只要出于游戏逻辑中的某种原因,我们想要跟踪一个新创建的实体,就在存储桶中注册一个entity_watcher,并且一旦在删除期间必须将该实体std :: move移开,我们便会查找其观察者并进行更新他们real_index_in_vector的新价值。在大多数情况下,这对每个实体删除都仅施加了一个字典查找。 这种方法还有其他缺点吗? 尽管很明显,为什么没有提到解决方案? 编辑:我正在编辑问题以“回答答案”,因为评论不足。 您将失去可插拔组件的动态特性,该特性是为摆脱静态类构造而专门创建的。 我不。也许我没有足够清楚地解释它: auto signature = world.get_signature(entity_id); // this would just return …

1
实体组件系统-如何实现对象的变换?
在为引擎设计实体组件系统时,我在存储和检索特定类型的组件方面遇到了一些麻烦。 首先,让我澄清一下我将在此问题中使用的一些术语: 我将“ 组件 ”称为一种数据结构,用于存储特定系统的相关数据。 我称“ 系统 ”为方法和数据结构的聚合,它利用组件来更新与用户的游戏状态/界面。 “ 实体 ”基本上只是一个ID,用于检索游戏逻辑中的特定组件并修改其数据。 每个系统都拥有其组件类型(例如Physics-> PhysicsComponent,AI-> AIComponent,Rendering-> RenderingComponent)的(ID映射)数组,因此它可以有效地遍历数据。 但是,并非所有组件都专门由系统拥有。例如,“变换”组件存储对象的位置,旋转和比例。它是实体最重要的部分之一(Unity甚至使它成为强制性的),因为它被许多系统使用,例如物理,AI,渲染等。 这几乎就是我面临的问题。由于其他许多系统都使用了Transform,因此我应该如何为每个组件检索一个?我看到的一种可能的解决方案是使每个组件存储自己的实体ID。这样检索任何组件很容易,但是效率不高,而且它与组件的概念相反,后者是孤立的独立数据束,不知道其他任何组件。 是否有解决此问题的正确方法?变形应该成为一个组成部分吗?

3
有效分离读取/计算/写入步骤,以便在实体/组件系统中对实体进行并发处理
设定 我有一个实体组件体系结构,其中实体可以具有一组属性(它们是纯数据,没有任何行为),并且存在运行运行于该数据上的实体逻辑的系统。本质上,用某种伪代码: Entity { id; map<id_type, Attribute> attributes; } System { update(); vector<Entity> entities; } 只是以恒定的速率沿所有实体移动的系统可能是 MovementSystem extends System { update() { for each entity in entities position = entity.attributes["position"]; position += vec3(1,1,1); } } 本质上,我正在尝试尽可能高效地并行化update()。这可以通过并行运行整个系统来完成,也可以通过为一个系统的每个update()提供几个组件来实现,以便不同的线程可以执行同一系统的更新,但对于在该系统中注册的实体的不同子集而言。 问题 对于所示的MovementSystem,并行化是微不足道的。由于实体彼此不依赖,并且不修改共享数据,因此我们可以并行移动所有实体。 但是,这些系统有时要求实体彼此交互(从/向彼此读/写数据),有时在同一系统内,但经常在彼此依赖的不同系统之间。 例如,在物理系统中,有时实体可能会彼此交互。两个对象发生碰撞,它们的位置,速度和其他属性会从它们读取,更新,然后将更新后的属性写回到两个实体。 并且在引擎中的渲染系统可以开始渲染实体之前,它必须等待其他系统完成执行,以确保所有相关属性都是它们所需要的。 如果我们试图盲目地并行化它,这将导致经典的竞争条件,其中不同的系统可能同时读取和修改数据。 理想情况下,将存在一种解决方案,其中所有系统都可以从其希望的任何实体读取数据,而不必担心其他系统同时修改相同的数据,而无需程序员关心正确地排序执行和并行化。这些系统是手动的(有时甚至是不可能的)。 在基本实现中,这可以通过将所有数据读写放在关键部分中(用互斥锁保护它们)来实现。但是,这会导致大量的运行时开销,并且可能不适用于对性能敏感的应用程序。 解? 在我看来,可能的解决方案是将数据的读取/更新和写入分开的系统,以便在一个昂贵的阶段,系统仅读取数据并计算其需要计算的内容,以某种方式缓存结果,然后将所有内容写入将更改后的数据通过单独的写入传递回目标实体。所有系统都将以数据在帧开始时的状态对数据进行操作,然后在帧结束之前,在所有系统完成更新后,会发生序列化写入过程,其中所有不同的缓存结果系统遍历并写回到目标实体。 这是基于(可能错了?)的思想,即简单的并行化胜利可能足以超过结果缓存和写入过程的成本(在运行时性能以及代码开销方面)。 问题 如何实现这样的系统以获得最佳性能?这样的系统的实现细节是什么?要使用此解决方案的实体组件系统的先决条件是什么?

2
在基于组件的实体系统中处理脚本化和“本机”组件
我目前正在尝试实现一个基于组件的实体系统,该实体基本上只是一个ID和一些将一组组件捆绑在一起以形成游戏对象的辅助方法。其中的一些目标包括: 组件仅包含状态(例如位置,健康状况,弹药计数)=>逻辑进入“系统”,该系统处理这些组件及其状态(例如PhysicsSystem,RenderSystem等) 我想用纯C#和脚本(Lua)实现组件和系统。基本上,我希望能够直接在Lua中定义全新的组件和系统,而不必重新编译C#源代码。 现在,我正在寻找有关如何高效且一致地处理此问题的想法,因此,例如,无需使用其他语法来访问C#组件或Lua组件。 我当前的方法是使用常规的公共属性实现C#组件,这些属性可能装饰有一些属性,这些属性可以告诉编辑器默认值和内容。然后,我将得到一个C#类“ ScriptComponent”,该类仅在内部包装Lua表,该表由脚本创建并保存该特定组件类型的所有状态。我真的不希望从C#端访问该状态,因为我在编译时不知道可以使用哪些ScriptComponents和哪些属性。尽管如此,编辑器仍需要访问它,但是像下面这样的简单界面就足够了: public ScriptComponent : IComponent { public T GetProperty<T>(string propertyName) {..} public void SetProperty<T>(string propertyName, T value) {..} } 这将简单地访问Lua表并从Lua设置和检索那些属性,并且也可以轻松地将其包含在纯C#组件中(但随后可以通过反射或其他方式使用常规C#属性)。这只能在编辑器中使用,不能用常规的游戏代码使用,因此性能在这里并不重要。这将有必要生成或手写一些组件描述,以记录某些组件类型实际提供的属性,但这不是一个大问题,并且可以实现足够的自动化。 但是,如何从Lua端访问组件?如果我调用类似的东西,getComponentsFromEntity(ENTITY_ID)我可能只会获得一堆本地C#组件(包括“ ScriptComponent”)作为用户数据。从包装的Lua表访问值将导致我调用该GetProperty<T>(..)方法,而不是像其他C#组件那样直接访问属性。 也许编写一个getComponentsFromEntity()只能从Lua调用的特殊方法,该方法会将所有本机C#组件作为用户数据返回,但“ ScriptComponent”除外,它将返回包装后的表。但是会有其他与组件相关的方法,我真的不想复制所有这些方法,无论是从C#代码还是从Lua脚本调用。 最终目标将是处理所有类型的组件相同,而在本机组件和Lua组件之间(尤其是从Lua方面)不区分特殊情况的语法。例如,我希望能够编写如下的Lua脚本: entity = getEntity(1); nativeComponent = getComponent(entity, "SomeNativeComponent") scriptComponent = getComponent(entity, "SomeScriptComponent") nativeComponent.NativeProperty = 5 scriptComponent.ScriptedProperty = 3 该脚本不必关心它实际上获得了哪种组件,我想使用与C#端相同的方法来检索,添加或删除组件。 也许有一些将脚本与实体系统集成在一起的示例实现?

5
为什么将实体配置放在脚本之外?
我见过很多在脚本文件中定义实体组件的游戏,但是当它们配置每个实体并指定其具有的组件时,它们会使用其他文件格式(例如XML)。他们为什么这样做? 我主要是想看看其他人对此的依据是什么。我还在脚本之外配置了我的实体(尽管我选择了JSON而不是XML)。我这样做的原因是使我更容易实现保存游戏,还因为我认为这种配置可以更好地组织为XML或JSON之类的东西。 // @ 克里斯托弗·拉森的答案: 太长了,无法发表评论 我担心您可能偏离了问题的主题。您所描述的问题与基于层次的实体更相关;在我提到的问题中,我提到我在谈论基于组件的实体。 这是我想问的一个例子。以下是配置实体的两种替代方法:通过脚本和通过外部JSON文件。我的问题是,为什么有那么多人喜欢在脚本之外配置实体? 基础实体类: class Entity: def __init__(self, name): pass def addComponent(self, comp): pass 脚本方法: orc = Entity('Orc') orc.addComponent(PositionComponent(3.4, 7.9)) JSON方法: { "name" : "Orc", "components": { "PositionComponent": { "x" : 3.4, "y" : 7.9 } } } 我已经说明了使用这种方法的原因,包括技术上和组织上的原因。我想知道为什么这么多其他人(根据我所看到的)使用它。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.