Learn How to Implement NEAT AI in Unity

Greetings
This is hopefully the easiest implementation write-up you’ve seen for NEAT AI in Unity. Neural Networks are a broad topic and can seem daunting, however using them doesn’t need to be difficult. In case you don’t know what a Neural Network is, in simple terms it is a function that takes a number of inputs and produces a number of outputs after doing work on the values.

Grabbed from our favorite guide: Hacker’s guide to Neural Networks
Chapter 1: Real-valued Circuits
In my opinion, the best way to think of Neural Networks is as real-valued circuits, where real values (instead of boolean values {0,1}) “flow” along edges and interact in gates. — Andrej Karpathy (Hacker’s guide to Neural Networks)

What are we doing here?

We are going to show you how to use a Neural Net called NeuroEvolution of Augmenting Topologies (NEAT) inside of Unity3D. In the end, you can expect to have smoothly bobbing cubes like the ones you see in the gif below.

What NEAT does

Over the course of many evolution cycles, NEAT determines the most efficient balance between weights and structure. If you want to learn more about weights of Neural Nets you should check out Hacker’s guide to Neural Networks for the how NEAT alters structure, see Kenneth Stanley’s paper (he created the NEAT method).

Who is this for?

This is for people who know how to program using C# and who use Unity. You can still follow along if you don’t know either of those, but it might take you a little while longer because you will need to look up other programming information. Which is awesome, though, because that’s totally what I would be doing too.

Downloads

  1. Download the repo

Let’s get this party started

What we have now is the foundation needed to build some very cool Artificial Intelligence. When we started this project, we just wanted to do something that was a simple baseline to get our hands dirty. So, if you’re ready, let’s get to it. You can work in the current scene and just follow along or create a new one to get a better feel for putting this together.

Create smartUnit

  1. Create a Cube GameObject named smartUnit
  2. Create a folder called Prefabs
  3. Drag smartUnit into the prefabs directory to create a prefab
  4. Delete smartUnit from the hierarchy
  5. Add a RigidBody component (using the defaults is okay for this one)

Add Controller to smartUnit

  1. Add a new script called LocomotionController
  2. Attach the new script to your smartUnit
  3. Open the script to edit it

Edit LocomotionController.cs

Add a Target

  1. Create a new Sphere GameObject called Target. You can add a color to it if you like (this can make it easier to visually track)
  2. Position the Target on the floor, but off in a corner, so that your smartUnits don’t spawn directly on top of the target, and so they have something to achieve and improve their fitness score
  3. Drag your Target into the prefabs directory
  4. Click on the smartUnit to view its components in the inspector
  5. Drag your new Target prefab onto the “Target” field of the smartUnit Locomotion Controller component. NOTE: you cannot drag the Target GameObject from the hierarchy to this component because the component is on a prefab. That’s why we are making the Target a prefab as well
  6. It should look like this when you’ve done it correctly

You can mess around with the Force on this controller to see what works best for you. Ours is 5 here, but you can try different things out to see if you can get more interesting results. :-)

Update Number of Inputs and Outputs

  1. We need to ensure that the Optimizer.cs class knows the number of inputs and outputs we intend to use.
  2. Optimizer.cs is located at Assets > 3rd Party > UnityNEAT > SharpNEAT
  3. Look at the LocomotionController.cs. In the FixedUpdate method, we can count the number of inputArr and outputArr we have. Respectively, there are 3 inputs and 5 outputs.
  4. In Optimizer.cs we update NUM_INPUTS and NUM_OUTPUTS accordingly
  5. Ensure that you update the input and output counts anytime you change the actual inputs/outputs you are going to use for different training scenarios

Optimizer.cs

Create Evaluator

  1. Create an empty GameObject named Evaluator
  2. Attach the Optimizer script to the Evaluator as a component
  3. On the Optimizer component, update the following values as a baseline

Create Floor and Walls

  1. Add a GameObject for a floor that is 100x, 1y, 100z (we just used cubes but you can use whatever you want)
  2. Add GameObjects for walls around your perimeter. Ours are 10 units high and enclose the perimeter completely (we used cubes here as well but you can use whatever you want)
  3. Position the Target and smartUnit on the floor and make sure the Target is somewhere off in a corner so the smartUnits have something to achieve, in order to increase their fitness score
  4. Make sure that once you have repositioned the Target and smartUnit you drag each back to it’s respective prefab to update the prefabs. Once you’ve done that, delete the smartUnit from the hierarchy. Make sure to keep the prefab in the prefab folder. We just want to make sure those are instantiated as prefabs so as not to just have a random GameObject floating around the scene
(this is a still photo) We made the walls partially opaque for easy side viewing. Check out all of the failed attempts too lol. We tracked the changes made to each trial to make sure we never tried the same thing more than once.

Hit play and click the “Start EA” button to start training

After many trial and error approaches, we were able to figure out how to get the cubes to bounce within a about 10 generations, which is very small. This number is likely to vary for every person and trial but should not be by too far off.

Watch the generation counter on the bottom left of the “Game” tab. Once it hits 5 or 6, select the Target GameObject and toggle off “Is Tracked” and watch the cubes. If it looks like they are all bouncing erratically around the floor, you should be good. If it looks like 75% or less are bouncing around you have two options. (NOTE: you can click on the Evaluator and adjust the frame rate (FPS) to something like 25 or 10 to see more clearly how the smartUnits are behaving.)

  1. Toggle on “Is Tracked” for the Target and continue training for 5–10 more generations then toggle “Is Tracked” to see how they are doing — rinse and repeat every 5 generations up to 20 or 30 generations.
  2. Click the “Stop EA” button then once the cubes are cleared from the screen automatically, click the “Run Best” button to see what the best cube does. Hopefully, it bounces nicely! Otherwise, restart the game and start training some more.
Watch all the way through the training to the bouncing cube that was trained

Congratulations!

That’s all there is to it, you have learned how to create a NEAT Neural Network in Unity that learns how to move a GameObject using its own unique form of locomotion. You also now have the starting framework to create more complex learning scenarios and outcomes. Congratulations!

So what can be done next with NEAT? It depends on if you want to create a game, something for personal productivity, or for commercial enterprise use. NEAT has been used to create neural nets that can play games such as Mario with high accuracy and efficiency, teaching 3D characters bipedal locomotion, or might be used to train AR / VR people or animals to interact with. Taken a step further, this could be used to train robots or autonomous vehicles within the safety of a 3D physics engine such as Unity. Adding other algorithms to this one can help you achieve the more elaborate plans.

What’s Next?

We are considering creating another part to this article that includes the Boids algorithm which will cause the cubes to travel from location to destination as a flock. From there we may possibly add in some Pathfinding. If this is something you’d like to see, or if you have another idea for an article, please follow us, clap, and leave a comment to that effect below.

Boids Algorithm

Using the same bouncing cubes model adding in the Boids algorithm