英文:
ECS with Go - circular imports
问题
我正在探索Go语言和实体-组件系统(Entity-Component-Systems,ECS)。我理解ECS的工作原理,并且我正在尝试复制似乎是ECS的首选文档,即http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/。
对于性能,该文档建议使用每种组件类型的静态数组,而不是组件接口的数组(指针数组)。在Go语言中,这样做的问题是循环导入。
我有一个名为ecs的包,其中包含Entity、Component和System类型/接口的定义,以及一个EntityManager。另一个名为ecs/components的包包含各种组件。显然,ecs/components包依赖于ecs包。但是,为了在EntityManager中声明特定组件的数组,ecs包将依赖于ecs/components包,从而创建了循环导入。
有没有办法避免这个问题?我知道通常高级系统不应该依赖于低级系统。我还想指出,对于我的目的来说,使用指针数组可能已经足够快了,但我对可能的解决方法(供将来参考)感兴趣。
谢谢你的帮助!
英文:
I'm exploring both Go and Entity-Component-Systems. I understand how ECS works, and I'm trying to replicate what seems to be the go-to document of ECS, namely http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/
For performance, the document recommends to use static arrays of every component type. That is, not arrays of component interfaces (arrays of pointers). The problem with this in Go is circular imports.
I have one package, ecs, which contains the definitions for Entity, Component and System types/interfaces as well as an EntityManager. Another package, ecs/components, contains the various components. Obviously, the ecs/components package depends on ecs. But, to declare arrays of specific components in EntityManager, ecs would depend on ecs/components, therefore creating a circular import.
Is there any way of avoiding this? I am aware that normally a high level system should not depend on lower systems. I'm also want to point out that using an array of pointers is probably fast enough for my purposes, but I'm interested in possible workarounds (for future reference)
Thank you for your help!
答案1
得分: 5
为了性能,该文档建议使用每个组件类型的静态数组。
首先,我要说一下,我可能看漏了,但我使用了Ctrl+F并多次阅读了该文档,没有看到任何相关内容。(当然,通过使用静态数组可以进行一些优化,例如避免缓存未命中,但我对其是否能够抵消文书工作量表示怀疑)。
对于你首先提出的确切问题,有一个简单的答案,即“.”导入。任何具有类似于import . "some/other/package"
的导入语句的包都将将该包的内容视为自己的内容,忽略循环依赖关系。不要这样做。
不幸的是,如果不合并包,你将无法做到这一点(我是指不使用接口)。不过,不要担心。你发布的文章在“实现细节”部分明确指出了这一点。
给每个组件一个共同的接口意味着从具有虚函数的基类派生。这会引入一些额外的开销。不要因此而对这个想法产生反感,因为与对象简化带来的节省相比,额外的开销很小。
它明确告诉你要使用接口(好吧,C++虚继承,但足够接近了)。这是可以接受的,也是必要的。特别是如果你想要两个略有不同的AI组件之类的东西,那么接口就是救星。
英文:
> For performance, the document recommends to use static arrays of every
> component type.
I'm just going to start off saying that I may be blind, but I ctrl+f'd and read that document multiple times and didn't see anything close to that. (Certainly some optimizations could be made this way with regards to things like avoiding cache misses, but I'm dubious it in any way outweighs the clerical overhead).
There's the easy answer to the exact question you asked first, the .
import. Any package with an import statement like import . "some/other/package"
will treat that package's contents as its own, ignoring circular dependencies. Don't do this.
Unfortunately, without merging the packages, you won't be able to do this (without using interfaces, I mean). Don't fear, though. The article you posted explicitly says this under "implementation details".
> Giving each component a common interface means deriving from a base
> class with virtual functions. This introduces some additional
> overhead. Do not let this turn you against the idea, as the additional
> overhead is small, compared to the savings due to simplification of
> objects.
It's outright telling you to use interfaces (okay, C++ virtual inheritance, but close enough). It's okay, it's necessary. Especially if you want two slightly different AI components or something, it's a godsend then.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论