-
Notifications
You must be signed in to change notification settings - Fork 0
Sensing
Sensors periodically refresh perception state onto the blackboard so the planner, tree, and actions don't each have to
re-derive it. CortexRuntime ticks exactly one optional Sensor<E> per agent per tick (skipped while the running
action is LOCKED/EMERGENCY).
@FunctionalInterface
public interface Sensor<E> {
void tick(E agent, Blackboard blackboard);
}The main sensor most agents need. It periodically evaluates a Selector and writes the result to
CommonBlackboardKeys.TARGET.
var validity = VanillaTargetPredicates.players()
.or(VanillaTargetPredicates.abstractVillagers())
.or(VanillaTargetPredicates.ironGolems());
var targetSensor = new TargetSensor<MyEntity>(
TargetSensor.nearestMatching(24.0D, validity), // Selector: retain current if still valid, else nearest match
10, // retarget every 10 ticks (or immediately if target dies)
TargetSensor.lineOfSight() // visibility predicate, enables last-seen tracking
);-
retargetInterval, forced re-evaluation cadence. The sensor still re-checks immediately whenever the current target dies or becomes invalid, regardless of this interval. -
visibilityPredicate(optional, third constructor argument), tested every tick against the current target. While it holds, the sensor additionally maintainsLAST_SEEN_POS/LAST_SEEN_VELOCITY/LAST_SEEN_TICK, frozen at the moment visibility was last confirmed, as opposed toLAST_KNOWN_TARGET_POS, which updates unconditionally every tick regardless of visibility. Agents constructed without a predicate never populate these three keys.TargetSensor.lineOfSight()supplies the commonMob#hasLineOfSightcheck; mods with unusual vision rules (echolocation, blindness effects, seeing through thin walls) should supply their own.
TargetSensor.nearestMatching(range, validity) covers the common case, retain the current target if validity still
accepts it, otherwise scan for the nearest entity within range blocks that does:
public static <E extends Mob> Selector<E> nearestMatching(double range, Predicate<LivingEntity> validity)VanillaTargetPredicates ships a few reusable building blocks matching vanilla's own targeting rules —
players(), abstractVillagers(), ironGolems(), babyTurtlesOnLand(), combine them with Predicate.or.
Write your own Selector directly when "nearest matching" isn't the rule you want (e.g. prefer a specific target type,
or weight by something other than distance).
Pairs with TargetSensor's visibility-gated tracking. Instead of an investigate action always walking to the exact
block a target occupied when last seen, predictInterceptPosition extrapolates forward along its last-known velocity:
var searchPoint = TargetPrediction.predictInterceptPosition(
lastSeenPos, lastSeenVelocity, lastSeenTick, currentTick,
100, // maxStalenessTicks, give up extrapolating beyond this age
0.02D, // minSpeed, below this, treat the target as stationary
2.0D, // minDistance
8.0D, // maxDistance
TargetPrediction.standableIn(level) // rejects predictions embedded in a wall, etc.
);Falls back to the raw lastSeenPos whenever extrapolation isn't warranted: no recorded velocity/tick, the sighting is
too stale, the target was essentially stationary, or the projected point fails the standable check.
TargetPrediction.standableIn(level) is the ordinary ground-mob predicate (open space with solid footing below); mods
with different footprints (wall-crawlers, flyers) should supply their own.
See InvestigateLastSeenTargetAction in Built-in Actions for the ready-made action built on this.
Not a periodic Sensor, these are cheap enough to call directly wherever needed rather than caching every tick:
EnvironmentSensor.isInDarkness(mob, 4); // ambient light <= threshold
EnvironmentSensor.isInWater(mob);
EnvironmentSensor.healthFraction(mob); // in [0, 1]A thin Sensor wrapper around MovementController.hasNearbyDangerEntity (see Navigation and Pathfinding), for
when a behavior tree wants the flag pre-computed on the blackboard rather than recomputing the repulsion scan itself:
public static final BlackboardKey<Boolean> NEAR_HAZARD = BlackboardKey.of("near_hazard", Boolean.class);
var hazardSensor = new HazardSensor<MyEntity>(NEAR_HAZARD);Most agents don't need this dedicated sensor, call MovementController.hasNearbyDangerEntity directly from an action
or planner if you only need the value in one place.
-
Built-in Actions, actions that consume
TARGET/LAST_SEEN_POS/etc. -
Navigation and Pathfinding,
MovementCapabilityfor classifying hazard entities/blocks per mob.