Entity AI

Entity AI (not to be confused with actual AI) is the logic that controls agents. It is structured using goals (e.g. defeat enemies) that an entity tries to achieve using smaller scaled tasks (e.g. go to enemy and hit him with your weapon). The following sections will describe what the goals, tasks and other components of the entity AI API are, how you can change the behavior of entities, and describe how you can write your own AI goals.

Agents

An Agent is a type of living Entity that supports AI and thus simulates the typical behavior of the mobs. You can check whether an Entity is also an Agent by simply trying to cast it.

import org.spongepowered.api.entity.living.Agent;

World world = ...;
UUID uuid = ...;

Agent entity = (Agent) world.getEntity(uuid).get();

if (entity.getAgentData().aiEnabled().get()) {
    configureAI(entity);
}

After the cast we can also check whether the AI is actually enabled. Some plugins disable it to simulate statues, guards or shops.

Goals

In vanilla Minecraft there are two types of Goals.

The first one is the default one: NORMAL. This part of the AI controls the behavior of the entity. This goal contains tasks such as attacking a target using a melee or ranged weapon and looking idly around the area if there are no enemies nearby. If we want to override or modify the AI we will usually change this goal and its tasks.

The second goal is more for hostile mods: TARGET. This goal contains tasks that help the entity to identify which target it should select and then attack using the normal goal. The target goal contains tasks such as select nearest attackable target and select the enemy that attacked you first.

The following code gets the NORMAL goal from the entity and clears it.

import org.spongepowered.api.entity.ai.Goal;
import org.spongepowered.api.entity.ai.GoalTypes;

Agent entity = ...;

Optional<Goal<Agent>> normalGoal = entity.getGoal(GoalTypes.NORMAL);
if (normalGoal.isPresent()) {
    normalGoal.get().clear();
}

An entity with an empty goal will not move by itself, however it will complete its current action/movement and might take an idle pose (i.e. look straight ahead). The AI does not affect any sound effects that an entity might play randomly.

備註

Please note that editing the goals or tasks of an entity will directly edit the entity. This behavior differs from other parts of the API where only copies are returned which must be applied afterwards. Thus, it is not possible to transfer the tasks from one entity to another.

備註

Any changes made to an entities AI will be lost after a server restart or after the entity was unloaded.

Tasks

The behavior of many entity types is similar to each other. For this reason, the entity AI split into multiple smaller and reusable parts called AITasks. Each of the AITasks represents a single behavior trait of an entity such as WATCH_CLOSEST, which makes the entity look at the nearest matching entity, or AVOID_ENTITY, which makes the entity flee from certain matching entities.

備註

Vanilla Minecraft itself provides a huge variety of AITasks, however most of them are not yet accessible via the API.

Adding Additional AITasks

Adding additional AITasks to an Entitys goal is pretty easy. We start with creating with a simple AITask.

import org.spongepowered.api.entity.ai.task.builtin.WatchClosestAITask;

Agent entity = ...;
Goal<Agent> goal = ...;

WatchClosestAITask watchClosestAiTask = WatchClosestAITask.builder()
        .chance(1)
        .maxDistance(30)
        .watch(Player.class)
        .build(entity);
goal.addTask(0, watchClosestAiTask);

In this example we create an WatchClosestAITask using the associated builder. Using the build we define that the chance of this goal triggering is 100%. Thus, the entity will look at the closest Player in a range of 30 blocks. We assign zero as a priority, which is a high priority, thus this goal takes precedence above almost all other tasks.

備註

Minecraft uses low values as high priority and high values as low priority. By default Minecraft uses priority values from zero to roughly ten.

Removing Certain AITasks

Removing the correct AITasks can be a little tricky, especially if modded entities or custom AI come into play. The Sponge API tries to provides a disambiguator. Calling AITask#getType() returns an AITaskType which can be used to differentiate between the existing types of tasks.

First we try the simple version which will only work if there are no AI altering mods present:

Goal<Zombie> goal = ...;

AITask<Zombie> attackTask = (AITask<Zombie>) goal.getTasks().get(1); // EntityAIZombieAttack
goal.removeTask(attackTask);

In this case we blindly rely on the fact that in vanilla Minecraft 1.12.2 Zombies will have the EntityAIZombieAttack task as their second task. After that you no longer have to fear attacks from that zombie. As you can imagine this strategy has some flaws, as it requires explicit knowledge about the order of AI tasks in the given entity.

備註

It is not possible to remove AITasks directly from the list returned by Goal#getTasks() because it is immutable.

A much simpler but also less powerful way of removing tasks is removing them by their type. This is the approach you should follow, if you don’t need the task’s internals to identify which task should be removed.

goal.removeTasks(AITaskTypes.WANDER);

In this case we remove all AITasks that have the AITaskType WANDER.

備註

Currently this way is seriously limited in usability due to the incomplete AITaskType support in the API.

If you want to remove all AITasks, because you want to configure the entity’s AI from scratch, you can also use Goal#clear().

Implement Your Own AITask

We can also try to implement our own AITasks. The Implement Your Own AITasks page describes the process and some obstacles you will encounter.

事件

The AI API as well as most other parts of the SpongeAPI utilize events. You can read more about events here.

The AI API itself makes use of the following 3 events:

The AITaskEvent.Add event is published whenever a new AITask has been added to a Goal, likewise, the AITaskEvent.Remove event is published if an AITask has been removed.

The SetAITargetEvent is published every time that an Agent selects a new target (usually for attacking) or drops a target.

All of these events are cancelable, thus allowing us to prevent unwanted third-party changes to our custom entities.