Salomonsson.se
Jul 16 2020

Swarming

There is a known problem that occurs when you try to have a large number of objects moving towards the same exact point… they will meld together and in the end seem like a single object.

Jerky animation because of low framerate of the gif

In Ripple I avoided this problem by making sure we did not have enough flying/following enemies on screen at the same time. However, I have a case now where I want a ton of theese… a swarm of enemies.

Funny thing is that the math for moving an object towards a point is dead simple. Does some kind of scatter function have to be incredible complex? Or can we do it in an almost as simple way? I had a theory I wanted to try, here’s the psuedo code.

   for each enemy in enemies
   if enemy == me: continue
   if distance D is less than minAllowedRadius R
      move me away from enemy in opposite direction by distance D

Result 1

Glitchy

Looks glitchy. Figured out pretty quick that we push by distance D, which means that if minAllowedRadius is 25 and you touch the outer rim of it, distance will be 24, and you will be pushed 24 units instead of 1 unit. And the opposite, if your are 1 unit away you will only be pushed one unit. We obviously need to push it 1 unit in the first example and 24 units in the second.

Result 2

Ladies and gentlemen... a fine swarm indeed!

Whoa! That looks amazing! I did not expect it to be that fluid!!!

Here is the final psuedo code

   foreach enemy in enemies
   velocity = (playerPos - enemy.pos).normalize * speed
   lookAt = velocity.angle() // update lookAngle before calculating scatter

   pushback = new Vector2(0,0)
   var newPos = enemy.pos + velocity
   foreach checkScatter in enemies
      if (checkScatter = enemy) continue
      otherTowardsMe = newPos - checkScatter.pos
      if (otherTowardsMe.squaredMagnitude < minAllowedRadiusSquared)
         // Too close to someone!
         otherTowardsMeMagnitude = otherTowardsMe.magnitude
         // need to adjust amountToPushBack, or we get gif number 2
         amountToPushBack = minAllowedRadius - otherTowardsMeMagnitude
         pushback += otherTowardsMe / otherTowardsMeMagnitue * amountToPushBack

   enemy.pos = newPos + pushback

As you might (or might not) notice is that I use the squaredDistance and squaredMagnitude in the first check, where we see if we are in contact. This is to avoid using squared root in the inner loop (that would be a lot of square root calculations).

However, if we do have a hit we calculate the actual magnitude, so we can adjust the amount of pushback. I store it in a variable as I need it twice.

There might be even more optimized ways to do this, but doing a sqrt for N^(N-1) seemed a bit too expensive.

Oh… and this was also my first attempt at using Godot engine. Is was actually really pleasant! I might actually start using it instead of Unity for experiments and personal prototypes… at least for a while, as a test!