In any computer game, the non-player characters (called "entities" in Minecraft) act in ways that are sort of smart -- they have artificial intelligence (AI). AI consists of the entity making a decision then acting on the decision. The decisions though should not happen too frequently, otherwise they would be too good and would beat the players too easily. So instead the decisions are made occasionally and with some random occurrence. For example a EntityCow will stand in one place for a while then occasionally wander around. The AI in Minecraft is mostly related to controlling the movement and the targeting, but theoretically you can add any other type of activity an entity might do.
History Of Minecraft Entity AI
In earlier versions of Minecraft, the AI was simply coded directly in the update functions, including an updateAITick() method and an updateEntityActionState() method. By coding each individually, they probably had to do a lot of cut and paste and ran the risk of inconsistencies in behavior so they came up with a more organized approach to applying AI. So 1.7.x they introduced the "new" AI system.
However, they didn't apply it to all mobs initially. So they made a method called isAIEnabled() which returns true when the entity is supposed to use the new system. However, this doesn't mean that entities that return false have no AI, it just means they use the old style.
In 1.8 they finally converted all the entities to use the new AI system.
This progression is important to understand if you are maintaining a mod across several versions. For example, if you wanted to add a new AI task to all vanilla mobs, you'd find that in 1.7.10 things like EntitySpider would not process the AI. So you would have to copy the AI code into the update methods. On the other hand, in 1.8 you could simply add the AI task.
This tutorial is about the new AI system.
The New AI Architecture
To use the built-in AI processing, you need to use EntityCreature or an extended class. This gives two built in task lists of class type EntityAITasks. There is one task list for movement-related AI (called tasks) and another task list for the targeting AI (called targetTasks), since it is okay for both types to be operating concurrently. Theoretically you could add additional EntityAITasks if you had a set of AI that could be operated concurrently with movement and targeting, but in most cases you can use the existing lists and just avoid any masking.
It is important that different AI classes don't conflict with one another, so Minecraft came up with a way of "masking" the tasks so that similar types of AI can't operate at the same time. This is controlled with the setMutexBits() method explained below.
In addition to masking, there is also a priority order in which the AI is tested on whether it should start executing. This is set when you add the task to the task list. For your custom entity, it is usually a good idea to build up your own AI task list, starting with clearing the one from the superclass. That list could just be a combination of existing AI or can include some custom AI.
This tutorial will try to explain all the above, using my EntityElephant as an example.
If you want to see my elephant rear up (using the AI described below) you can see it near the end of the video at: Video of Jabelar's Custom Entities With AI And Animations
Multiple AI Task Lists
As mentioned above, there are actually two built-in task lists: tasks and targetTasks.
These two lists are independent and concurrent. In other words, there can be a task on each list running at the same time, and the priority and mutex bits (explained below) are separate for each list.
As indicated by the task list names, targetTasks is intended for finding new attack targets, and tasks is intended for all other activities such as wandering, actually attacking (based on the target found in the targetTasks AI), begging, etc.
How The AI Executes
Overall the sequence that AI is executed is something like this (on each list separately):
- Minecraft starts at the priority 0 task in the list and checks if it is masked by any already running AI.
- If it is not masked, the shouldExecute() method is checked to see if it returns true.
- If shouldExecute() returns true, then the startExecute() method will be called and this method will initiate the entity according to what it should do for that AI.
- Minecraft continues up the AI list checking steps #2 and #3 above for lower and lower priority (higher priority number) until it gets to the end of the list.
- Then every active AI has the continueExecuting() method checked, and if true that method can update anything according to that AI and if false the method should clean up the entity state to finish that AI.
- In other words, for your AI to execute it needs to not be masked and needs the shouldExecute() method to return true.
Ensure That The New AI Is Enabled
In your custom entity class, you should make sure that the isAIEnabled() method is overridden such that it returns true. For example:
Create Custom AI Task Classes
The class should extend EntityAIBase.
AI Task Class Constructor
The constructor should usually take the custom entity as a parameter. This allows you to both react and change fields from the entity instance.
In the constructor it is very important to set the mutexBits to prevent AI from conflicting. Basically the way this works is that if two AI classes have mutexBits that have any bits that overlap then they cannot operate at the same time. This is important because you don't want the entity to decide to wander when it is in the middle of panicking, and so forth.
The mutexBit should be set according to the following guidelines:
- If you are extending or copying a vanilla AI, you'll typically be okay using same mutexBits setting but consider the following:
- If you want to make it compatible with swimming, begging and watching closest then you should set mutexBits to 1.
- If you want to make it compatible with swimming, but not begging and watching closest you should set mutexBits to 3.
- If you want to make it compatible with begging and watching closest, but incompatible with swimming you should set mutexBits to 5.
- If you want to make a new AI that is incompatible with everything vanilla, then you should set the mutexBits to 7.
- If you want to make a new AI that is compatible with everything vanilla, then you can choose to set mutexBits to 8 (or any larger power of 2).
AI Task shouldExecute() Method
The shouldExecute() method will be tested every tick that the AI isn't masked. If it returns true then the AI will begin and startExecute() will be called.
The shouldExecute() method should have whatever code makes sense for your entity. For example, if you want your entity to run away from creepers that get too close, then you would check for any creepers nearby and return true if it finds one.
For example, I wanted my elephant entity to rear up when attacked instead of running away. I actually set the isRearingFirstTick() in my entity class and then check it here.:
AI Task startExecuting() Method
The startExecuting() method is called the first time shouldExecute() returns true. It can be used to set up some stuff if needed. I personally organize my code so that the continueExecuting() method does more of the work. So I all have is:
AI Task continueExecuting() Method
The continueExecuting() method is called every tick and used to test whether the AI should continue. You can also use this method to do whatever further processing you want as appropriate to the continuing AI.
In my example, I was having an elephant entity rear up. Since rearing would take some time, I had a counter (in the entity class) that would count down to determine whether it should be finished. So all I had to do was to test whether that counter had counted down.
If you find that the AI should finish, you can clean up the values of the fields in your entity. For example here I turn off the isRearing() and then set the elephant to attack back.
Setup Your Entity AI Task List
In your custom entity class' constructor you need to set up the task lists. So I create a method called setupAI() and then call that from the constructor. Since it is just a list, it is a good idea to clear the list since the EntityCreature superclass has already added some tasks.
I consider the navigator setting to be related to AI, so I set it here as well.
You should think about the priority numbers assigned. If an two tasks mask each other, then the higher priority one will have a chance to more often (since it will mask the lower priority one). To put it another way, a low priority task (one with higher number) will only happen if all the lower priority masking AI are not executing.
Anyway, here is the example of how I created the task list for my elephant entity. Note that I combined the built-in AI with two custom AI (EntityAIPanicHerdAnimal and EntityAIHurtByTargetHerdAnimal). Since my elephant was a mostly passive animal that only attacked when it was attacked, I didn't have it actively target anything except when it was hurt. In contrast, I have created a tiger entity that actively hunted pigs, in which case there was an additional targeting task. For tamable entities you might also add a task where it targets anything that hurts the owner, and another task where it targets anything the owner targets. I think you get the idea..
More On The Task Priority Numbers
It is important to note that the numbers assigned during the addTask()are NOT indexes. This means that:
- You cannot replace a task by simply adding another task with the same number.
- The numbers do not have to be unique, or consecutive. You can inspect how this works by looking at the source for the canUse() method in the EntityAITasks class. Basically it iterates through the list and compares the priority of the currently executing task to the next one in the list and the first one it finds with higher priority is executed to completion. So if two have same priority, then the one that was added to the list first will have actual priority.
- You can't use the number to look up the task, instead if you need to do this (maybe to remove a task later) you can create a field for each instance so I can reference it later if I need. For example, you would create fields like the following, and then reference these during the addTask() method calls:
I hope this helps your understanding. Please feel free to suggest corrections and improvements.
Appendix: The MutexBits Settings For The Built-In AI Classes
To better understand what tasks are masked by the MutexBits, here is a listing of the settings for each built-in AI task.
Category 1 (seems to be most "targeted movement" type AI)
Category 2 (seems to be fairly "passive" type AI)
Category 3 (seems to be "interactive" AI):
Category4 (swimming is category on its own, and is compatible with a lot of stuff)
Category 7 (seems like non-AI, like being controlled by the player)
Uncategorized (I'm not actually sure how these are handled, but I assume they are compatible with everything):