t1k:unity:dots-ai:core
| Field | Value |
|---|---|
| Module | dots-ai |
| Version | 2.1.7 |
| Effort | medium |
| Tools | — |
How to invoke
Section titled “How to invoke”/t1k:unity:dots-ai:coreDOTS AI Core — Perception, Alert, Leash, Stealth, Party
Section titled “DOTS AI Core — Perception, Alert, Leash, Stealth, Party”Package:
com.the1studio.dots-ai— namespaceDOTSAI.*Note: This skill covers DOTSAI core systems. For BDP behavior trees, see:dots-ai-behavior-designer-pro·dots-ai-bdp-tactical-pack·dots-ai-bdp-formations-pack
When This Skill Triggers
Section titled “When This Skill Triggers”- Implementing alert escalation (Unaware → Suspicious → Alert → Combat)
- Adding AI leash / home-radius aggro reset
- Building party formations and party-buff systems
- Implementing stealth detection, line-of-sight, detection cone
- Setting up per-entity perception and threat tracking
Public Components
Section titled “Public Components”| Component | Type | Purpose |
|---|---|---|
AlertLevel | IComponentData + IEnableableComponent | AlertTier Level, Entity DetectedEntity, float AlertTimer |
AlertTier | enum | Unaware=0, Suspicious=1, Alert=2, Combat=3 |
AILeash | IComponentData | float3 HomePosition, float LeashRadius, bool Initialized |
PartyFormation | IComponentData | FormationType Type, float Spacing; baked on every member |
PartyMember | IComponentData | Marks entity as party member |
PartyLeaderTag | IComponentData (tag) | Marks party leader |
PartyBuff | IComponentData | Passive bonus leader grants members in formation |
public enum FormationType { Line, Wedge, Circle, Column }Public Systems
Section titled “Public Systems”| System | Group | Role |
|---|---|---|
AlertDecaySystem | AISystemGroup | Decrements AlertTimer; decays AlertTier on zero; disables component at Unaware |
AlertTriggerSystem | AISystemGroup | Escalates AlertLevel on player/target detection |
AILeashInitSystem | AISystemGroup | Initializes HomePosition on first tick |
AIRespawnResetSystem | AISystemGroup | Clears aggro state on respawn |
StealthVisibilitySystem | AISystemGroup | Checks LOS + detection range for stealth entities |
PartyFormationSystem | AISystemGroup | Positions members relative to leader per FormationType |
Common Patterns
Section titled “Common Patterns”Alert decay (4-tier escalation)
Section titled “Alert decay (4-tier escalation)”// AlertDecaySystem — IJobEntity pattern:foreach (var (alert, enabled) in SystemAPI.Query<RefRW<AlertLevel>, EnabledRefRW<AlertLevel>>()){ alert.ValueRW.AlertTimer -= deltaTime; if (alert.ValueRW.AlertTimer <= 0f) { if (alert.ValueRO.Level == AlertTier.Unaware) enabled.ValueRW = false; // disable component at base tier else alert.ValueRW.Level--; // decay one tier }}AILeash enforcement
Section titled “AILeash enforcement”// Leash check in leash system:float distSq = math.distancesq(currentPos, leash.HomePosition);if (distSq > leash.LeashRadius * leash.LeashRadius){ // clear Threat/Aggro buffers, set navigation target to HomePosition ecb.SetBuffer<AggroEntry>(entity); // clear aggro}Party formation positioning
Section titled “Party formation positioning”// PartyFormationSystem — per-member offset from leader:float3 offset = GetFormationOffset(formation.Type, memberIndex, formation.Spacing);ecb.SetComponent(member, LocalTransform.FromPosition(leaderPos + offset));Cross-Package Interactions
Section titled “Cross-Package Interactions”| Direction | Package | Detail |
|---|---|---|
| Depends on | DOTSCore base | LocalTransform, ECB, SystemAPI |
| Depends on | DOTSCore.Stats | Health/Experience for damage-based aggro integration |
| Used by | DOTSCombat | Attack targeting reads AlertLevel |
| Used by | DOTSBDP | Behavior tree checks AlertLevel for combat branch |
Demo Precedents
Section titled “Demo Precedents”- BattleDemo2D — enemies escalate Alert on detection; party formations for mob groups
- ChaosForgeDemo — AILeash prevents farm-camping single rooms; alert decay enables stealth evasion
- BackpackBattlefield — party buff scales with FormationType (wedge = +attack damage)
Gotchas
Section titled “Gotchas”- AlertLevel is IEnableableComponent — disabling via
EnabledRefRWis cheaper than add/remove. NEVER useGetSingleton/HasSingletonon IEnableableComponent types. UseWithPresent<AlertLevel>()to include disabled entities in jobs that need to SET the alert. - AILeash.Initialized flag required — without it,
HomePositionoverwrites every frame. Self-init pattern: if!leash.Initializedthen setleash.HomePosition = currentPos; leash.Initialized = true;inAILeashInitSystem.OnUpdate. - PartyFormation baked on every member, not leader — this is intentional for query efficiency. Do not try to centralize formation data on the leader entity; the query iterates members directly.
- StealthVisibilitySystem needs explicit LOS — distance-only detection fails against obstacles. The system performs spatial hash or raycast checks. Pure distance checks are insufficient for stealth.
- [RequireMatchingQueriesForUpdate] missing —
AlertDecaySystemandAlertTriggerSystemare early-game empty-query candidates. Add[RequireMatchingQueriesForUpdate]to skip frames when no entities have AlertLevel (W4.4 finding). PerceivedEntity.Target == Entity.Nullis NOT a valid “no enemy found” sentinel — aPerceivedEntityentry can legitimately carryTarget == Entity.Null(the perception pipeline records a detected threat whose resolved entity is null, e.g. an unresolved/destroyed-this-frame target).AlertTriggerSystemoriginally scanned thePerceivedEntitybuffer for the closest visible enemy intoclosestEnemy = pe.Target, then short-circuited withif (closestEnemy == Entity.Null) return;. Because a found enemy could haveTarget == Entity.Null, that guard returned early even when a real enemy was found → all tier classification stayed stuck atUnaware(silent, no error, full perception→alert path dead). Fix: track a dedicatedbool hasEnemy = false;set totrueinside the closest-enemy branch and gate onif (!hasEnemy) return;— never overload anEntity.Nullcomparison as the loop’s “found anything?” flag. Confirmed pre-existing bug, fixed 2026-05-29 (90405f77). General rule: when reducing a buffer to “did we find a qualifying element?”, use a separate bool, not a sentinel value the element type can legitimately hold.