Spirits: AI MarkUp Scoring

In my game, Spirits, the boss character followed the player around the map while they fought. This was problematic for a couple reasons:

  • The boss was too large: if the boss got too close, the player wouldn't be able to see anything.

  • The player was forced to always look at the boss to know where it was, defeating our method of achieving the retro nostalgia pillar (being able to look around the environment).

  • The player was forced to be strafing backwards all the time, causing them to back into environmental assets.

To correct this, we needed a way to have the boss appear in and move between authored locations to fight the player at. To do this, I used empty objects that I manually placed in the level called Markup. However, I needed a way to score each piece of Markup depending on the current position of the player, otherwise the boss would be moving between arbitrary positions that might not be good for gameplay purposes. To begin, I identified what are important characteristics that the chosen pieces of Markup need to have. I determined that they must:

  • Be within a certain distance of the player (not too close and not too far)

  • Be within sight of the player.

  • Have an unobstructed path to the player.

Because all of these characteristics were quantifiable, I used a logic pattern that I read about here. Basically, for each location, I score the 3 characteristics between 0-1 and multiply them all together to determine an overall score. The location is selected from whatever has the highest score. How I rated each location based on these characteristics were as follows:

Distance to the Player

I calculated the distance between the Markup and the player, and then I took the difference between that and an optimal distance. I then clamped the difference between 0 (being the most optimal) and 15 (the least acceptable) and found the value on a scale of 0-1.

In Sight of the Player

I calculated the angle between the forward of the player and the direction of the Markup from the player. Because of calculations, this gave me an angle of 0-180 degrees. I clamped this to a range of 0-90, and found its value on a range of 0-1.

Check if Sight to the Player is Unobstructed

This one was simple. I raycasted from the player to the Markup, and if anything was in the way, the value automatically became 0, otherwise it was 1.


With the 3 characteristics multiplied together, I ensured that if one characteristic were to fail then the corresponding piece of Markup would never be chosen. Also, if all the Markup were to fail, then a fallback location would be chosen.

This was a very powerful pattern that helped immensely, and had been used in other parts of Spirits as well. Hopefully, by reading this, you can see the value in using it too, and this was just an example of its many uses.