< 返回版块

sstudioer 发表于 2021-05-23 23:06

打算用rust写项目, 最大的问题就是链表, 彼此引用问题.

比如房间型游戏服务器, 每个房间都是局独立游戏. 每个游戏game有自己的节点, 怪物, 碰撞组件, 各种模块.... 这些部件为了方便使用和模块化都会引用*game, 而game中又会以数组方式存储这些部件的指针.

游戏即是这样, 想要模块化, 想要高度设计, 想要逻辑清晰, 互相引用指针是必须大量存在的. 除非: 存储部件id, 每次通过id获取对应游戏的对应部件, 这浪费性能,不利于使用.

问: rust是否不适合实际项目? 虽然rust是新式语言, 但是综合成本是否比C++更大?

附加: 目前使用golang, 为了降低垃圾回收成本, 使用uintptr 或 arr[id]标识, 写的很不优雅; c++ module编译器都未实现, 代码没法写;

评论区

写评论
Pikachu 2021-05-26 03:03

不管你用哪种方式写游戏,建议你先上手写了之后再来判断,很多问题都是在写的过程中浮现出来的。比如你最开始的例子,多房间的游戏服务器。你可能觉得每个房间里的components都不多,但ECS是允许你一次性遍历完所有房间的同种类components的。在这种情况下,get component带来的一次性overhead可以忽略不计。

ECS的核心思路是data oriented,它本来就是要以增大add component的开销为代价,来换取get component的遍历速度。它还有其他的一些优势:

  • 编程思路的转变。每个system只对自己所需的一部分components做处理,可以降低耦合。
  • 因为不同类型的components存储在内存的不同区域,ECS可以很容易地并行。只要两个system所处理的components没有重叠,他们就可以并行,不需要mutex的开销。
  • 内存连续便于SIMD优化(依赖于具体实现)。

ECS当然不是银弹。如果你没有大量components需要遍历的话,AoS (array of struct) 确实也是一种思路。但在这种情况下,你也并不需要太担心遍历的开销,你甚至可以用hashmap存储你的所有components.

但无论在哪种情况下,如果你真的想要一个低耦合的游戏,指针都不是你所必须的,最起码borrow不是。你可以看看其他的指针类型,比如unsafe的ptr,比如weak、Rc,比如自己实现一个integer handle来模拟指针(并不会有什么开销,汇编层面会被优化掉的)。

--
👇
sstudioer: thanks;

不考虑 cache miss: ecs查找组件, 需要寻找的过程, get_componnet() 是个递归判断. 考虑 cache miss: ecs只有在频繁递归component的情况下才有优势.

C#: 这类垃圾回收语言用 ECS, 本身降低垃圾回收的效果就是显著的, 所以unity必须使用ECS; C++ RUST: 用ECS就很费解;

--
👇
Pikachu: 其实ECS的思路是这样的。

作者 sstudioer 2021-05-25 10:32

thanks;

不考虑 cache miss: ecs查找组件, 需要寻找的过程, get_componnet() 是个递归判断. 考虑 cache miss: ecs只有在频繁递归component的情况下才有优势.

C#: 这类垃圾回收语言用 ECS, 本身降低垃圾回收的效果就是显著的, 所以unity必须使用ECS; C++ RUST: 用ECS就很费解;

--
👇
Pikachu: 其实ECS的思路是这样的。

Pikachu 2021-05-24 14:56

其实ECS的思路是这样的。

fn getPhysicsComponent() -> PhysicsComponent{
 for comp in position_compnents{
  //
 }
 for comp in physics_components{   
  //
 }
}

每种component都是单独的数组。具体细节你可能得读代码了,涉及到稀疏数组、archetype等一些用于优化搜索速度的设计。

补充一个关于ECS中archetype的blog: https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9

--
👇
sstudioer

作者 sstudioer 2021-05-24 11:48

对于esc不能理解的是: 比如要使用entity的 physics组件, 则需要

fn getPhysicsComponent() -> PhysicsComponent{
 for comp in components{   // 这个循环判断不影响性能吗? 
   if comp is physics{
     return comp
   }
 }
}

--
👇
Pikachu

Pikachu 2021-05-24 04:10

再补充一个链接: are we game yet?

这个网站汇总了rust中游戏相关的生态系统、已经实现的游戏和参考资料。如果你需要做游戏的话,应该是一个比较有用的参考网站。

Pikachu 2021-05-24 04:02

如果仅限游戏相关的话,我觉得你可以了解一下ECS (entity-component-system)。我在这里先粗糙地描述一下,如果想要深入了解,可以看最后面的链接。

不妨先只考虑一个房间。你的方法是,各种组件在game这个全局变量中以指针数组形式存在,指针指向的内存位置则是分散且不确定的。 而ECS的思路,则是一张表。每一种组件在是表中独立的一列,如位置、速度等。每一个实体则是表中独立的一行。每一列组件可以粗糙地认为是以数组的形式存在,通过index进行查找。 如果不考虑CPU cache miss的情况,这两种方式的效率应该是相近的,都是两次寻址。而如果考虑cache miss的话,ECS的方式则有明显优势,因为同一种组件在内存中是连续的,所以你可以很快速地遍历同一种组件。

回到你的问题上来,

游戏即是这样, 想要模块化, 想要高度设计, 想要逻辑清晰, 互相引用指针是必须大量存在的. 除非: 存储部件id, 每次通过id获取对应游戏的对应部件, 这浪费性能,不利于使用.

这段话基本上是错误的。互相引用指针并非必须存在,存储数组index也并不会浪费性能。

ECS并不是一个新鲜的概念,也确实已经被广泛用于各种游戏中(galgame除外)。我上面的描述并没有非常精确,只涉及了直觉上的理解,你应该能在知乎等地方找到很多更详细的文章介绍,我这里先贴几个链接以供查阅。 相关链接

  1. Unity ECS:https://learn.unity.com/tutorial/entity-component-system#
  2. Bevy (A data-driven game engine built in Rust): https://bevyengine.org/learn/book/getting-started/ecs/
  3. Wikipedia: https://en.m.wikipedia.org/wiki/Entity_component_system
johnmave126 2021-05-24 03:06

如果部件是一个树状/层次结构,可以考虑向下用Rc,向上用Weak

我觉得rust的优势在于需要很显式地考虑一个部件的所属,到底一个东西由谁管理由谁清理。然后就是高性能并行计算的时候避免出现竞争。

1 共 7 条评论, 1 页