-
Notifications
You must be signed in to change notification settings - Fork 15
Making an Entity With SmartBrainLib
It's surprisingly easy to get an entity working on SmartBrainLib - that being one of the main points of the mod. No longer do you need to have an entire additional class for the mob's AI, or weird long chains of methods and immutable lists.
Now we can do it all neatly in the entity class!
NOTE: Only entities that extend LivingEntity can use the brain system. This is an inherent limitation of the vanilla system, which SBL tries to maintain compatibility with.
Here's a couple examples of entities using SmartBrainLib:
- AoA Elite Smash - A complex boss monster with phases, multiple attack patterns and reactive AI
- SBLSkeleton - A faithful recreation of the vanilla Skeleton, using SmartBrainLib
Firstly, we'll implement SmartBrainOwner on our entity:
public class MyMob extends LivingEntity implements SmartBrainOwner<MyMob>Then we'll need to return the SmartBrainProvider for our entity, functionally replacing the vanilla system:
@Override
protected Brain.Provider<?> brainProvider() {
return new SmartBrainProvider<>(this);
}And lastly, tick our brain so it can function. If you're extending Mob you can use customServerAiStep, otherwise use serverAiStep:
@Override
protected void customServerAiStep() {
tickBrain(this);
}And that's it! Your entity is now running on the SmartBrain system. This doesn't really mean a lot yet however, since we haven't actually given the brain anything to do. So now we can give it some behaviours and sensors.
Sensors are the 'senses' of your entity. They're the continually-running tasks that pick up external input and save it into the brain of the entity, for use in behaviours. By default, most sensors run once per second, but this can be configured on any ExtendedSensor. We can run sensors much less frequently than traditional goals here, because we're storing full objects, not just references, so we can just use the stored objects to get up-to-date positions and data later as-needed.
To add sensors to our entity, we'll override the getSensors
@Override
public List<ExtendedSensor<MyMob>> getSensors() {
}Let's add a couple basic sensors. For most basic entities, you'll only need two:
@Override
public List<ExtendedSensor<MyMob>> getSensors() {
return ObjectArrayList.of(
new NearbyLivingEntitySensor<>(), // This tracks nearby entities
new HurtBySensor<>() // This tracks the last damage source and attacker
);
}That's it! This keeps track of all nearby living entities for use later, as well as who hit us last. It's that simple
Now that we have some sensors, we'll add some behaviours to make use of the memories, and make our mob actually do something.
SBL pre-prepares a couple activity types by default since most entities use them. You're free to ignore them and use handleAdditionalBrainSetup if you want, but for the most part you should only really need to use the builtin activity methods.
We'll use the builtin activity methods here:
@Override
public BrainActivityGroup<MyMob> getCoreTasks() { // These are the tasks that run all the time (usually)
return BrainActivityGroup.coreTasks(
new LookAtTarget<>(), // Have the entity turn to face and look at its current look target
new MoveToWalkTarget<>()); // Walk towards the current walk target
}
@Override
public BrainActivityGroup<MyMob> getIdleTasks() { // These are the tasks that run when the mob isn't doing anything else (usually)
return BrainActivityGroup.idleTasks(
new FirstApplicableBehaviour<MyMob>( // Run only one of the below behaviours, trying each one in order. Include the generic type because JavaC is silly
new TargetOrRetaliate<>(), // Set the attack target and walk target based on nearby entities
new SetPlayerLookTarget<>(), // Set the look target for the nearest player
new SetRandomLookTarget<>()), // Set a random look target
new OneRandomBehaviour<>( // Run a random task from the below options
new SetRandomWalkTarget<>(), // Set a random walk target to a nearby position
new Idle<>().runFor(entity -> entity.getRandom().nextInt(30, 60)))); // Do nothing for 1.5->3 seconds
}
@Override
public BrainActivityGroup<MyMob> getFightTasks() { // These are the tasks that handle fighting
return BrainActivityGroup.fightTasks(
new InvalidateAttackTarget<>(), // Cancel fighting if the target is no longer valid
new SetWalkTargetToAttackTarget<>(), // Set the walk target to the attack target
new AnimatableMeleeAttack<>(0)); // Melee attack the target if close enough
}And that's it! We have a basic functioning entity that walks around randomly and fights players! We can then add more behaviours if we want to do additional actions.
The order of behaviours and sensors are both deterministic, so the order that you enter them in matters.