NOTE: This article is a rewrite of a very old AI tutorial: http://yrrep.me/dota/dota-simple-ai.html
I have encountered many questions about AI on the modding irc over the time, so I decided to write up a tutorial for a very basic AI that can be used in Lua. The term AI might seem intimidating as a programmer that has little to no experience with it, I will try however to lay out the process for a simple state-driven AI in a way that is as clear as possible. Hopefully by the end of this article writing your own AI does not seem as scary anymore.
We will make a little state-driven AI that mimics how neutrals behave in DotA 2. This means it will do these things:
- It will stand idle in its location until an enemy comes in range.
- After spotting an enemy it will run to attack them.
- If the neutral runs too far from its initial 'idle location' it will return back to it.
- Repeat from the first point.
The first phase to making reliable AI (in the sense that it will always do what you expect it to) is planning. I personally think that making a diagram representing the different states and the transitions between these states are a big help when making an AI like this. The more effort you put into this diagram, the easier the actual implementation of your AI will be.
For our neutral example I have translated the text describing the unit's behaviour into a state diagram, which contains all possible states and the conditions for transitioning between these states. The result is the following diagram:
These diagrams can be made with any software with drawing possibilities such as paint, photoshop or word. I really like using https://www.draw.io/, which is an online drawing tool specialised for drawing diagrams and graphs.
In the diagram you can see the different states represented by boxes and transitions represented by arrows. The labels on the arrows describe when this transition happens.
To show how to translate one state to code I will give the example implementation of the aggressive state. I am implementing each state as a 'think' function that will check if any of the transition conditions are true, and execute that transition if they are true.
Look at the 'Aggressive' state in the above diagram. It has two transitions, so I would expect the 'AggressiveThink' function to contain one check for 'Target died', one check for 'Out of leash range', and some aggressive behavior that happens inside the state.
This translates to the following code:
This way of translating your state diagram to code will always work as long as you can write code describing your transition conditions.
Now we have one function that describes one 'tick' of one of our AI states, how do we make sure this is called?
The easiest way to create an AI tied to one unit is to make the AI a Lua modifier. This modifier has some very convenient properties built in:
- The AI will stop once the unit dies
- The modifier provides convenient created/destroyed handlers to setup/cleanup your AI
- The modifier provides an interval think
So really, the very core of your AI comes down to calling
StartIntervalThink(interval) in your modifier's
OnCreated, and then in the
OnIntervalThink calling the correct 'state' function that you created like in the previous section.
Below is the complete state diagram from above implemented as AI. This AI can be added to
unit by calling:
I pass in some parameters to the AI behavior when I apply it, allowing for customization per-unit. Keep in mind this modifier is just like any other Lua modifier, so you can execute any code you can also call in regular modifiers.
This tutorial only covers a very basic concepts for making your first AI, but if you want to extend this here are some more interesting ideas:
- Since you are using a lua modifier, you do not have to change state in a think function, you can also just register a modifier event listener and change state inside those!
- Generalize state classes and give each state
- You can nest these AIs! You could make the internal behavior of one state be its own AI built in the same way.