https://github.com/hongshanyin/Aperi_Oculos
-
Project Overview
1.1 Design Philosophy
Aperi Oculos is a perception layer framework designed for Minecraft:
The Aperi Oculos (Perception Layer) contains the Vision System and the Hearing System (VibrationSystem). It broadcasts events and provides an API to the Upper-level AI Mods (Decision Layer), which handle things like Behavior Trees / State Machines, Group Alert Logic, and Pathfinding.
Core Principles:
-
Single Responsibility: Only answers "What can the mob perceive?"
-
Zero Coupling: Contains no AI behavior logic.
-
Data-Driven: Highly configurable, supports modpack customization.
-
Performance First: Multi-level caching + lazy evaluation + cheap checks first.
Vision System (VisionSystem)
Workflow
The process starts with a ServerTickEvent. Every 10 ticks, it iterates through all players. It checks if there is a mob near a player. If yes, it performs a distance check to see if the mob is within FOLLOW_RANGE. If yes, it performs a Field of View (FoV) angle check. If the player is within the FoV, it performs a light level check. If the player is not within the sneaking light range, it performs a line of sight (LOS) check with caching. If the LOS is clear, it triggers a TargetSpottedEvent, followed by conditional logging.
Key Features
-
Proactive Scanning Mechanism
// Player-centric, avoids iterating through all mobs
AABB scanBounds = player.getBoundingBox().inflate(MAX_SCAN_RADIUS);
for (PathfinderMob mob : level.getEntitiesOfClass(PathfinderMob.class, scanBounds)) {
if (AperiOculosAPI.canSee(mob, player)) {
MinecraftForge.EVENT_BUS.post(new TargetSpottedEvent(mob, player));
}
} -
Five-level Check Gradient
Check Order: Invisibility → Distance → FoV → Light Level → Line of Sight
Performance Cost: O(1) O(1) O(1) O(1) O(n) [n=number of blocks in path] -
Invisibility Check
-
If the target has the Invisibility effect (MobEffects.INVISIBILITY), it cannot be seen.
-
Exception: If the target also has the Glowing effect (MobEffects.GLOWING), it can be seen.
-
This implements a vanilla-like invisibility mechanism, but more efficiently.
-
Night Vision Exemption
-
Supports potion effects (MobEffects.NIGHT_VISION)
-
Supports entity tags (#minecraft:undead)
-
Supports direct IDs (minecraft:spider)
Hearing System (VibrationSystem)
How it Works
Based on Minecraft's vanilla GameEvent system (the same source as Sculk Sensors), it listens for all "vibration" events that occur in the world.
@SubscribeEvent
public void onGameEvent(VanillaGameEvent event) {
GameEvent gameEvent = event.getVanillaEvent();
Vec3 sourcePos = event.getEventPosition();
// Get the base propagation radius of the event
int baseRadius = gameEvent.getNotificationRadius();
// Calculate the effective range for each potential listener
for (Mob listener : nearbyMobs) {
// Read from Capability
double hearingMultiplier = getHearingMultiplier(listener);
double effectiveRange = baseRadius * hearingMultiplier;
if (distance <= effectiveRange && !isOccluded(listener, sourcePos)) {
MinecraftForge.EVENT_BUS.post(
new VibrationPerceivedEvent(listener, sourcePos, gameEvent, ...)
);
}
}
Capability System
Storage: Attaches an IHearingCapability to each LivingEntity.
// Read
double multiplier = entity.getCapability(HearingCapabilityProvider.HEARING_CAPABILITY)
.map(IHearingCapability::getHearingMultiplier)
.orElse(1.0);
// Modify (needs to be synced to the client)
entity.getCapability(HearingCapabilityProvider.HEARING_CAPABILITY)
// Hearing increased by 2x
.ifPresent(cap -> cap.setHearingMultiplier(2.0));
Custom Attraction Ranges
The default propagation range can be overridden for specific GameEvents:
[hearing]
customAttractionRanges = [
"minecraft:projectile_land = 16.0", # Projectile landing: 16 blocks
"minecraft:hit_ground = 8.0", # Entity landing: 8 blocks
"minecraft:explode = 32.0" # Explosion: 32 blocks
]
Attraction duration (10 seconds)
vibrationAttractionDurationTicks = 200
Occlusion Detection
// Optional: Whether sound can pass through walls without loss
// true=enable occlusion attenuation, false=sound passes through walls
enableVibrationOcclusion = false