一帧模拟上万条鱼:DOTS版Boids鱼群仿真的空间哈希与Job并行实现拆解
2026/9/20 21:09:03 网站建设 项目流程

一帧模拟上万条鱼:DOTS版Boids鱼群仿真的空间哈希与Job并行实现拆解

【免费下载链接】EntityComponentSystemSamples项目地址: https://gitcode.com/GitHub_Trending/en/EntityComponentSystemSamples

实体一多帧率就崩,这是做大规模群体模拟最先撞上的墙。EntityComponentSystemSamples 仓库里的 Boids 鱼群示例给出了一种纯 ECS 解法:空间哈希网格加三层 Job 并行流水线,把数万条鱼的群体行为压在一帧里算完,全部逻辑装在一个BoidSystem.cs里。

原理速览:群游到底靠什么

Boids 的核心其实只有几条规则,每条个体只盯着邻居,群体行为就自己"涌现"出来了:

概念一句话作用
分离 Separation离得太近的邻居推开,防止撞成一坨
对齐 Alignment顺着邻居的平均朝向调整自己的方向
凝聚 Cohesion往邻居的中心靠拢
目标 / 障碍物本示例扩展:追目标鱼、躲鲨鱼

难点不在规则,在规模:每条鱼逐个找邻居就是 O(n²)。这个示例的三板斧——空间哈希定位邻居、按格子预聚合数据、Job 并行算行为——就是冲着把 O(n²) 打下来的。

核心实现拆解

数据怎么摆:整群鱼塞进同一片连续内存

OOP 里每条鱼一个对象,指针满天飞,缓存和并行都难受。这里把Boid定义为ISharedComponentData(参数在 BoidAuthoring.cs 里配),相同参数的一组鱼自动归成一个变体、连续存放。BoidSchoolSpawnSystem.cs 用EntityManager.Instantiate批量实例化鱼群,再拿一个并行 Job 把初始位置随机撒到半径范围内。数据摆整齐了,后面按变体遍历才能既省缓存又方便切分并行。

空间怎么划:让每条鱼只盯着自己的格子

每条鱼按CellRadius把坐标量化成格子坐标,再哈希进桶:

var hash = (int)math.hash(new int3(math.floor(localToWorld.Position * InverseBoidCellRadius))); ParallelHashMap.Add(hash, entityIndexInQuery);

同格子的鱼落在NativeParallelMultiHashMap的同一个桶里。找邻居从"扫全世界"变成"翻自己这个桶",平均复杂度从 O(n²) 降到 O(n)。

邻居数据怎么省:每格只聚合一次

同格子的鱼共享朝向均值、位置均值,没必要每条各算一遍。MergeCellsJob 把同哈希桶的数据合并:

cellCount[cellIndex] += 1; cellAlignment[cellIndex] += cellAlignment[cellIndex]; cellSeparation[cellIndex] += cellSeparation[cellIndex]; cellIndices[index] = cellIndex;

顺手把每格离自己最近的目标和障碍物也记下来。之后每条鱼查一次数组就拿到整格聚合值,个体级计算被压成几次向量运算。

行为怎么算:三个权重定方向

SteerBoidJob里用共享权重把聚合值变成转向:

var alignmentResult = CurrentBoidVariant.AlignmentWeight * math.normalizesafe((alignment / neighborCount) - forward); var separationResult = CurrentBoidVariant.SeparationWeight * math.normalizesafe((currentPosition * neighborCount) - separation); var targetHeading = CurrentBoidVariant.TargetWeight * math.normalizesafe(nearestTargetPosition - currentPosition);

三向量加权求和后,如果最近障碍物比ObstacleAversionDistance更近,就整体替换成规避方向,再回写LocalToWorld的位置与朝向。整个 Job 带[BurstCompile],由 Job System 切到多核跑,鱼群规模上去时帧时间基本不涨。

性能账本:优化到底省在哪

维度暴力 OOP 写法本示例做法
邻居查询O(n²) 全量两两比较空间哈希,平均 O(n)
每帧行为计算主线程单核顺序执行Job System 多核并行 + Burst 向量化
数据布局对象指针分散按变体连续排布,批量处理
实体规模几百条就开始掉帧万级实体仍流畅(仓库定位为大规模模拟示例,未公布具体 FPS,实测以 Profiler 为准)

迁移应用:这套规则换个皮还能用在哪

  • 足球游戏:球员跑位与阵型保持,本质就是"对齐邻居 + 各自追球"
  • 策略游戏:兵团集群移动,用分离规则避免单位叠成一团
  • 应急演练:建筑内人群疏散流,分离加障碍物规避直接对应
  • 虚拟水族馆、动物园:锦鲤池、鸟群的写实群体效果

三步跑起来

先收藏这几个关键文件:

  • BoidSystem.cs:三层 Job 流水线主系统
  • BoidAuthoring.cs:全部六个参数的默认值
  • BoidSchoolSpawnSystem.cs:鱼群批量生成
  • SampledAnimationClipPlaybackSystem.cs:目标鱼与鲨鱼的轨迹动画
  • README.md:示例说明

运行步骤:

  1. 克隆仓库:git clone https://gitcode.com/GitHub_Trending/en/EntityComponentSystemSamples
  2. 用 Unity Hub 打开其中的EntitiesSamples项目
  3. 打开场景EntitiesSamples/Assets/Boids/Boids.unity,进 Play 模式,鱼群生成后就能看到群体追逐红鱼、绕开鲨鱼

运行时可以在 ECS 窗口里观察 Archetype 与实体数据,验证变体批处理是否按预期生效。

参数调优速查表

默认值直接来自 BoidAuthoring.cs:

参数作用项目默认值
CellRadius空间哈希格子半径,决定"邻居"范围8.0
SeparationWeight分离权重,越大越排斥拥挤1.0
AlignmentWeight对齐权重,越大越容易排成同向鱼群1.0
TargetWeight目标追逐权重,越大越黏目标鱼2.0
ObstacleAversionDistance障碍物触发规避的距离30.0
MoveSpeed每条鱼的移动速度25.0

调参手感:CellRadius调大会让鱼"近视"变"远视",群更散但计算更省;SeparationWeight拉高后鱼群瞬间散开,压回 1.0 以下则容易出现密集巡游。

把这份示例的提速拆开看,其实是三件事:数据连续摆放让 CPU 缓存不浪费,Job 把计算切到多核,空间哈希把要比对的量砍掉一个量级。想继续折腾的话,可以加一套行为状态机(觅食、迁徙、受惊三种模式切换),给鱼区分出带头者和跟随者,或者用强化学习自动搜上面那组权重。更多 ECS 入门与进阶内容在 官方文档目录 里。

【免费下载链接】EntityComponentSystemSamples项目地址: https://gitcode.com/GitHub_Trending/en/EntityComponentSystemSamples

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询