估计阅读时长: 7 分钟

Boids算法(也称鸟群/鱼群算法)是Craig Reynolds于1986年提出的群体行为模拟模型,通过三条局部规则模拟鸟类、鱼群等生物群体的自组织运动。在Boids算法中,整个过程通过个体(称为“boid”)的局部交互实现全局有序行为,无需中央控制。每条规则计算个体与邻居的相互作用力,最终合力决定运动方向。Boids算法的精髓在于用局部规则涌现全局智能,其简洁性、可扩展性使其成为连接生物行为与工程控制的桥梁。从《蝙蝠侠》的蝙蝠群到无人机编队表演,从游戏生态到交通优化,Boids持续证明:自然界的简单规则,足以驱动复杂系统的有序演化。

算法原理讲解

Boids算法中,其精髓在于无中央控制,而是依靠个体仅感知局部邻居(如半径内的其他boid),全局行为通过简单规则叠加自然涌现,无需全局路径规划。整个算法可以使用三部分来描述:

1.分离(Separation)

这部分的代码主要是为了避免碰撞,维持个体间距。算法代码主要是按照排斥力原则来进行计算,例如个体排斥过近的邻居,排斥力与距离成反比(距离越小,排斥越强)。

Private Function Avoid(boid As Boid, distance As Double, power As Double) As (Double, Double)
    ' point away as boids get close
    Dim neighbors = grid.SpatialLookup(boid, radius).Where(Function(x) x.GetDistance(boid) < distance)
    Dim sumClosenessX As Double = Nothing, sumClosenessY As Double = Nothing

    For Each neighbor As Boid In neighbors
        Dim closeness = distance - boid.GetDistance(neighbor)
        sumClosenessX += (boid.x - neighbor.x) * closeness
        sumClosenessY += (boid.y - neighbor.y) * closeness
    Next
    Return (sumClosenessX * power, sumClosenessY * power)
End Function

2.对齐(Alignment)

这部分的代码是为了实现群体运动方向同步。代码中主要是通过匹配调整个体调整速度方向,匹配邻居的平均运动方向。

Private Function Align(boid As Boid, neighbors As Boid(), power As Double) As (Double, Double)
    ' point toward the center of the flock (mean flock boid position)
    ' Dim neighbors = grid.SpatialLookup(boid, radius).Where(Function(x) x.GetDistance(boid) < distance).ToArray
    Dim meanXvel As Double = neighbors.Sum(Function(x) x.Xvel) / neighbors.Count()
    Dim meanYvel As Double = neighbors.Sum(Function(x) x.Yvel) / neighbors.Count()
    Dim dXvel = meanXvel - boid.Xvel
    Dim dYvel = meanYvel - boid.Yvel
    Return (dXvel * power, dYvel * power)
End Function

3.聚合(Cohesion)

最后我们需要通过代码来维持群体聚集,防止分散。主要是通过吸引力算法来实现个体向邻居的中心位置(质心)靠拢。

Private Function Flock(boid As Boid, neighbors As Boid(), power As Double) As (Double, Double)
    ' point toward the center of the flock (mean flock boid position)
    Dim meanX As Double = neighbors.Sum(Function(x) x.x) / neighbors.Count()
    Dim meanY As Double = neighbors.Sum(Function(x) x.y) / neighbors.Count()
    Dim deltaCenterX = meanX - boid.x
    Dim deltaCenterY = meanY - boid.y
    Return (deltaCenterX * power, deltaCenterY * power)
End Function

GDI+渲染

在拥有了上面的模拟算法代码之后,我们就可以计算出每一个Boid的速度方向,通过循环进行迭代计算更新位置,即可实现整个模拟计算的逻辑了。在每一次完成了位置的更新后,我们可以通过对应的绘图代码来进行图像的渲染更新。在这里,我们将每一个Boid的速度向量提取出来,按照热图的渲染规则进行颜色索引的计算,通过特定的颜色版对计算出来的每一个颜色索引都赋予对应的热图颜色,就可以得到最终的粒子模拟计算渲染结果了。

Public Module SDRender

    Dim colors As Color()
    Dim n As Integer = 30

    Sub New()
        colors = Designer.GetColors(ScalerPalette.viridis.Description, n)
    End Sub

    Public Function RenderField(field As Field) As Bitmap
        Dim bmp As Bitmap = New Bitmap(CInt(field.Width), CInt(field.Height))
        Dim max As Double = field.MaxSpeed

        Using gfx = Graphics.FromImage(bmp)
            gfx.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
            gfx.Clear(Color.Black)

            Dim len As Integer = field.Entity.Count

            For i = 0 To len - 1
                Dim boid As Boid = field(i)

                If i < field.PredatorCount Then
                    RenderShape.RenderBoid(gfx, boid.x, boid.y, boid.GetAngle, Color.White)
                Else
                    Dim lv As Integer = ((boid.GetSpeed / max) * n) - 1

                    If lv < 0 Then
                        lv = 0
                    ElseIf lv >= colors.Length Then
                        lv = colors.Length - 1
                    End If

                    RenderShape.RenderBoid(gfx, boid.x, boid.y, boid.GetAngle, colors(lv))
                End If
            Next
        End Using

        Return bmp
    End Function
End Module
谢桂纲
Latest posts by 谢桂纲 (see all)

Attachments

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *

博客文章
August 2025
S M T W T F S
 12
3456789
10111213141516
17181920212223
24252627282930
31  
  1. […] 在完成了仿真计算后,针对得到的计算结果文件,接下来我们就可以通过dump_stream函数将计算得到的每一帧的数据以热图的形式导出来,在指定的文件夹中生成每一帧的数据热图结果。在进行热图渲染的时候,可以参考之前写的讲解颜色调色板的文章中列举出来的颜色版进行热图的颜色设置。 […]