1.1Percepción graduada por jugador
1.1Per-player graded perception
AIPerception · C++ · Behavior Tree
Problema
AIPerception es binario: te ve o no te ve, y cambia de estado en un frame. En terror eso produce dos fallos que se sienten mal: detección instantánea al cruzar el borde del cono, y pérdida igual de rápida tras una esquina. En coop de 4 hay un tercero: con un solo «objetivo actual», el enemigo oscila entre dos jugadores equidistantes y no persigue a ninguno.
Problem
AIPerception is binary: it sees you or it doesn't, and it flips state in one frame. In horror that produces two failures that feel wrong: instant detection when you cross the edge of the cone, and equally instant loss around a corner. In 4-player co-op there is a third: with a single «current target», the enemy oscillates between two equidistant players and chases neither.
Qué decidí
Un medidor continuo 0→1 por jugador, almacenado en el AIController y no en el
pawn: el conocimiento del enemigo no es propiedad de su cuerpo. Sube con
Gain × DistFactor × PostureFactor × Δt. DistFactor interpola de 1.0 a
0.25 entre 500 y 1500 unidades; PostureFactor va de 0.35 agachado a 1.5 corriendo.
El umbral 0.5 escribe la posición a investigar; 1.0 escribe el objetivo.
What I decided
A continuous 0→1 meter per player, stored on the AIController and not on the
pawn: the enemy's knowledge is not a property of its body. It rises with
Gain × DistFactor × PostureFactor × Δt. DistFactor interpolates from
1.0 to 0.25 between 500 and 1500 units; PostureFactor goes from 0.35 crouched to
1.5 sprinting. Threshold 0.5 writes the position to investigate; 1.0 writes the
target.
Por qué gana a lo obvio
La solución ingenua es un temporizador — «si te veo N segundos, te detecto» — que no distingue estar a 15 metros agachado de estar a 3 metros corriendo. Aquí sale una curva que el jugador puede leer y contra la que puede jugar.
Why it beats the obvious
The naive solution is a timer — «if I see you for N seconds, I detect you» — which doesn't tell being 15 metres away crouched from 3 metres away sprinting. This gives a curve the player can read and play against.
Dos detalles que solo aparecen jugando
- Hold de 0.75 s antes de decaer, para que romper la línea de visión un frame no devuelva el medidor a cero.
- Sticky bonus de 0.15 al objetivo ya elegido, usado solo para comparar candidatos y no para los umbrales. Mata la oscilación entre jugadores sin hacer al enemigo más agresivo.
Two details that only show up in play
- A 0.75 s hold before decay, so breaking line of sight for one frame doesn't drop the meter back to zero.
- A 0.15 sticky bonus for the already-chosen target, used only to compare candidates and not for the thresholds. It kills the oscillation between players without making the enemy more aggressive.
Integración
El sistema entró sin refactorizar nada aguas abajo: su única salida son claves de blackboard que ya existían.
Integration
The system went in without refactoring anything downstream: its only output is blackboard keys that already existed.
Por qué hay C++ aquí
SensesConfig es protected + EditDefaultsOnly,
y GetSenseConfig / ConfigureSense no son UFUNCTION.
Sin un wrapper en C++ (UBlueprintFunctionLibrary), el DataAsset no puede gobernar radios
ni ángulo de visión en runtime.
Why C++ here
SensesConfig is protected + EditDefaultsOnly,
and GetSenseConfig / ConfigureSense are not
UFUNCTION. Without a C++ wrapper (UBlueprintFunctionLibrary), the
DataAsset cannot drive sight radii or cone angle at runtime.