A Gentle Introduction to AI — Continued

Part two of an article about designing and implementing AI bots for a battle royale game released on Apple Arcade

[NOTE: This was originally published on Medium, and has been reposted on this blog for posterity]


In the first segment of this two-part article about implementing bots for Butter Royale, I outlined the early design and how we arrived at an AI framework to use. This time, I will go a bit more in-depth into how we designed our AI Behaviour Trees, how we setup AI profiles for bot difficulty and, finally, touch on how our QA department actually managed to test and verify that the bots worked as intended!

As mentioned at the end of the previous article, we ended up using NodeCanvas for our AI needs, since its feature set fit our feature requirements like a glove. Its Behaviour Trees, visual editor/debugging tools, built-in event system and variable blackboard system allowed us to setup dynamic, reactive and (most importantly) functional bots with multiple difficulty levels — in less than three weeks.

Game designer taking NodeCanvas for a spin, as seen through the eyes of any programmer on the planet

We originally anticipated that it would take longer to develop the feature into a functional state, but due to the extensive pre-planning we did, along with the robustness of the AI framework we landed on, the pieces fell into place faster than expected. Everything came together smoothly and without major hitches, so we even had time to spare for polish and iterating on the feature — always a bonus!


DRY Behaviour Trees

Without further ado, let’s delve briefly into the world of programming, and have a look at a common principle of software development that also applies to Behaviour Trees— DRY.

“Don’t repeat yourself” is a principle that basically aims to reduce repetition of similar work being done in multiple different areas of a software’s code-base. This helps avoid situations where if you need to change one small piece of code, you don’t have to apply that change in many different places in the code-base, but can just change it in that one location and have it reflected throughout the software.

I will not repeat myself

DRY also applies to Behaviour Trees — and visual scripting in general, since these are essentially abstractions of code presented in a different form, one that is often easier to understand by the likes of myself and other nerf herders/game designers compared to “pure” code approaches.

For instance, instead of having one Behaviour Tree for each AI profile in our game — which would mean lots of duplicate trees and an ongoing maintenance hell, we decided to have only one, global tree used by all the bots. This way, any change we made to the overall bot behaviour would apply to ALL our bots, independent of their individual profiles.

AI Bots Engaged in Combat in Butter Royale

Using SubTrees to Maintain DRY Principle

“…the Behaviour Tree had finally grown to start resembling a spaghetti beast…”

A feature of NodeCanvas that can help maintain that DRY principle is SubTrees. Specific bot behaviours might need to be repeated in different branches of the Behaviour Tree, and instead of having multiple instances of those behaviours that would all need to be maintained individually, they can be separated out into SubTrees that could then be reused anywhere in the main Behaviour Tree.

While this was our approach in the very beginning, we eventually chose to scrap the usage of SubTrees and merge everything into one tree due to a performance issue in the version of NodeCanvas we were using; given the amount of bots we were spawning per match, the additional loading time needed for SubTrees caused a noticeable stutter when spawning the bots.

There was one happy side-effect of doing away with SubTrees: Debugging bot behaviour problems actually became easier, since the visual debugger tool present in NodeCanvas doesn’t at a glance tell you what goes on within a specific SubTree when viewing the main tree.

Visual debugging in NodeCanvas

The downside was that by the end of the project, when we had implemented all the various bot behaviours we wanted — like collection of items, melee and ranged combat, fallback behaviours that kept the bots safe from the danger zone and more — the Behaviour Tree had finally grown to start resembling a spaghetti beast, pushing at the limits for how far one singular Behaviour Tree can go before becoming unmanageable.

Designer’s Rendition of Flying Spaghetti Monster in the Sky

Behaviour Tree Setup Details

Priority Based Branches

Our Behaviour Tree was setup to be constantly reevaluated by the bots, working from left to right through a series of branches with decreasing priority, with the order of the branches being the determining factor for priority.

Within each branch of the tree, the various conditions being checked were all marked as dynamic within NodeCanvas, a flag which allows the higher priority branches on the left to take precedence at a moment’s notice if the conditions checked should ever change and warrant the execution of a different action.

Example of our use of Dynamic Binary Selectors in NodeCanvas

At the extreme left end, we placed the highest priority behaviour like seeking out nearby squad member or reacting to nearby enemies. At the extreme right end, the “least important” fallback behaviour that handles basic movement when there’s no other interesting actions that should be taken.

Rough overview of the different branches, listed here in the order of their appearance and priority in the tree:

  • Seek out Nearby Squad Members (if bot itself is knocked down and needs to be revived, or if nearby squad member needs to be revived)
  • Open Nearby Delivery (supply drop in the form of a fridge)
  • React to Nearby Enemy (seek out, attack or flee)
  • Collect Nearby NOM (“Nutritionally Operated Machinery” — the term used to describe weapons in the game. Depending on factors such as current NOM tier, tier of nearby NOM, ammo remaining)
  • Open Nearby Chest (after NOM collection so bot can secure a NOM to defend itself before trying to open chest)
  • Collect Nearby Health > Ammo > Armor (in that order, depending on state of the bot’s health, ammo and armor at any given time)
  • Follow Nearby Squad Member (to try to stay together as a squad)
  • Seek Safe Zone and Avoid Butter (butter = danger zone, shrinking every X seconds, deals damage if bot is caught in it)
  • Wander Aimlessly (fallback; nothing else to do, so just walk around and pretend to have a purpose in life)
The Central Nervous System of Butter Royale AI Bots, branching out into different behaviours

Dynamic Conditions and Probability Checks

Each of these branches contained a number of conditions that had to be met before the bot could perform the intended behaviour, and each branch contained sub-branches to handle edge cases or the different “choices” the bot could make every time the tree was reevaluated.

To make those choices, the bots relied on probability selectors, where the outcomes were based on weighted variables stored in the variable blackboard for each AI profile. Some of those variables were fixed, defined per AI profile, while others were dynamic/floating values that changed throughout the course of a match.

Example of a Probability Selector being used to aid AI decisions

For instance, some selectors weighed the bot’s current health versus a fixed value, with the probability of the bot making one choice or the other — in this example, whether to pick up a Health Kit or not — gradually increasing or decreasing inverse of the bot’s health. The same principle was applied to determine whether the bot would pick up additional ammo or armour, and to help bots decide between engaging in combat or running away to fight another day.

Setting up the conditions in this way helped ensure that the bot would react dynamically and appropriately to the situation at hand.


AI Profiles for Difficulty

As mentioned, we decided to setup AI profiles for a range of different play styles and difficulty levels, to simulate the variety of human players one can encounter in a real match; from absolute noobs to self-proclaimed pro-gamers.

Using the variable Blackboards in NodeCanvas, each profile was setup with weighted variables that determined how aggressive, stealthy, loot hungry, campy or brave the individual bots would be. This was also used to determine the radius of their Line of Sight, their overall firing accuracy, their chance to flee from battles, etc.

Some of the parameters used by the AI in Butter Royale

In total, each profile was setup with nearly fifty parameters that could be used to alter each AI profile’s behaviour under different conditions.

To start with, we defined a few different profiles and set up the parameters according to their intended behaviour:

  • Balanced—baseline profile that’s balanced to do a bit of everything, without a particular focus.
  • Yolo — all out attack, this guy doesn’t hold back. Will take any opportunity to go after the next available target.
  • Survivor — highly focused on health, armor and staying alive no matter what. Will run away to live and fight another day
  • Camper — high tendency to camp in bushes, bide their time, ambush players who walk past — but only if within optimal firing range.

AI Skill Levels

We then took each of these AI profiles and created more granular versions of each to simulate players of different skill levels — from weak, through medium and hard to godlike.

Examples of AI Profiles in Butter Royale
  • Weak— weaker variants have shorter Line of Sight, poor accuracy when shooting, rarely heal themselves and have little regard for their own well being. Attempts to simulate beginner and low-skilled players.
  • Medium — average LoS, average accuracy, higher chance to collect items and chests than the weak bots.
  • Hard — longer LoS, better accuracy, picks up health when needed, seeks optimal weapon range in encounters with other contestants on the butterfield.
  • Godlike — godlike variants cheat a little bit, having longer Line of Sight than normal players would, and also have near perfect accuracy (against targets that don’t move, anyway). They will stock up on ammo, health and armor, and actively seek out bigger and better NOMs. If they’re in danger, they will sometimes run away to find health. Attempts to simulate more hard-core pro gamers.

Overall, the pool of actions the bots could take throughout the match stayed the same across the different profiles and difficulty levels, but the parameters allowed us to balance the frequency at which those actions would take place for any given profile, as well as tweak how efficient the bots would be when executing the various actions.

At a glance, it might not be apparent to a normal player what kind of profile and difficulty level a given bot in a match is setup with, but over time, this system provides an overall experience for the player where no two encounters with bots are ever exactly the same. Instead, the bots all display different priorities, strategies and wants — like a normal player would.

Weighted Bot Spawning System

Finally, we setup a weighted spawning system that was used to determine the types of bots that would spawn in any given match, balanced in such a way that players would get fair matching with a varied selection of AI profiles and difficulties in each match. Neither too many weak nor too many hard bots would make for a good experience for the average, casual Apple Arcade player, so it required some iteration to find a good balance.

Extract of weighted spawn system used to spawn bots in Butter Royale

Quality Assurance — Testing the Bots

To put the final stamp on any feature and say “It’s done! Let’s move on to the next thing”, there’s one more thing that needs to happen. Sometimes neglected, sometimes done halfheartedly — I am of course talking about Quality Assurance; the process of making sure that a feature actually works as expected.

Now, it should always be the responsibility of each designer, developer, artist or anyone else who contribute to a project to at least do the bare minimum of testing to make sure their stuff is up to snuff, but even with rigorous testing, we often become blind to flaws in our own work, and bugs slip through the cracks.

AI Bot Collection Behaviour (2x Speed)

Having a dedicated QA team to help find and iron out those flaws is a must, and having one on-site and as part of the team — like we have at Mighty Bear — is invaluable!

Alright, so we’ve established that testing is good, and that QA is necessary, but… How do you QA test semi-unpredictable bots that can dynamically change their behaviour from one second to the next, sometimes for reasons that are not apparent to the casual observer — and the only tool at your disposal for testing is to observe the bots using the game’s regular spectator mode, one bot at a time? 🤔

AI Bot Combat Behaviour (2x Speed)

Our solution was to write up a document outlining the expected, likely and possible bot behaviours in different situations, based on the actual implementation. Another mind-map, if you will — similar to what we did at the very start of the project as described in my previous article. This new and updated map of bot behaviour allowed the QA department to build an accurate and extensive checklist they could use when observing the bot behaviours.

QA Checklist for checking AI bot behaviours

While the bots could still display seemingly erratic behaviour (though no different/worse than a regular human player), QA could check against this list and go “Yep, that’s a possible action to take in this situation. Works as intended!”

That’s all, folks! So long and thanks for all the fish!

tl;dr; — bots were implemented in game. They have since taken on a will of their own and have been escaping into the real world. We tried to pull the plug on them at one point, but it backfired badly (long story), so now we just look the other way and pretend not to notice. I, for one, welcome our new AI overlords…

A Gentle Introduction to AI

A two-part article about designing and implementing AI bots in a battle royale game for Apple Arcade

[NOTE: This was originally published on Medium, and has been reposted on this blog for posterity]


When we were developing Butter Royale, some of the core requirements were to make the game available both online and offline, with players being able to get into matches in 30–40 seconds or less. Aside from the initial connection needed to download the game from Apple Arcade, players had to be able to play and enjoy the game without necessarily going online first.

In order to support offline play, we couldn’t merely rely on other humans for our match-making needs, but would also need non-human opponents contained on each player’s individual device. In other words, we needed…

…bots!

“RoboClock” — Copyright © Tama Leaver — Licensed under Creative Commons v2.0

Hello there! My name is Geir Ove, and I’m a game designer/amateur juggler at Mighty Bear Games. In this two-part article I’ll outline the steps we took to design and implement bots in Butter Royale, starting with the early design and evaluation of AI frameworks, and following up with a more in-depth dive into the gritty details in a second article.

Please, sit back and make yourself comfortable, gentle reader, as I take you on a journey through time to explore how we came to implement those bots! You see, it all started back in the mid-1920s, when the world was still displayed in black and white and corona mostly existed in people’s minds as a refreshing summer beverage… (story continues after image)

Butter Royale was released on Apple Arcade in January 2020

Scoping the Design Through Mind-Mapping

…then, roughly 94 years later, we started looking into creating bots for our new game, and launched that process by mapping out actions that normal players could perform in the game, using online, freely available mind-mapping services like MindMeister1.

We went through a couple of iterations of this mind map, and the basic decision tree it contained became more detailed and more accurate with each iteration.

Extract of early mind map

Going through this process from a “possible player actions” perspective helped us scope the early design and come up with a plan for:

  • The actions a bot should be able to perform, like moving, collecting items and shooting.
  • The common player strategies a bot could try to emulate, like circle strafing in combat, fleeing when low on health and ambushing players from… well… bushes, of course!
  • How bots should react to events like safe zone shrinking or taking environment damage.

The mind-map was not a final, cast-in-stone blueprint for how the bots should behave, but it was a guide to keep us on the right track and make sure we focused on the important parts we got around to the implementation phase.


Learning from Previous Projects

“…make sure that bot AIs can be easily setup, iterated on and maintained by designers…”

Before I go on, I should note that this was not our first attempt at doing bots; in an earlier game a couple of years back — World of Legends2 — we also made use of bots to provide players with easy opponents.

The bots in WoL were setup using a very engineer-focused and script-based AI framework that controlled bots server side, and required recompiling the server to have any AI changes go into effect. As a result, making even minor changes to the AI scripts required engineering support, and iterating on or debugging those changes was a slow process.

World of Legends, a now-sunset MMO for casual players

One important lessons we took away from the above-mentioned experience, which remained in the forefront of our thoughts going forward with bots in our new project, was to make sure AI bots could be setup, iterated on and maintained by designers, without having to rely too much on engineers

This meant more up front work for the engineers to ensure the initial implementation was solid, but it also freed them up to work on other features later, leaving the bulk of AI tweaking and iterating to design.


Evaluating AI Systems and Frameworks

With that in mind, and with a solid plan in place, we were able to start evaluating different options for making our bots come to life, including:

  • Finite State Machines — The classic starting point for most designers and developers wanting to do something with AI. Useful for setting up linear, scripted encounters and gameplay. Shows its limitations with more dynamic gameplay, where it can quickly become a complex mess of potentially hundreds of states and transitions between these.
  • Behaviour Trees3— The next step up from FSM (depending on your needs). Easy to setup initially, read, debug and expand, but very large trees can become prohibitively costly to evaluate. Expanding will also eventually reach a point where, like FSM, it can become a maintenance nightmare. Rules and AI choices are specifically defined by the designer.
  • Utility AI4— Allows setting up dynamic and (sometimes) unpredictable gameplay, with the AI prioritising behaviours based on the score of each available option under the circumstances at hand. Easy to design and to extend; good for complex, goal-based behaviours. Potentially takes a lot of testing and iterating to get the scores for the different options right, and can be hard to debug whether the bot’s actions are due to “emergent behaviour” or a bug in your scoring system.
  • AI Planning5 — Amongst the most recent contestants to enter the arena, AI Planners try to procedurally find the best course of action to reach a particular goal in an optimised way. The AI is given a goal, some possible actions, and the AI is then “let loose” to figure out how to best reach the goal. The upside of this is that you get an AI with emergent-style behaviour. The downside is that you get an AI with emergent-style behaviour. The designer risks losing control over what the AI does, potentially leading to unpredictable and/or unwanted behaviour.

All these AI systems have their own pros and cons in regard to the depth and complexity of the AI and to the ease of setting up, testing and iterating on the system, and all come with their own multitudes of frameworks and plugins — so how to choose one method over the others?

Process of Elimination

Going back to our mind map from earlier, we knew our bots needed to be somewhat predictable, with just a few overarching goals that would drive their base behaviour, such as:

  • Avoiding the danger zone
  • Seeking the safe zone
  • Collecting stuff
  • Fighting opponents
  • Helping out Squad members

At the same time, the bots needed to be able to jump between those goals in a dynamic fashion, without delay, as the situation demanded. For a more varied player experience, we also wanted a variety of AI profiles, each with a different set of priorities and difficulty level. These requirements effectively took FSM (too limited) and AI Planners (too unpredictable) off the board.

Some of the other frameworks and plugins we found were too experimental and unproven in the field, or were only published for older Unity versions and no longer received updates. None of these were given much consideration, as we had very limited development time (6 months) and couldn’t afford to do a lot of experimentation and prototyping with potential dead end solutions.

Other deciding factors came down to how easily we could incorporate an existing, proven AI framework into our game, and whether said framework would work on all the platforms we needed to support (iOS, MacOS, tvOS).

Unity’s AI Planner was a serious contender for a while, until we realised it didn’t work well with the version of ECS6 (Unity’s Entity Component System) we were using. It was also still a young framework at the time, and lacking extensive documentation and implementation examples.

Near the end of our evaluation process, we were leaning towards Behaviour Tree and Utility AI systems, with the Utility AI eventually losing out as we landed on a BT framework which aligned closely with our requirements and allowed us to proceed ahead at full steam.

The winner: NodeCanvas7

NodeCanvas, by ParadoxNotion

End of Chapter One — or “How Good Planning Resulted in Quality Bots”

I will go into more detail about how we used NodeCanvas to setup our Behaviour Trees and AI Profiles in the next article, but for now I’ll leave you with this:

The process we went through with mind-mapping, along with the evaluations we did to make sure we chose the right framework, played an important part to ensure that the quality of our bots was up to snuff.

Our bots in fact ended up mimicking player behaviours to such an extent that many actual players (and at times, members of our own team) still to this day have a hard time consistently telling human and AI opponents apart!

Bots and players in action in Butter Royale

Of course, any game AI that doesn’t continuously receive updates (or updates itself!) will display patterns and styles of play that can be identified over time — but for beginner Butter Royale players these patterns are not immediately clear, and it often takes a substantial amount of time for players to tell bots and humans apart with 100% certainty.

That is a testament to the up-front planning we did with mind maps, to the chosen AI framework and the engineer (Yo, Khalid!) who evaluated, integrated and extended said framework to support our AI needs, to the extra time we had available for iterating on and polishing the feature, and last — but not least — to the continuous feedback we got from the rest of the team through the internal playtests that were held multiple times every week.

It also helped that we implemented machine learning, block chain and virt[Inner Editor Voice: No, we did not. Stop. You’re done. This article is OVER.]


Thanks for sticking with me so far! In part 2 of this article, if my inner editor voice lets me, I will go into more detail about how we designed and implemented our behaviour trees and AI profiles in NodeCanvas. Stay tuned!

Also, if you’re interested in reading more about some of the things mentioned in this article, please check out the resources listed below!


  1. MindMeister. Online mind mapping application that allows its users to visualize, share and present their thoughts via the cloud
    https://www.mindmeister.com/ ↩︎
  2. FSM. Finite State Machines
    https://en.wikipedia.org/wiki/Finite-state_machine ↩︎
  3. Behavior Trees
    https://en.wikipedia.org/wiki/Behavior_tree ↩︎
  4. Utility AI
    https://en.wikipedia.org/wiki/Utility_system ↩︎
  5. AI Planners
    https://planning.wiki/guide/whatis/aip ↩︎
  6. ECS. Entity Component System, part of a Unity framework for writing optimized, data oriented code
    https://learn.unity.com/tutorial/entity-component-system ↩︎
  7. NodeCanvas. Node-based Visual Behaviour Authoring framework for Unity
    https://nodecanvas.paradoxnotion.com ↩︎

Lessons learned by working at Funcom for five years

I have an upcoming jubilee of sorts in a few months, at which point I can celebrate having endured life in the game development industry for five (measly) years. This cause for celebration is somewhat diminished by the fact that Funcom announced earlier this month that they are restructuring and consolidating offices – which means that I, along with the majority of the other developers at Funcom’s Montreal office, are being let go. For my part this means that I have at most two and a half month left before my official last day at FC, and having started working for FC in late March 2008, this means I might just about pass the five-year mark (yay!) before I’m officially out of a job (nay!).

Throughout these last soon-to-be five years I’ve had the pleasure and privilege of working with a diverse bunch of awesome people (and I hope that I’ll be able to work with some of them again in the future!) on two different MMORPG projects, and I would not change that for the world, but what exactly have I learned after this time spent working in the game development industry? Which of the preconceived assumptions and expectations I brought with me have held up, and which have been thoroughly shattered? What knowledge have I acquired that I can bring with me where-ever I go next?

I’ve played with the idea of writing a post along these lines in the past year or so, but what I’ve found out is that it’s not easy to summarize several years worth of experience in a simple blog post. Instead, I will try to focus on some of the more obvious lessons I have learned, the ones I can point at and say “that might have been useful to know/realize the value of when I first started”. Some – or maybe all – of them are perhaps obvious enough that they’re hardly worth mentioning, but then again – everything is obvious is hindsight.
Continue reading “Lessons learned by working at Funcom for five years”