- Home /
Problem using boids algorithm for chasing enemies.
Hello,
I'm making a 2D game where I want the enemies to chase the player (think geometry wars). I don't want them to overlap and stack though. So they need to move away from eachother and chase the player in a smooth way.
While googling I came across flocking and boids. It's exactly what I need (I think). While I kind of understand what's supposed to happen I can only make it kind of work but not really. I'm self taught and may be overlooking some basic stuff. I've been following this explanation and trying to implement it in Unity. (http://www.kfish.org/boids/pseudocode.html).
I've also heard of and looked into UnitySteer (https://github.com/ricardojmendez/UnitySteer). Some of the examples are really cool but it's a bit above my level. I find it hard to understand and even harder to implement in my own project.
So here's the code. I'm using a list of enemies, each enemy has a velocity (that's set to Vector3.zero when they spawn) that contains the amount they need to move every update. (I think I may be doing/using this wrong).
The enemies do kind of move but they keep accelerating and spinning out of control like this. (https://www.youtube.com/watch?v=OYn4zVMD-9M&feature=youtu.be ).
This made me think that the velocity keeps stacking so I tried resetting the velocity to zero every update (and upping the movespeed) which results into this. (https://www.youtube.com/watch?v=mMuyqp1NZcs&feature=youtu.be ). I don't think that's how it's supposed to be since the velocity should get updated and compared to the last one in rule 3. Rule 2 is also not very smooth.
I'm not sure what's going wrong. I've tried singling out each rule but it's hard to find problems because I'm also not entirely sure what it's supposed to look like. Any help or suggestions would be appreciated.
private void Update()
{
MoveEnemies();
}
private void MoveEnemies()
{
Vector3 v1,v2,v3,v4;
for(int i = 0; i < AliveEnemies.Count; i++)
{
EnemyScript e = AliveEnemies[i];
//e.veloc = Vector3.zero;
v1 = rule1(e); //fly towards (percieved) center
v2 = rule2(e); //distance other boids
v3 = rule3(e); //match (percieved) velocity with near boids
v4 = rule4(e); //move towards player
e.veloc = e.veloc + v1 + v2 + v3 +v4;
e.transform.position = e.transform.position + e.veloc*Time.deltaTime;
}
}
private Vector3 rule1(EnemyScript ej)
{
Vector3 percievedCenter = Vector3.zero;
for(int j = 0; j < AliveEnemies.Count; j++)
{
EnemyScript e = AliveEnemies[j];
if(e != ej)
{
percievedCenter = percievedCenter + e.transform.position;
}
}
percievedCenter = percievedCenter / (AliveEnemies.Count-1);
return(percievedCenter - ej.transform.position)/100f;
}
private Vector3 rule2(EnemyScript ej)
{
Vector3 center = Vector3.zero;
for(int j = 0; j < AliveEnemies.Count; j++)
{
EnemyScript e = AliveEnemies[j];
float enemyDistance = Vector3.Distance (ej.transform.position, e.transform.position);
if(e != ej && enemyDistance <= 1f)
{
center = center - (e.transform.position - ej.transform.position);
}
}
return center;
}
private Vector3 rule3(EnemyScript ej)
{
Vector3 percievedVeloc = Vector3.zero;
for(int j = 0; j < AliveEnemies.Count; j++)
{
EnemyScript e = AliveEnemies[j];
if(e != ej)
{
percievedVeloc = percievedVeloc + e.veloc;
}
}
percievedVeloc = percievedVeloc / (AliveEnemies.Count-1);
return (percievedVeloc - ej.veloc)/8f;
}
private Vector3 rule4(EnemyScript ej)
{
if(player != null)
{
Vector3 target = player.transform.position;
return((target - ej.transform.position)/20f);
}
else
{
return(Vector3.zero);
}
}
Hi, this kind of behavior can be difficult to debug as every vector depends on other object's position etc... The best method I use to debug this is to reduce the number of objects to a $$anonymous$$imum and with fixed situations ( = only 2 or 3 objects and a player with a fixed position, then, when the behavior seems correct, try to move the player).
I would also try to find configurations that are easy to reproduce to debug each of the rules. I.e put the objects far from each other and check if they move toward a center with rule1 only. Each time you launch the program, the same thing will happen so it's easier to debug.
Other things to help: Debug.DrawLine or DrawRay with different colors representing the different vectors. It can help to "see" what's wrong. This is even more convenient with changing the time scale either by script (on a button pressed) or by changing the time scale in the editor project's settings.
With upping the movespeed I just meant that I had to multiply time.deltatime because they moved really slow. Also thanks for the tips dns!
I think I don't actually have to implement all the flocking rules since that's not really what I need. I mostly need the chase while making sure the enemies don't overlap so I've tried using this and it's been working quite well. I only use the second boid rule together with a Vector3.$$anonymous$$oveTowards the player. (https://youtu.be/ELNC$$anonymous$$J4gFA4 ).
v1 = rule1(e); //fly towards (percieved) center
v2 = rule2(e); //distance other boids
v3 = rule3(e); //match (percieved) velocity with near boids
v4 = rule4(e); //move towards player
e.veloc = e.veloc + v2;
e.transform.position = e.transform.position + e.veloc*Time.deltaTime;
e.transform.position = Vector3.$$anonymous$$oveTowards(e.transform.position,player.transform.position, 4f*Time.deltaTime) ;
Answer by timohausmann · Jul 16, 2015 at 02:46 PM
As far as I can tell, you implemented everything according to the pseudocode. Maybe try to limit the velocity, instead of setting it back to zero, so the movement won't be as exaggerated as in your first video example:
e.veloc = Vector3.ClampMagnitude(e.veloc, 6); //play with this value