Thursday, 25 August 2016

SpaceBoids Episode 1

So I wanted to talk about a crazy idea that I've been kicking around in my head, but I realised a blog post on it needs a bit of setup first. I need to explain a few ideas and have a working project I can actually try it out in, so rather than dive in the deep end and risk either not getting enough depth or just not being accessible I figured I'd just go over the foundations of it in another blog post, and in doing so I can get some setup done on a usable project. Two birds with one stone, and an extra few blog posts while I build a unity project and get it up to speed. So without further ado, let's talk about Boids!

Boids, if you've never heard of them are a super interesting way to make simple creatures. If you want some history and a deep description here's a link: http://www.red3d.com/cwr/boids/

So basically a Boid is a kind of framework for designing flocking creatures. Using only a few variables and some fairly simple functions we can create a lot of distinct realistic looking behaviours for schools or flocks of animals, like birds or fish. That's neat if you're marketing the newest Call of Duty game, but in a more indie game we don't really need that kind of background detail, right? Well it's actually useful for a variety of simple AI. Squads of ingame entities can use the same boids AI, they produce really convincing groupings for loose aircraft or spacecraft formations, I've used them in the past with some basic pathfinding to allow for swarms of simple melee attackers in a top down game and I'm fairly sure Blizzard used them to some degree for unit movement in Starcraft 2.

So without wasting time lets build some boids in Unity. So heres the beginning of our boids, an AI that randomly moves around. Pretty dumb, but they'll grow in time. They just choose a direction and head in it. That direction changes every 0.5s, the rest is just some simple code for sending their orders to the rigidbody attached. There's also a basic player in this simulation color coded gold.



So from these humble beginnings our AI will emerge. The next step will be making them try to cluster. To do this we simply keep track of every other boid, in my implementation I just get every other boid in the scene but for performance reasons in a real game you'll need a better way to do this, such as a manager that stores all living npcs or something. There's a million ways to do that kind of stuff so I'll leave the implementation up to you since that's not the point of this exercise, but for boids you essentially need each boid to know a few things about it's friends.

To start with it needs to know their positions, once we know all our friends positions it becomes trivial to work out the average position of the flock, we simply add up all positions and average it. It's a good idea at this point to only look at nearby flock members for the best effect, if you only check nearby boids you can have seperate flocks flying around in the same scene, but if you check all boids they have a tendency to just form one huge blob. Either might be ideal for your game, but in my case I also added a variable for controlling how far each boid can 'see'. So the result is this:


 //calculate flock variables
Vector3 flockCentre = Vector3.zero;
int count = 0;
foreach(Rigidbody body in friends)
{
    //only add nearby friends to the average.
    if ((body.position - transform.position).sqrMagnitude < Mathf.Pow(weights.sightRange,2f))
    {
        flockCentre += body.position;
        count++;
    }

}
flockCentre = flockCentre/(float)count; //find average position.
Vector3 flockDirection = flockCentre - transform.position; //make it relative to our position
flockDirection.Normalize();
//normalize our direction

Well they definitely look a bit more intelligent, they now cluster up and fly around kinda randomly. They have a tendency to slam into eachother however and just clump in general. In this version they treat me as one of their own so I'm able to lead them around since they're attracted to me, which is kinda neat. But they're still dumb as rocks so lets step it up another notch.

Next we'll add avoidance, slamming into eachother looks dumb so lets make it so if we see someone who is too close, we back off a tad. This has a couple benefits, one it makes them look much smarter and two it means you can actually disable their collisions (since they'll naturally avoid eachother they don't need to do physics checks) which can save you a lot of processing in larger swarms. Of course, you can still allow them to collide if you want for gameplay reasons, but they should do a fairly good job avoiding eachother.

So avoidance is a little different to clustering, instead of moving towards the average position, instead we move away specifically from nearby boids. One idea might be to move against the average position we calculated before, but it's actually better to specifically move away from each nearby boid, this allows them to still stick together, whereas if they simply moved away from the average position it would just be fighting against the previous clustering behaviour, so the small logical change adds enough variation to prevent it simply cancelling out.


Vector3 avoidVector = Vector3.zero; //direction away from nearby boids
foreach(Rigidbody body in friends)
{
    if ((body.position - transform.position).sqrMagnitude < Mathf.Pow(weights.nearRange,2f))
    {
        avoidVector += transform.position - body.position;
    }
}
avoidVector.Normalize();


With some number tweaks and disabling collisions we get a pretty nice effect, but there's still a few issues. They tend to group up and then sit in one spot, which isn't ideal, and they like to kinda freak out on the spot which looks bad. They behave like metal shavings and magnets kinda, clumping up and then being a massive pain to seperate, they do follow the player in a cute fashion though.

So the final step in making basic boids is to make them head in the same direction as their fellows. By following the general movement of the crowd you get a much more realistic flock of creatures. Working out the heading of the flock is easy, while we're working out the average position of the flock we can also add up the velocity of each nearby boid, and by doing so we get a vector of the average direction, then we simply make our own acceleration move towards that and we get a nice flow with the crowd.



Vector3 flockHeading = Vector3.zero; //average direction of the flock velocity
foreach(Rigidbody body in friends)
{
    //only add nearby friends to the average.
    if ((body.position - transform.position).sqrMagnitude < Mathf.Pow(weights.sightRange,2f))
    {
        flockHeading += body.velocity;
        count++;
    }
}
flockDirection.Normalize();

And there's the final product, beautiful. At this point it's actually possible to turn off the random heading entirely and they'll still roughly fly around in packs, but the randomisation helps them to split up now and then and prevents them blobbing up.

So the final logic for what direction to go is dictated roughly by the following logic:
Vector heading = RandomDirection + AveragePosition + AvoidanceDirection + AverageDirection

Really it's rather simple but once put together you get quite a neat display.

This post has gone long, so I'll wrap it up here. I'll be returning to boids, this is just part one of many. I think they're a great little tool, next time I cover them I'll talk about weighting the variables to get specific behaviours such as flying in squads, swarming, fleeing and chasing other objects and maybe even (basic) pathfinding. Beyond that I'd like to look into some more advanced AI stuff and maybe even build a small RTS or something.

One last note, Boids work in 3D as well as 2D, I just did 2D since it's easier to see what they're doing.
Could be a good extension if you wanna play around a bit. Shouldn't be hard to get them to go in all directions.

Here's a copy of the current project if you'd like to play around yourself, it was built in v5.1.1f and so may not be compatible with other versions of unity. The variables are all exposed in the editor so feel free to play around:

https://my.mixtape.moe/zrotux.rar

No comments:

Post a Comment