This 3D flocking tool is designed to simulate complex movements with boids within a 3D space. It does thought the use of two different scripts:
- Boid Controller which controls the behaviour of the boids
- Flocking Controller which controls the space, how many boids there are and the target.
The flocking controller is the first part in making this system work. It controls the amount and stores every boid in the scene. Controls the area the boids move in and sets the target for the boids to move too. How this all works is that,
Firstly there needs to be some boids in the scene before anything can happen. How I did this was creating a public game object that stores the boid prefab, an int that controls the number of boids on the scene, an array that stores every boid in the scene and another int to store how big of an area the boids spawn and move in.
With this I cycle thought the array of boids and getting a random X,Y and Z position before instantiating the boid onto that position and pushing it back into the list.
Code:
void Start ()
{
boids = new GameObject[numOfBoids];
for (int i = 0; i <= numOfBoids; i++) //Random starting positions for each boid in the list within the spawn area.
{
Vector3 boidPos = new Vector3(Random.Range(-spawnSizeArea, spawnSizeArea), //X
Random.Range(-spawnSizeArea, spawnSizeArea), //Y
Random.Range(-spawnSizeArea, spawnSizeArea)); //Z
boids[i] = Instantiate(boidPrefab, boidPos, Quaternion.identity) as GameObject; //Instantiating the boid onto the scene
}
}
Result:
Next up is the target and I had set a bool to check if there is custom target the user wants to set else I just choose a random position to be the target at random times.
Code:
void Update ()
{
if (addTarget) //if there is a custom target that the boids should go after
{
targetGoal = targetPrefab.transform.position;
}
else
{
if (Random.Range(0, 5000) < 50) //Else get a random position that changes randomly as well to keep the boids moving
{
targetGoal = new Vector3(Random.Range(-spawnSizeArea, spawnSizeArea), //X
Random.Range(-spawnSizeArea, spawnSizeArea), //Y
Random.Range(-spawnSizeArea, spawnSizeArea)); //Z
}
}
}
Result:
Once the boids are in the scene I needed them to start moving around the scene, I did this by giving the boids a random speed when they spawned, and had to reference both the flocking manager and the flocking controller before I could apply the rules to the boids.
Code:
void Start ()
{
boidSpeed = Random.Range(1, RandomnessSpeed);
FlockingManager = GameObject.Find("Flocking Manager");
fc = FlockingManager.GetComponent&lt;FlockingController&gt;(); //Finally got the reference corret
}
In terms of the rules for flocking, I need to keep in mind the Alignment of the group of boids, the cohesion of the group of boids and the separation of the group of boids. To do this I got the group the boids from the flocking controller and stored into another array inside the boid controller so every boid would have a list of every boids on them. With this I needed to figure out how close all the boids are to each other, This involved performing a distance check on every boid in update to see if any boids were close enough to be picked up. Boids that were close enough were called neighbours and were added to a group.
Code:
float dist; //Distance
int groupSize = 0;
foreach(GameObject bo in bos ) //Calculating the group distance between boids to see if they are within range to become part of the group
{
if (bo != this.gameObject)
{
dist = Vector3.Distance(bo.transform.position, this.transform.position); //Getting the distance between each of the boids
if (dist &lt;= neighbourDistance) //if they are in range of their neighbours
{
vCenter += bo.transform.position; //Update the center of the group
groupSize++;
if (dist < 2.0f) //if they are too close
{
vAvoid = vAvoid + (this.transform.position - bo.transform.position); //push them in the other direction
}
BoidController anotherBoid = bo.GetComponent<BoidController>(); //Getting the boid script off the neightbour and adding the group speeds together so we can get an average
groupSpeed = groupSpeed + anotherBoid.boidSpeed;
}
}
}
You can also see that there is another distance check at the end that checks if the boid is too close to one another and there fore move in the opposite direction to avoid hitting it. I also needed to add the speed of the new boids together in a group speed value so it can averaged out later on.
Result:
Now that there is a group of boid together, next rules need to be allied, cohesion and alignment. This involved getting the current center of the group and averaging out the speed of the group as well. I then needed to average out the direction the boids are looking and moving in based on the center of the group, their current position and the avoidance distance. If at any point the boids end up hitting the end direction or target then I perform a Quaternion.Slerp to rotate around the target instead.
Code:
if (groupSize > 0) //if there is a group
{
vCenter = vCenter / groupSize + (targetPos - this.transform.position); //getting the center of the group
boidSpeed = groupSpeed / groupSize; //averaging it
Vector3 direction = (vCenter + vAvoid) - transform.position; //direction the boid needs to move in
if (direction != Vector3.zero) //if not on the right direction
{
transform.rotation = Quaternion.Slerp(transform.rotation, //rotating the boid to go in the correct direction
Quaternion.LookRotation(direction),
boidRotationSpeed * Time.deltaTime);
}
}
Result:
This was the basic’s of flocking currently implemented into this system. I lastly added one extra feature that checks if the boids are out of bounds. As if the boids were to be seperated or not join in a group they would move forever and not find anything so another distance check was done if they have left the spawning area set in the flocking manager.
Code:
if (Vector3.Distance(transform.position, Vector3.zero) >= 20) //if the boids move out of bounds (size of the spawn area) then they turn around
{
turning = true;
}
else
{
turning = false;
}
if (turning) //A quaternion.Slerp to rotate the boids back towards the center of the spawn area and give more opertunities for the boids to become groups
{
Vector3 direction = Vector3.zero - transform.position;
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), boidRotationSpeed * Time.deltaTime);
boidSpeed = Random.Range(1, RandomnessSpeed); //Give the boid a new random speed if selected
}
This is current flocking system I have made, hopefully it all made sense.
Thanks for Readying 🙂