Class AIController
Controls AI navigation, state transitions, and behavior for NPCs using NavMeshAgent. Handles movement, enemy detection, leash logic, waypoints, state management, and provides a virtual camera for aiming abilities at targets during combat.
[RequireComponent(typeof(NavMeshAgent))]
public class AIController : CharacterBehaviour, IAIController, IAINavigation, IAIStateMachine, IAIWaypoints, ICharacterBehaviour
- Inheritance
-
ObjectComponentBehaviourMonoBehaviourNetworkBehaviourAIController
- Implements
- Inherited Members
-
NetworkBehaviour.IsSpawnedNetworkBehaviour.ComponentIndexNetworkBehaviour.NetworkObjectNetworkBehaviour.MAXIMUM_NETWORKBEHAVIOURSNetworkBehaviour.UNSET_NETWORKBEHAVIOUR_IDNetworkBehaviour.ToString()NetworkBehaviour.Reset()NetworkBehaviour.OnValidate()NetworkBehaviour.IsBehaviourReconcilingNetworkBehaviour.ClearReplicateCache()NetworkBehaviour.CreateReconcile()NetworkBehaviour.Reconcile_Reader<T>(PooledReader, ref T)NetworkBehaviour.OnStartServerCalledNetworkBehaviour.OnStartClientCalledNetworkBehaviour.WritePayload(NetworkConnection, Writer)NetworkBehaviour.ReadPayload(NetworkConnection, Reader)NetworkBehaviour.OnStartServer()NetworkBehaviour.OnStopServer()NetworkBehaviour.OnOwnershipServer(NetworkConnection)NetworkBehaviour.OnSpawnServer(NetworkConnection)NetworkBehaviour.OnDespawnServer(NetworkConnection)NetworkBehaviour.OnStartClient()NetworkBehaviour.OnStopClient()NetworkBehaviour.OnOwnershipClient(NetworkConnection)NetworkBehaviour.ClearBuffedRpcs()NetworkBehaviour.ExcludeOwnerFromUnbufferedObserversRpcsNetworkBehaviour.IsClientOnlyNetworkBehaviour.IsServerOnlyNetworkBehaviour.IsHostNetworkBehaviour.IsClientNetworkBehaviour.IsServerNetworkBehaviour.IsDeinitializingNetworkBehaviour.NetworkManagerNetworkBehaviour.ServerManagerNetworkBehaviour.ClientManagerNetworkBehaviour.ObserverManagerNetworkBehaviour.TransportManagerNetworkBehaviour.TimeManagerNetworkBehaviour.SceneManagerNetworkBehaviour.PredictionManagerNetworkBehaviour.RollbackManagerNetworkBehaviour.NetworkObserverNetworkBehaviour.IsClientInitializedNetworkBehaviour.IsClientStartedNetworkBehaviour.IsClientOnlyInitializedNetworkBehaviour.IsClientOnlyStartedNetworkBehaviour.IsServerInitializedNetworkBehaviour.IsServerStartedNetworkBehaviour.IsServerOnlyInitializedNetworkBehaviour.IsServerOnlyStartedNetworkBehaviour.IsHostInitializedNetworkBehaviour.IsHostStartedNetworkBehaviour.IsOfflineNetworkBehaviour.IsNetworkedNetworkBehaviour.GetIsNetworked()NetworkBehaviour.IsManagerReconcilingNetworkBehaviour.ObserversNetworkBehaviour.IsOwnerNetworkBehaviour.IsControllerNetworkBehaviour.HasAuthorityNetworkBehaviour.OwnerNetworkBehaviour.OwnerIdNetworkBehaviour.ObjectIdNetworkBehaviour.LocalConnectionNetworkBehaviour.OwnerMatches(NetworkConnection)NetworkBehaviour.Despawn(GameObject, DespawnType?)NetworkBehaviour.Despawn(NetworkObject, DespawnType?)NetworkBehaviour.Despawn(DespawnType?)NetworkBehaviour.Spawn(GameObject, NetworkConnection, Scene)NetworkBehaviour.Spawn(NetworkObject, NetworkConnection, Scene)NetworkBehaviour.RemoveOwnership()NetworkBehaviour.GiveOwnership(NetworkConnection)NetworkBehaviour.GetInstance<T>()NetworkBehaviour.TryRegisterInstance<T>(T)NetworkBehaviour.UnregisterInstance<T>()NetworkBehaviour.CanLog(LoggingType)MonoBehaviour.IsInvoking()MonoBehaviour.CancelInvoke()MonoBehaviour.StopCoroutine(Coroutine)MonoBehaviour.StopAllCoroutines()MonoBehaviour.destroyCancellationTokenMonoBehaviour.useGUILayoutMonoBehaviour.didStartMonoBehaviour.didAwakeMonoBehaviour.runInEditModeBehaviour.enabledBehaviour.isActiveAndEnabledComponent.GetComponent<T>()Component.TryGetComponent<T>(out T)Component.GetComponentInChildren<T>()Component.GetComponentsInChildren<T>()Component.GetComponentInParent<T>()Component.GetComponentsInParent<T>()Component.GetComponents<T>()Component.GetComponentIndex()Component.CompareTag(TagHandle)Component.transformComponent.transformHandleComponent.gameObjectComponent.tagObject.GetEntityId()Object.GetInstanceID()Object.GetHashCode()Object.InstantiateAsync<T>(T)Object.InstantiateAsync<T>(T, Transform)Object.InstantiateAsync<T>(T, Vector3, Quaternion)Object.InstantiateAsync<T>(T, Transform, Vector3, Quaternion)Object.Instantiate(Object, Vector3, Quaternion)Object.Instantiate(Object, Vector3, Quaternion, Transform)Object.Instantiate(Object)Object.Instantiate(Object, Scene)Object.Instantiate<T>(T, InstantiateParameters)Object.Instantiate<T>(T, Vector3, Quaternion, InstantiateParameters)Object.Instantiate(Object, Transform)Object.Instantiate<T>(T)Object.Instantiate<T>(T, Vector3, Quaternion)Object.Instantiate<T>(T, Vector3, Quaternion, Transform)Object.Instantiate<T>(T, Transform)Object.Destroy(Object)Object.DestroyImmediate(Object)Object.DontDestroyOnLoad(Object)Object.DestroyObject(Object)Object.FindObjectsOfType<T>()Object.FindObjectsByType<T>(FindObjectsSortMode)Object.FindObjectsByType<T>(FindObjectsInactive, FindObjectsSortMode)Object.FindObjectOfType<T>()Object.FindFirstObjectByType<T>()Object.FindAnyObjectByType<T>()Object.FindFirstObjectByType<T>(FindObjectsInactive)Object.FindAnyObjectByType<T>(FindObjectsInactive)Object.FindObjectsByType<T>()Object.FindObjectsByType<T>(FindObjectsInactive)Object.nameObject.hideFlags
Remarks
Every AI state previously drove the NavMeshAgent with the same three-line pattern:
NavMesh.SamplePosition, SetDestination, and then
!pathPending && remainingDistance < 1 to decide it had arrived. All three
steps have failure modes that the pattern silently swallows:
-
SamplePositionreturns false when the sampled point has no NavMesh near it, and the caller then simply did not move — with no retry and no fallback. - An unreachable destination does not fail. Unity returns a partial path to the closest reachable point, so the agent walks to the near side of the obstacle and stops.
-
remainingDistanceis 0 when there is no path at all, andpathPendingis false at the same moment — so "no path" and "arrived" are indistinguishable. A patrol whose first waypoint failed to sample cycled through every waypoint once per tick without walking anywhere.
These helpers collapse that pattern into calls that report what actually happened.
Fields
ARRIVAL_TOLERANCE
Distance from the destination at which an NPC counts as arrived, in world units.
public const float ARRIVAL_TOLERANCE = 1
Field Value
AbilityRotation
[Header("Ability Rotation")]
[Tooltip("Optional ability rotation for condition/sequence-based ability selection.")]
public AIAbilityRotation AbilityRotation
Field Value
AggressionDamageWeight
[Header("Aggression / Threat")]
[Tooltip("Aggression points per 1 damage taken.")]
public float AggressionDamageWeight
Field Value
AggressionDecayRate
Points per second that each entry decays when no new events occur.
[Tooltip("Aggression decay per second.")]
public float AggressionDecayRate
Field Value
AggressionHealingWeight
Points awarded per 1 point of healing an enemy of the NPC witnesses.
[Tooltip("Aggression points per 1 healing witnessed on a combat participant.")]
public float AggressionHealingWeight
Field Value
AggressionHitBonus
Flat points added per hit, regardless of damage amount.
[Tooltip("Flat aggression per hit.")]
public float AggressionHitBonus
Field Value
AggressionStaleTimeout
Seconds after last event before an entry is removed entirely.
[Tooltip("Seconds before a stale aggression entry is pruned.")]
public float AggressionStaleTimeout
Field Value
AggressionVarietyChance
Chance (0-1) that target selection ignores the top-threat target and picks a secondary one.
[Range(0, 1)]
[Tooltip("Chance to pick a non-top-threat target for variety.")]
public float AggressionVarietyChance
Field Value
AiTickRate
How many times per second this NPC's brain runs, in hertz.
[Header("Tick Rate")]
[Tooltip("Brain updates per second. 5-10 is the useful band. Rounded to a divisor of the network tick rate.")]
[Range(1, 30)]
public float AiTickRate
Field Value
Remarks
Rounded to the nearest whole divisor of the FishNet tick rate, so the brain always lands on network ticks and never drifts against them. At the project's 30 Hz network tick, 8 Hz resolves to every 4th tick — 7.5 Hz exactly, forever, on any hardware. EffectiveAiTickRate reports what a requested rate actually resolved to.
5-10 Hz is the useful band for an MMO brain. Decisions below about 5 Hz start to read as sluggish reaction time to a player; above about 10 Hz the NPC is re-deciding faster than its own pathing and animation can respond, so the extra ticks buy nothing but CPU.
AllyScanTimer
Seconds until a healer archetype rescans for wounded allies.
[NonSerialized]
public float AllyScanTimer
Field Value
Archetype
[Header("Archetype")]
[Tooltip("Optional archetype asset that fills in the state and tuning slots below.")]
public AIArchetypeTemplate Archetype
Field Value
AttackCooldownTimer
Seconds remaining before this NPC may activate another ability.
[NonSerialized]
public float AttackCooldownTimer
Field Value
Remarks
Lives on the controller, not on the attacking state, because the state is a ScriptableObject shared by every NPC of that archetype — a timer stored there would be one global pacing clock for the whole population.
AttackingState
Reference to the attacking state for combat behavior.
public BaseAIState AttackingState
Field Value
AvoidancePriority
The avoidance priority for this agent (affects how strongly it avoids other agents).
public AgentAvoidancePriority AvoidancePriority
Field Value
BehaviorTree
[Header("Behavior Tree")]
[Tooltip("Optional behavior tree for high-level decision making.")]
public AIBehaviorTree BehaviorTree
Field Value
BossScript
[Header("Boss Script")]
[Tooltip("Optional boss script for phased encounters.")]
public BossScript BossScript
Field Value
CachedHealTarget
The ally a healer archetype last chose to heal, re-validated cheaply between scans.
[NonSerialized]
public ICharacter CachedHealTarget
Field Value
DeadState
Reference to the dead state for death logic.
public BaseAIState DeadState
Field Value
EnemySweepRate
How often (in seconds) to sweep for nearby enemies.
public float EnemySweepRate
Field Value
FlankTimer
Seconds of manoeuvring budget remaining for RogueAttackingState's flanking attempt. Per-NPC for the same reason as AttackCooldownTimer.
[NonSerialized]
public float FlankTimer
Field Value
Group
The NPC group this controller belongs to. Set by NPCGroup.
[NonSerialized]
public NPCGroup Group
Field Value
GroupRole
This NPC's role within its group. Set by NPCGroup.
[NonSerialized]
public NPCGroupRole GroupRole
Field Value
IdleState
Reference to the idle state for passive behavior.
public BaseAIState IdleState
Field Value
InitialState
[Header("States")]
public BaseAIState InitialState
Field Value
LodSettings
[Header("AI LOD")]
[Tooltip("Optional LOD settings for distance-based AI throttling.")]
public AILodSettings LodSettings
Field Value
LookTarget
The current look target for the AI (used for facing/rotation).
public Transform LookTarget
Field Value
- Transform
OrbitAngle
Per-NPC orbit angle (radians) used by OrbitState. Stored here instead of on the ScriptableObject to avoid the shared-instance mutable state problem.
[NonSerialized]
public float OrbitAngle
Field Value
PatrolState
Reference to the patrol state for waypoint movement.
public BaseAIState PatrolState
Field Value
Personality
[Header("Combat Personality")]
[Tooltip("Optional combat personality for data-driven ability preference.")]
public AICombatPersonality Personality
Field Value
PetStuckTimer
Seconds a pet has spent unable to reach its owner. Drives the follow state's teleport escape hatch. Per-NPC, for the same reason as the other timers here.
[NonSerialized]
public float PetStuckTimer
Field Value
RandomizeState
If true, the AI will randomize its movement state.
public bool RandomizeState
Field Value
RepathInterval
[Header("Pathfinding")]
[Tooltip("Minimum seconds between NavMeshAgent.SetDestination calls via SetThrottledDestination.")]
public float RepathInterval
Field Value
RetreatState
Reference to the retreat state for fleeing behavior.
public BaseAIState RetreatState
Field Value
ReturnHomeState
Reference to the return home state for leash logic.
public BaseAIState ReturnHomeState
Field Value
RotationIndex
Per-NPC rotation index used by AIAbilityRotation in Sequence mode. Tracks which entry in the rotation to try next.
[NonSerialized]
public int RotationIndex
Field Value
StuckTimeout
Seconds of no progress before the NPC is considered stuck.
[Header("Navigation Recovery")]
[Tooltip("Seconds of no movement, while trying to move, before the NPC is considered stuck.")]
public float StuckTimeout
Field Value
StuckWarpTimeout
Seconds of continued no progress after the first recovery attempt before the NPC is teleported to its destination. 0 disables teleport recovery.
[Tooltip("Seconds stuck before the NPC is warped free. 0 = never warp.")]
public float StuckWarpTimeout
Field Value
SubStateTimer
Countdown used by bounded combat sub-states such as OrbitState to know when their manoeuvre is finished. Per-NPC, for the same reason as the other timers here.
[NonSerialized]
public float SubStateTimer
Field Value
SweepHits
Buffer for storing colliders hit during enemy sweep. Grown on demand — see SweepForEnemies(AIController, List<ICharacter>).
public Collider[] SweepHits
Field Value
- Collider[]
Remarks
Not a fixed 20. A non-allocating overlap returns at most buffer.Length results and
says nothing about how many it discarded, and the ones it discarded were chosen by the
physics broadphase — so an NPC in a fight larger than its buffer detected an arbitrary,
run-varying subset of its attackers and ignored the rest. The sweep re-queries into a
larger buffer through TargetOrdering.TryGrowQueryBuffer until it stops coming back
full, which is the same treatment every other spatial query in the project gets.
TurnRate
How quickly the NPC turns to face its look target, in radians-ish per second.
[Header("Facing")]
[Tooltip("How quickly the NPC turns to face its target. Higher is snappier.")]
public float TurnRate
Field Value
Remarks
Feeds an exponential smoothing factor, so the value is a rate rather than a hard angular speed: higher snaps faster, and the result is identical at any frame rate.
UnreachableTargetTimer
Seconds the NPC has spent unable to reach its combat target.
[NonSerialized]
public float UnreachableTargetTimer
Field Value
WanderState
Reference to the wander state for random movement.
public BaseAIState WanderState
Field Value
WasAttackingLastTick
True when the previous combat tick resolved to attacking or holding position rather than moving. Feeds the range hysteresis in AICombatDecision.
[NonSerialized]
public bool WasAttackingLastTick
Field Value
Waypoints
The waypoints available to this AI controller.
public Vector3[] Waypoints
Field Value
- Vector3[]
Properties
Agent
The NavMeshAgent component used for navigation.
public NavMeshAgent Agent { get; }
Property Value
- NavMeshAgent
Aggression
Convenience accessor for the underlying aggression controller.
public AggressionController Aggression { get; }
Property Value
AggressionState
The aggression (threat) state for this NPC. Manages the threat table, event subscriptions, and target re-evaluation timer. One instance per NPC — not shared.
public AggressionState AggressionState { get; }
Property Value
AiTickIndex
Monotonic count of brain updates. Drives the LOD stagger.
public uint AiTickIndex { get; }
Property Value
BossState
Runtime state for the boss script. Null when no BossScript is assigned.
public BossScriptState BossState { get; }
Property Value
CombatTargetBuffer
Reusable buffer for collecting targets during combat state updates. Used by attacking states to avoid per-frame GC allocations.
public List<ICharacter> CombatTargetBuffer { get; }
Property Value
CurrentLodTier
Current AI LOD tier. Determines how frequently this NPC's brain ticks.
public AILodTier CurrentLodTier { get; }
Property Value
CurrentState
The current AI state.
public BaseAIState CurrentState { get; }
Property Value
CurrentWaypointIndex
The current waypoint index.
public int CurrentWaypointIndex { get; }
Property Value
EffectiveAiTickRate
The brain rate actually achieved, after rounding to a whole number of network ticks.
public float EffectiveAiTickRate { get; }
Property Value
EyeTransform
The transform used for vision checks. Defaults to the character's transform if not set.
public Transform EyeTransform { get; }
Property Value
- Transform
Home
The anchor this AI leashes and wanders around.
public Vector3 Home { get; set; }
Property Value
- Vector3
Remarks
For a normal NPC this is its spawn point. For a pet it is its owner — a pet's home is a moving target, and every leash check, wander radius and return-home destination in the AI reads this property, so anchoring it to the owner here fixes all of them at once. Previously each site that cared had to remember to overwrite the field with the owner's position, and the ones that forgot dragged the pet back toward wherever it happened to be summoned.
A pet ordered to Stay is the exception: it holds the position it was standing in, which is what the setter stores.
LastAiDeltaTime
Seconds elapsed during the AI tick currently executing.
public float LastAiDeltaTime { get; }
Property Value
Remarks
Published so helpers reached from deep inside a state's update — which do not receive deltaTime as a parameter — can still advance per-tick timers on the same clock the state machine runs on, rather than sampling UnityEngine.Time.deltaTime and getting one frame instead of one AI tick.
LastPathWasPartial
True when the last accepted destination request produced only a partial path.
public bool LastPathWasPartial { get; }
Property Value
Remarks
Consumed by GetMovementProgress(float, float): an agent that has run out of a partial path has stopped somewhere it was not asked to go, which is a stuck condition rather than an arrival.
NpcRNG
The seeded RNG from the owning NPC.
All AI randomisation should use this instead of DeterministicRNG.Shared
so that NPC behaviour is fully deterministic given the same seed.
Returns null for non-NPC characters.
public DeterministicRNG NpcRNG { get; }
Property Value
OwningPet
The pet this controller drives, or null when it drives a normal NPC.
public Pet OwningPet { get; }
Property Value
PendingState
The state that is about to become CurrentState, visible to the outgoing state's Exit(AIController).
public BaseAIState PendingState { get; }
Property Value
Remarks
Exists so an attacking state can tell "combat is over" from "combat is continuing in a
sub-state". Exit(AIController) clears the target and interrupts the
cast, which is right when the NPC disengages and catastrophic when it does not: the
melee archetype's flanking roll called ChangeState(GetBehindState), Exit wiped
the target on the way out, and GetBehindState then found no target and dropped the NPC
to idle. Every configured orbit / flank / strafe roll silently ended the fight.
Null outside of a transition.
PhysicsScene
The physics scene associated with this AI controller.
public PhysicsScene PhysicsScene { get; }
Property Value
- PhysicsScene
RequestedDestination
The destination most recently requested through TryMoveTo(Vector3, bool, float).
public Vector3 RequestedDestination { get; }
Property Value
- Vector3
Target
The current target for the AI (e.g., enemy, destination). Setting this property updates the agent's destination.
public Transform Target { get; set; }
Property Value
- Transform
TargetCharacter
The current combat target as an ICharacter, or null.
public ICharacter TargetCharacter { get; }
Property Value
Remarks
Prefer this over calling Target.GetComponent<ICharacter>(). The result is
cached against the transform, so it costs a field read rather than an interface
component lookup.
TargetReevaluationTimer
Per-NPC timer for mid-combat target re-evaluation. Delegates to TargetReevaluationTimer.
public float TargetReevaluationTimer { get; set; }
Property Value
VirtualCameraPosition
Virtual camera position used by the ability system to aim projectiles. Computed from the eye transform, aimed toward the current target's center. Mirrors the role of KCCController.VirtualCameraPosition for player characters.
public Vector3 VirtualCameraPosition { get; }
Property Value
- Vector3
VirtualCameraRotation
Virtual camera rotation used by the ability system to aim projectiles. Points from the eye transform toward the current target's center. Mirrors the role of KCCController.VirtualCameraRotation for player characters.
public Quaternion VirtualCameraRotation { get; }
Property Value
- Quaternion
Methods
AgentIsUsable()
True when the NavMeshAgent can accept movement commands.
public bool AgentIsUsable()
Returns
- bool
True if the agent is enabled and on a NavMesh.
Remarks
Unity rejects isStopped and SetDestination with an error for an agent that
is disabled or not on a NavMesh. Spawn paths legitimately touch the brain around the
moment an object is activated and placed, so guard rather than log a wall of errors.
ChangeState(BaseAIState, List<ICharacter>)
Changes the AI state, optionally providing targets for attacking states. Handles speed and state transitions.
public void ChangeState(BaseAIState newState, List<ICharacter> targets = null)
Parameters
newStateBaseAIStateThe new state to transition to.
targetsList<ICharacter>Optional list of targets for attacking states.
ClearPath()
Clears the agent's path without moving it.
public void ClearPath()
FaceLookTarget(float)
Rotates the character to face the current look target smoothly.
public void FaceLookTarget(float deltaTime)
Parameters
deltaTimefloat
ForceTarget(ICharacter)
Forces this NPC onto a specific character immediately, entering combat if it is not already fighting.
public bool ForceTarget(ICharacter character)
Parameters
characterICharacterThe character to attack. Ignored when null or dead.
Returns
- bool
True if the NPC took the new target.
Remarks
The scripted-aggro entry point, used by ApplyTauntAction. Distinct from setting Target directly, which changes who the NPC is fighting without putting it into a state that fights.
GetHealthPercent()
Returns this NPC's health as a fraction (0-1) of its maximum, or 1 when it has no health resource.
public float GetHealthPercent()
Returns
GetMovementProgress(float, float)
Classifies how the agent is doing against its destination, and accumulates the stuck timer.
public AIMovementProgress GetMovementProgress(float deltaTime, float tolerance = 1)
Parameters
Returns
- AIMovementProgress
The current progress classification.
Remarks
Call once per AI tick from a state that is trying to move. States that are deliberately standing still must not call it, or a stationary NPC would be reported stuck.
GetSqrDistanceToTarget()
Returns the squared distance from this NPC to its current target. Returns float.MaxValue if there is no target.
public float GetSqrDistanceToTarget()
Returns
HasAbilityInRange(float)
Returns true if the NPC has at least one ability with range >= the given distance that is off cooldown and meets activation conditions.
public bool HasAbilityInRange(float minRange)
Parameters
minRangefloatMinimum ability range required.
Returns
HasArrived(float)
True when the agent has arrived at its destination.
public bool HasArrived(float tolerance = 1)
Parameters
tolerancefloatDistance from the destination that counts as arrived.
Returns
- bool
True if arrived.
Remarks
Requires an actual path to exist. Without that check, an agent whose destination never
took reports remainingDistance == 0 and pathPending == false — which the
naive test reads as "arrived" on the very first tick.
Initialize(Vector3, Vector3[])
Initializes the controller with a home position and waypoints. Sets agent dimensions and initial state.
public void Initialize(Vector3 home, Vector3[] waypoints = null)
Parameters
homeVector3The home position for the AI.
waypointsVector3[]Optional waypoints for patrol.
InitializeOnce()
Initializes the controller and NavMeshAgent. Sets avoidance priority, speed, and movement states.
public override void InitializeOnce()
OnDestroying()
Unsubscribes from global events on destroy to prevent memory leaks.
public override void OnDestroying()
OnStartNetwork()
Called when the network starts. Disables the controller if not running on the server.
public override void OnStartNetwork()
OnStopNetwork()
Releases the tick subscription.
public override void OnStopNetwork()
OnThreatReceived(ICharacter)
Event-driven combat entry. Called by AggressionState when the NPC receives its first threat event (damage from a player/NPC). Immediately transitions to combat without waiting for the next SweepForEnemies(float) physics poll.
This eliminates the biggest polling cost for non-Active NPCs: thousands of per-NPC physics OverlapSphere calls every EnemySweepRate seconds. Nearby/Far tier NPCs rely entirely on this event to detect combat. Active tier NPCs still run SweepForEnemies for proactive (hostile faction) detection.
public void OnThreatReceived(ICharacter attacker)
Parameters
attackerICharacterThe character that generated the first threat event.
PetStanceAllowsAutoEngage(bool)
Returns whether this NPC's pet stance permits engaging on its own. Always true for anything that is not a pet.
public bool PetStanceAllowsAutoEngage(bool requiresAggressive)
Parameters
requiresAggressiveboolTrue to require the Aggressive stance (hunting for a fight); false to accept anything except Passive (fighting back).
Returns
- bool
True if the NPC may engage.
PickBestAbility(float)
Selects the best ability to use against the current target from the NPC's known abilities.
When an AbilityRotation is assigned, it is evaluated first. If it returns an ability, that ability is used. If no rotation entry matches and FallbackToDefault is true, the default scoring-based picker runs as a fallback.
Prefers abilities whose range covers the current distance. Among those, picks one at random weighted toward longer-cooldown (typically stronger) abilities. Returns null if no ability is usable (all on cooldown, out of resources, or no abilities known).
public Ability PickBestAbility(float preferredMaxRange = 3.4028235E+38)
Parameters
preferredMaxRangefloatMaximum desired range. Abilities with range beyond this are still considered but deprioritized.
Returns
- Ability
The chosen ability, or null if nothing is available.
PickBestAbility(float, Func<Ability, bool>)
Selects the best ability to use against the current target, optionally restricted to abilities matching a predicate.
public Ability PickBestAbility(float preferredMaxRange, Func<Ability, bool> filter)
Parameters
preferredMaxRangefloatMaximum desired range.
filterFunc<Ability, bool>Optional predicate an ability must satisfy to be considered. Used by HealerAttackingState to keep heals out of the damage rotation.
Returns
- Ability
The chosen ability, or null if nothing is available.
PickNearestWaypoint()
Picks the nearest waypoint to the current position and sets it as the destination.
public bool PickNearestWaypoint()
Returns
- bool
True if a waypoint destination was set.
PickScoredAbility(float, Func<Ability, bool>, float)
Scores every usable known ability against a subject at the given squared distance and returns the highest scorer.
public Ability PickScoredAbility(float sqrDistanceToSubject, Func<Ability, bool> filter, float jitter)
Parameters
sqrDistanceToSubjectfloatSquared distance to whatever the ability will be aimed at.
filterFunc<Ability, bool>Optional predicate an ability must satisfy. Null accepts all.
jitterfloatMaximum random score jitter, for variety.
Returns
- Ability
The best-scoring usable ability, or null.
Remarks
Shared by the default enemy picker and by HealerAttackingState's heal picker, which scores against an ally's distance rather than the target's. Both previously carried their own near-identical copy of this loop.
ResetState(bool)
Resets the controller's state, clearing home, target, look target, and virtual camera.
public override void ResetState(bool asServer)
Parameters
asServerboolWhether the reset is performed on the server.
Resume()
Resumes the agent's movement.
public void Resume()
SetRandomDestination(float)
Sets a random destination within a radius around the current position.
public bool SetRandomDestination(float radius = 5)
Parameters
radiusfloatRadius to randomize destination.
Returns
SetRandomHomeDestination(float)
Sets a random destination within a radius around the home position.
public bool SetRandomHomeDestination(float radius = 5)
Parameters
radiusfloatRadius to randomize destination.
Returns
SetThrottledDestination(Vector3)
Throttled destination setter. Only calls UnityEngine.AI.NavMeshAgent.SetDestination(UnityEngine.Vector3) if enough time has elapsed since the last repath (controlled by RepathInterval). Use this for ongoing movement toward a moving target (chase, orbit, retreat) to prevent path recalculation spam. For one-time destinations (waypoint arrival, warp), use UnityEngine.AI.NavMeshAgent.SetDestination(UnityEngine.Vector3) directly.
public bool SetThrottledDestination(Vector3 position)
Parameters
positionVector3The world position to navigate toward.
Returns
- bool
True if the destination was updated, false if throttled.
Stop()
Stops the agent's movement.
public void Stop()
TransitionToIdleState()
Transitions to the idle state.
public void TransitionToIdleState()
TransitionToNextWaypoint()
Transitions to the next waypoint in the waypoint array.
public bool TransitionToNextWaypoint()
Returns
TransitionToRandomMovementState()
Transitions to a random movement state from the available movement states.
public virtual void TransitionToRandomMovementState()
TryMoveTo(Vector3, bool, float)
Asks the agent to move to a world position, reporting whether the path actually reaches it.
public AIMovementResult TryMoveTo(Vector3 destination, bool throttle = true, float sampleRadius = 2)
Parameters
destinationVector3Desired world position.
throttleboolTrue to respect RepathInterval. Use for ongoing movement toward something that moves; pass false for one-shot destinations such as a waypoint or a spawn point, where a silently dropped request means the NPC never sets off at all.
sampleRadiusfloatInitial NavMesh sample radius.
Returns
- AIMovementResult
What happened.
TryRecoverFromStuck(Vector3)
Attempts to free a stuck NPC, escalating with each call within the same episode.
public bool TryRecoverFromStuck(Vector3 fallback)
Parameters
fallbackVector3Where to go if the original destination cannot be recovered — a pet's owner, an NPC's home. Pass the NPC's own position to mean "just get unstuck where you are".
Returns
- bool
True if a recovery action was taken.
Remarks
First a nudge: re-sample the destination from a wider radius and repath, which clears the common case of two agents wedged against each other or a destination that sampled onto the wrong side of a wall. Then, once StuckWarpTimeout has elapsed, a warp — the only remedy for genuinely unreachable geometry.
Warping is a last resort rather than the first move on purpose: it is visible to players, so an NPC should be seen to try to walk before it blinks.
TrySampleNavMesh(Vector3, out Vector3, float)
Places a world position onto the NavMesh, widening the search until it lands or the attempts run out.
public static bool TrySampleNavMesh(Vector3 position, out Vector3 result, float initialRadius = 2)
Parameters
positionVector3The desired world position.
resultVector3The nearest position actually on the NavMesh.
initialRadiusfloatRadius for the first attempt.
Returns
- bool
True if a NavMesh position was found.
Remarks
A single fixed-radius sample is the difference between "the NPC walks to a slightly different spot" and "the NPC does not move at all", and the old call sites all took the second outcome silently.
WarpTo(Vector3)
Places the agent at a world position, using UnityEngine.AI.NavMeshAgent.Warp(UnityEngine.Vector3) so the agent's internal NavMesh position is updated rather than only its transform.
public bool WarpTo(Vector3 position)
Parameters
positionVector3The world position to place the agent at.
Returns
- bool
True if the agent was placed on the NavMesh.
Remarks
Required for object pooling. A recycled NPC is reactivated at a new position, and
assigning transform.position alone leaves the agent believing it is still where
the previous occupant died — it then either refuses to path or walks back toward the
old location.