Grouping Around a Point and Moving Together With Steering Behaviors

I have a group of enemies that move together. I have them grouped around a point by setting their target to be a point at the center of the group. I have combined the "Seek" and "Separate" steering behaviors to achieve this. My issue stems from the fact that they all want to move to that point, effectively fighting for the center. (The separate steering behavior just has them doing this at a distance, and when trying to use the arrive steering behavior the same issue happens it's just slightly less pronounced, but has other side effects I don't want.) Is there any way I could group them like this without having them fight for the center? I don't want to have set positions as that would be unnatural looking and ill-fitting for my game. I also think a flow field would have the same issues (I'm not sure about that).

Note: The point moves, as this is for group movement.

• I know this looks similar to several other questions, but none solve my issue and this seems different enough not to be a duplicate. – GameDungeon Jul 22 at 3:11

Falloff with distance

Implement falloff with distance in your steering behaviours to make them do less when less relevant.

I assume your Seek and Separate behaviours each output a desired direction and weight that your enemies use to determine how to move. If you scale the weight by distance, then you can let the two behaviours balance each other out.

Seek scales inversely with distance to destination.

Separate scales proportionally with distance to other enemies.

function clamp(min, max, num)
return num <= min and min or (num >= max and max or num)
end

function inverse_lerp(a, b, t)
return (t - a)/(b - a)
end

function scale(min, max, val)
val = clamp(min, max, val)
local t = inverse_lerp(min, max, val)
-- See easings.net for alternatives to this cubic curve.
return t * t * t
end


Will give a curve like:

Use 1 - scale(min, max, distance) to scale inversely.

That way your Seek will have no influence when the enemies are close to their target, only small influence when "close enough", and lots of influence when far away.

Influence threshold before action

Additionally, if your enemies look good stationary (ground units, units with standing idles, etc), you could set a threshold of influence before they move. This threshold could vary so it becomes higher when they're stationary to avoid jittering and lower when moving quickly to be more responsive.

When figuring this stuff out, I'd always recommend visualizing your data with graphs. Here's the love2d program that I used to generate that graph:

function love.draw()
local pts = {}
local size = 10000
local min = 100
local max = 200
for x=0,size,1 do
table.insert(pts, x + 50)
table.insert(pts, 200 - 100 * scale(min, max, x))
end
love.graphics.points(pts)
love.graphics.line(10, 200, 10, 100)
love.graphics.line(10, 200, 20, 200)
love.graphics.line(10, 100, 20, 100)
love.graphics.print("0", 25, 190)
love.graphics.print("1", 25, 100)
end


The answer to your question lies with the strategies employed in this short video by Game Endeavor...