DES311 #5: Predators

Progress continues, this week I’ve implemented the last feature on the sprint 2 backlog, predators. Furthermore I’ve been making tweaks to the flock behaviour that has lead to some interesting behaviours and have put in a spawning system that controls the number of BOIDs on screen.

Predators

How predators work is quite simple when compared to the BOIDs. After a set cooldown period a predator is spawned in a random position outside the cameras view. The fist thing the predator does is create a list of all the BOIDs in the scene:

        boids = GameObject.Find("BOID Spawner");

        foreach (Transform child in boids.transform)
        {
            prey.Add(child);
        }

This was actually the first time I had used the for each statement, which on reflection is crazy because its very handy. Next the predator averages the position of each BOID and creates a vector that intersects that position. I’ve written similar code several times already in this project, so I just copy pasted it from the cohesion script. This sends the predator after the flock and when it collides with a BOID that BOID is destroyed. A bool value keeps track of if the predator is hungry. If it has collided with a BOID its hunger is satisfied and it stops following the flock, leaving the scene to be destroyed when out of view:

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.tag == "BOID")
        {
            hungry = false;
            Destroy(collision.gameObject);
        }
    }

After the the predator is destroyed the predator spawner starts its cool down timer. When its up another predator will be spawned.

Since BOIDs are now being destroyed I’ve had to put in a spawner that adds new BOIDs to the scene when they are destroyed by a predator. I experimented with using a similar script to the predator spawner that brings new BOIDs in at random positions off camera. However this lead to a problem. This method meant that if a predator destroys multiple BOIDs then new ones appear from multiple random directions, this leads to quite a lot of chaos as the BOIDs attempt to reform a single flock again. The solution was to have new BOIDs spawned in batches from a single random position that changes periodically. This also has the added benefit of leaving the scene empty for a while if a lot of BOIDs have been destroyed. It gives the simulation the feel of an Attenborough documentary as you watch a few remaining survivors of an attack attempt to regroup and reform their flock.

The introduction of predators that could destroy BOIDs caused a lot of problems due to the BOIDs creating lists of nearby flock mates to generate their behaviours. As soon as null objects started appearing in those lists I was getting a lot of errors and crazy bugs. This was easily remedied by adding a reverse counter that goes through lists removing null objects (it goes in reverse as to not interfere with the index when objects are removed):

        //removes destroyed BOIDS
        for (int i = nearbyBodies.Count - 1; i > -1; i--)
        {
            if(nearbyBodies[i] == null)
            {
                nearbyBodies.RemoveAt(i);
            }
        }

It seems strange to admit but at this point I did get a little sad, preparing those little white triangles to deal with the death of their nearby friends. But deep down I knew this was a good thing. If I’m getting sad about the death of little triangles it means their movement and behaviours are life like enough to trick my brain into forming attachments… after all isn’t that the essence of game development?

Weird Behaviours

I thought I would take some time to talk about some patterns and behaviours I’ve been noticing. The first is how the BOID flocks tend to always turn left, leading to anticlockwise motion. At first I thought this was because the separation script that generates vectors to avoid collisions checks the left side first. So I added a function that randomises the direction that is checked first:

float directionFlip = Mathf.Sign(Random.Range(-1, 1));

direction = Vector3.Normalize(new Vector3(X, Y, -Z * directionFlip))

It’s not very clean code and has a slight bias to the right side, but that doesn’t matter, because the BOIDs still swirl in an anticlockwise motion. So far I haven’t found any other reasons in the code this may be the case. Its super weird.

Another interesting behaviour that I first thought was a bug was the seemingly random snap changes in direction. Randomly in unison the flock will flip 180 degrees and go the other way. This I don’t think is a bug, since this behaviour can be observed in real-world schools of fish. Some further research is needed but what I think is happening is the BOIDs at the front are slowing down to try and reach the centre of the school. In doing so they gradually change the average velocity of the flock, and when enough of them do this and the average velocity reach’s nearly zero a chain reaction starts as each BOID follows their neighbour whilst avoiding stopping. What looks like a complex decision made by some hive mind is really each BOID reacting to a situation in a similar way. For now I’m classing this as an emergent behaviour.

What’s Next

With the conclusion of this sprint I have completed all the main mechanic features I wanted for the simulation. What I plan for sprint 3 is to create some sprites and animations to make the simulation more interesting to look at, I’d also like to build a UI that lets the user change parameters of the simulation.