9.2 KiB
9.2 KiB
title, type, weight
| title | type | weight |
|---|---|---|
| NPC AI | docs | 3 |
The NPC AI system provides intelligent decision-making through blackboards, decision makers, sensors, and instructions.
Packages:
com.hypixel.hytale.server.npc.blackboardcom.hypixel.hytale.server.npc.decisionmakercom.hypixel.hytale.server.npc.sensorinfocom.hypixel.hytale.server.npc.instructions
Blackboard System
The Blackboard is a shared memory space where NPC components communicate through key-value pairs.
Blackboard Class
public class Blackboard {
private Map<BlackboardKey<?>, Object> data;
// Store value
public <T> void set(BlackboardKey<T> key, T value);
// Retrieve value
public <T> T get(BlackboardKey<T> key);
public <T> T getOrDefault(BlackboardKey<T> key, T defaultValue);
// Check existence
public boolean has(BlackboardKey<?> key);
// Remove value
public void remove(BlackboardKey<?> key);
// Clear all
public void clear();
}
BlackboardKey
Type-safe keys for blackboard access:
// Predefined keys
public class BlackboardKeys {
public static final BlackboardKey<Entity> TARGET =
new BlackboardKey<>("target", Entity.class);
public static final BlackboardKey<Vector3d> HOME_POSITION =
new BlackboardKey<>("home_position", Vector3d.class);
public static final BlackboardKey<Float> ALERT_LEVEL =
new BlackboardKey<>("alert_level", Float.class);
public static final BlackboardKey<Boolean> IN_COMBAT =
new BlackboardKey<>("in_combat", Boolean.class);
}
// Custom keys
BlackboardKey<String> CUSTOM_KEY = new BlackboardKey<>("custom_data", String.class);
Using the Blackboard
NPCEntity npc = // get NPC
Blackboard bb = npc.getBlackboard();
// Set target
bb.set(BlackboardKeys.TARGET, targetEntity);
// Get home position
Vector3d home = bb.getOrDefault(BlackboardKeys.HOME_POSITION, npc.getPosition());
// Check combat status
if (bb.getOrDefault(BlackboardKeys.IN_COMBAT, false)) {
// Handle combat
}
Decision Maker System
The Decision Maker evaluates options and selects the best action for the NPC to take.
DecisionMaker Interface
public interface DecisionMaker {
// Evaluate and select best option
Option evaluate(NPCEntity npc, Blackboard blackboard);
// Get all available options
List<Option> getOptions();
// Add option
void addOption(Option option);
}
Option Class
Options represent possible actions:
public class Option {
private String id;
private Evaluator evaluator;
private Action action;
private float basePriority;
// Calculate score based on context
public float evaluate(NPCEntity npc, Blackboard blackboard);
// Execute the action
public void execute(NPCEntity npc, Blackboard blackboard);
}
Evaluator Interface
Evaluators calculate option scores:
public interface Evaluator {
// Return score from 0.0 to 1.0
float evaluate(NPCEntity npc, Blackboard blackboard);
}
// Built-in evaluators
public class Evaluators {
// Returns 1.0 if target exists
public static final Evaluator HAS_TARGET = (npc, bb) ->
bb.has(BlackboardKeys.TARGET) ? 1.0f : 0.0f;
// Returns health percentage
public static final Evaluator HEALTH_PERCENT = (npc, bb) ->
npc.getHealth() / npc.getMaxHealth();
// Returns 1.0 if at home
public static final Evaluator AT_HOME = (npc, bb) -> {
Vector3d home = bb.get(BlackboardKeys.HOME_POSITION);
return npc.getPosition().distance(home) < 5.0 ? 1.0f : 0.0f;
};
}
Creating a Decision Maker
DecisionMaker dm = new StandardDecisionMaker();
// Add attack option
dm.addOption(new Option(
"attack",
Evaluators.HAS_TARGET,
new AttackAction(),
10.0f // High priority
));
// Add flee option
dm.addOption(new Option(
"flee",
(npc, bb) -> npc.getHealth() < 20 ? 1.0f : 0.0f,
new FleeAction(),
15.0f // Higher priority when triggered
));
// Add wander option
dm.addOption(new Option(
"wander",
(npc, bb) -> 0.3f, // Low constant score
new WanderAction(),
1.0f // Low priority fallback
));
npc.setDecisionMaker(dm);
Sensor System
Sensors gather information about the world and update the blackboard.
Sensor Interface
public interface Sensor {
// Process sensor input
void sense(NPCEntity npc, Blackboard blackboard, float deltaTime);
// Get sensor type
String getSensorType();
}
SensorInfo Classes
Sensor information containers:
// Visual detection info
public class VisualSensorInfo {
private List<Entity> visibleEntities;
private float detectionRange;
private float fieldOfView;
public List<Entity> getVisibleEntities();
public boolean canSee(Entity entity);
}
// Audio detection info
public class AudioSensorInfo {
private List<SoundEvent> heardSounds;
private float hearingRange;
public List<SoundEvent> getHeardSounds();
public Vector3d getLoudestSoundPosition();
}
// Threat detection info
public class ThreatSensorInfo {
private List<Entity> threats;
private Entity primaryThreat;
private float threatLevel;
}
Built-in Sensors
// Visual sensor - detects visible entities
public class VisualSensor implements Sensor {
private float range = 20.0f;
private float fov = 120.0f; // degrees
@Override
public void sense(NPCEntity npc, Blackboard bb, float dt) {
List<Entity> visible = findVisibleEntities(npc);
bb.set(BlackboardKeys.VISIBLE_ENTITIES, visible);
}
}
// Proximity sensor - detects nearby entities
public class ProximitySensor implements Sensor {
private float range = 5.0f;
@Override
public void sense(NPCEntity npc, Blackboard bb, float dt) {
List<Entity> nearby = findNearbyEntities(npc, range);
bb.set(BlackboardKeys.NEARBY_ENTITIES, nearby);
}
}
// Damage sensor - reacts to damage taken
public class DamageSensor implements Sensor {
@Override
public void sense(NPCEntity npc, Blackboard bb, float dt) {
if (npc.wasRecentlyDamaged()) {
bb.set(BlackboardKeys.LAST_ATTACKER, npc.getLastAttacker());
bb.set(BlackboardKeys.ALERT_LEVEL, 1.0f);
}
}
}
Instruction System
Instructions define high-level behavioral goals.
Instruction Interface
public interface Instruction {
// Check if instruction should activate
boolean shouldActivate(NPCEntity npc, Blackboard blackboard);
// Execute instruction logic
void execute(NPCEntity npc, Blackboard blackboard, float deltaTime);
// Check if instruction is complete
boolean isComplete(NPCEntity npc, Blackboard blackboard);
// Get priority
float getPriority();
}
Built-in Instructions
// Wander instruction
public class WanderInstruction implements Instruction {
private float wanderRadius;
private float minWaitTime;
private float maxWaitTime;
@Override
public boolean shouldActivate(NPCEntity npc, Blackboard bb) {
return !bb.has(BlackboardKeys.TARGET);
}
}
// Guard instruction
public class GuardInstruction implements Instruction {
private Vector3d guardPosition;
private float guardRadius;
@Override
public void execute(NPCEntity npc, Blackboard bb, float dt) {
if (npc.getPosition().distance(guardPosition) > guardRadius) {
bb.set(BlackboardKeys.MOVE_TARGET, guardPosition);
}
}
}
// Follow instruction
public class FollowInstruction implements Instruction {
private Entity followTarget;
private float followDistance;
}
// Patrol instruction
public class PatrolInstruction implements Instruction {
private List<Vector3d> patrolPoints;
private int currentPoint;
}
Combining AI Systems
public void setupNPCAI(NPCEntity npc) {
// Configure blackboard
Blackboard bb = npc.getBlackboard();
bb.set(BlackboardKeys.HOME_POSITION, npc.getPosition());
bb.set(BlackboardKeys.AGGRO_RANGE, 15.0f);
// Add sensors
npc.addSensor(new VisualSensor(20.0f, 120.0f));
npc.addSensor(new ProximitySensor(5.0f));
npc.addSensor(new DamageSensor());
// Configure decision maker
DecisionMaker dm = new StandardDecisionMaker();
dm.addOption(new Option("attack", hasHostileTarget, attackAction, 10.0f));
dm.addOption(new Option("flee", lowHealth, fleeAction, 15.0f));
dm.addOption(new Option("patrol", isGuard, patrolAction, 5.0f));
dm.addOption(new Option("idle", always, idleAction, 1.0f));
npc.setDecisionMaker(dm);
// Add instructions
npc.addInstruction(new GuardInstruction(guardPost, 10.0f));
npc.addInstruction(new ReactToThreatInstruction());
}
Best Practices
{{< callout type="info" >}} AI Guidelines:
- Use the Blackboard for all inter-component communication
- Keep Evaluators simple and fast - they run frequently
- Use appropriate sensor ranges to balance awareness vs performance
- Design Instructions to be interruptible
- Test AI behavior with various scenarios {{< /callout >}}
{{< callout type="warning" >}} Performance: Large numbers of NPCs with complex AI can impact performance. Consider:
- Reducing sensor update frequency for distant NPCs
- Using LOD (Level of Detail) for AI complexity
- Limiting pathfinding requests {{< /callout >}}