Welcome to the Tactical AI Hiding System!

This asset is a powerful, flexible, and performance-oriented framework for creating intelligent stealth and cover-based AI. It's built on a simple philosophy: You are in control.

Instead of a black box that takes over your characters, this system acts as an intelligent service. Your AI's brain (like a Behavior Tree or State Machine) asks the question, "Where is the best place to hide right now?", and this system provides the answer.

Core Features

  • Data-Driven AI Personality: Define complex behaviors not in code, but in ScriptableObjects. Create cautious snipers, aggressive flankers, and sneaky assassins simply by mixing and matching components.
  • High Performance by Design: The asynchronous, multithreaded core uses Unity's Job System and Burst Compiler to run complex calculations on background threads, leaving your main thread free for smooth gameplay.
  • Extensible & Customizable: Don't like how the AI evaluates cover? Write your own custom Scorer. Need the AI to recognize a unique type of cover in your game? Write your own custom Provider.
  • Environment-Aware: Use optional helper components to tag your scene with tactical information. Designate cover nodes, danger zones (Hazards), strategic areas (TacticalZones), and even soft cover like smoke (VolumetricCover).
  • Dynamic Behavior: Use the SearchProfile API to change an AI's "mood" or tactics on the fly in response to game events like taking damage or receiving a squad command.

Key Concepts at a Glance

  • TacticalAgent: The main MonoBehaviour component you add to your AI. It's the central brain.
  • TacticalAISettings: A ScriptableObject that acts as the AI's "Rulebook" or "Personality," defining how it thinks.
  • Providers: ScriptableObjects that find potential hiding spots (e.g., "look behind obstacles").
  • Scorers: ScriptableObjects that rate those spots (e.g., "spots far from the player are better").

Ready to begin? Let's get your first AI hiding in under 10 minutes.

➡️ Next: Getting Started Guide

Getting Started: Your First Hiding AI

This guide will walk you through the entire process of setting up an AI that can intelligently hide from a player. We will cover the complete setup, from components to the final trigger script.

Step 1: Scene Preparation

Before we begin, ensure your scene is ready:

  1. NavMesh: Your scene must have a baked NavMesh so the AI can navigate.
  2. Layers: Create a new Layer called Obstacles (or similar). Place all your static, cover-providing geometry (walls, crates, etc.) on this layer. We will use this to tell the system what blocks line of sight.

Step 2: Create the AI's "Personality" Asset

The AI's behavior is stored in a TacticalAISettings ScriptableObject.

  1. In your Project window, right-click and go to Create > HidingSystem > Hiding Settings.
  2. Name it StandardAI_Settings.
  3. Select the new asset. In the Inspector, set the Occluder Mask to your Obstacles layer. This is critical for visibility checks.

Step 3: Configure the TacticalAgent Component

  1. Select your AI character GameObject. It must already have a NavMeshAgent component.
  2. Click Add Component and add the TacticalAgent.
  3. Drag your StandardAI_Settings asset into the Settings slot on the TacticalAgent.
  4. Drag the character's NavMeshAgent component into the Agent slot.

Step 4: Define the Hiding Rules (Providers & Scorers)

Now, let's give our AI some basic rules. Select your StandardAI_Settings asset again.

  1. Add Providers (Where to look for cover):
    • Under Providers, click + and add BehindObstacleProviderSO.
    • Click + again and add CurrentPositionProviderSO. This is important, as it allows the AI to consider "staying put" if it's already in a good spot.
  2. Add Scorers (How to decide which spot is best):
    • Under Scorers, click +. Add a VisibilityScorerSO and set its Weight to 2.0. This makes being hidden very important.
    • Add a PathSafetyScorerSO and set its Weight to 1.5. This tells the AI to avoid running through open areas.
    • Add a DistanceScorerSO with a Weight of 1.0. This will make the AI prefer spots that are a reasonable distance away.

Step 5: Triggering the AI with a Simple Controller

The TacticalAgent needs to be told when to hide. Create a new script, AIController.cs, and add it to your AI character.

using UnityEngine;
using MaharajaStudio.TacticalAI; // The Hiding System namespace

[RequireComponent(typeof(TacticalAgent))]
public class AIController : MonoBehaviour
{
    // Assign your Player GameObject to this in the Inspector
    public Transform playerTarget;
    private TacticalAgent hider;

    void Start()
    {
        hider = GetComponent();
        
        // Tell the hider who the enemy is
        hider.SetSinglePlayerTarget(playerTarget);
    }

    void Update()
    {
        // Check if we are exposed AND not already searching or moving to cover
        if (hider.IsThreatened && !hider.IsSearching && !hider.IsMoving)
        {
            Debug.Log("I'm exposed! Finding a place to hide.");
            hider.RetreatToHide();
        }
    }
}

Assign your Player object to the Player Target field on the AIController.

You're Done!

Press Play! Move your player into the AI's line of sight. You should see the "I'm exposed!" debug log, and the AI will use your rules to find the best spot behind an obstacle and move there.

➡️ Next: Learn the underlying principles in Core Concepts

How-To Guides (The Cookbook)

This section provides practical, goal-oriented recipes for creating common AI behaviors.

Recipe: The Cautious Marksman

Goal: An AI that tries to stay as far away from the player as possible and heavily prioritizes safety.

Ingredients:

  • A TacticalAISettings asset named Marksman_Settings.
  • Providers: BehindObstacleProviderSO, CurrentPositionProviderSO.
  • Scorers: DistanceScorerSO, VisibilityScorerSO, PathSafetyScorerSO.

Method:

  1. In your Marksman_Settings asset, add the providers listed above.
  2. Add the scorers with the following weights:
    • DistanceScorerSO: Weight: 3.0 (This is the highest priority. The AI desperately wants to be far away).
    • VisibilityScorerSO: Weight: 2.0 (Being unseen is very important).
    • PathSafetyScorerSO: Weight: 2.0 (The path to the safe spot must also be safe).

Result: When this AI hides, it will consistently choose valid cover points near the maximum edge of its search radius.


Recipe: The Aggressive Flanker

Goal: An AI that prefers to hide in locations that create a crossfire angle with its allies, getting into an advantageous flanking position.

Ingredients:

  • A TacticalAISettings asset named Flanker_Settings.
  • Providers: NavMeshRingProviderSO, BehindObstacleProviderSO.
  • Scorers: CrossfireScorerSO, VisibilityScorerSO, ProximityPenaltyScorerSO.

Method:

  1. Configure the Flanker_Settings asset.
  2. Set up the scorer weights:
    • CrossfireScorerSO: Weight: 3.0 (The absolute main priority is getting a good angle).
    • VisibilityScorerSO: Weight: 1.5 (The spot should still be hidden).
    • ProximityPenaltyScorerSO: Weight: 1.0 (Ensure it doesn't try to flank from a position that is suicidally close).

Result: This AI will ignore spots directly behind it, instead favoring cover that puts it at a 90-degree angle to its allies, relative to the player's position. Make sure to populate the allies list on the TacticalAgent for this to work

Core Concepts: How the System Thinks

Understanding the core architecture will empower you to create truly unique and intelligent AI. The entire system is built on a simple but powerful pipeline.

The Generate -> Score -> Select Pipeline

Every time you call RetreatToHide(), the system performs three main steps:

  1. GENERATE: Providers scan the world and generate a list of potential hiding spots (CandidateInfo). They don't know if a spot is "good" or "bad"; they only know how to find possibilities.
  2. SCORE: Each candidate spot is then shown to a list of Scorers. Each scorer evaluates the spot based on one specific criterion (e.g., "How visible is it?") and gives it a score.
  3. SELECT: The system multiplies each score by its user-defined Weight and adds them all up. The candidate with the highest total score is the winner.

This pipeline is what allows for such modular and data-driven behavior.

graph TD
    A[Start Search: RetreatToHide()] --> B{HidingContext Assembled};
    B --> C[1. GENERATE Candidates];
    C --> D(BehindObstacleProvider);
    C --> E(CoverNodeProvider);
    C --> F(...);
    D & E & F --> G[Master List of Candidates];
    G --> H[2. SCORE Each Candidate];
    H -- Candidate 1 --> I{For each Scorer...};
    I -- VisibilityScorer --> J(Score: 0.0 * Weight: 2.0);
    I -- DistanceScorer --> K(Score: 0.8 * Weight: 1.0);
    I -- PathSafetyScorer --> L(Score: 1.0 * Weight: 1.5);
    J & K & L --> M[Total Score for Candidate 1 = 2.3];
    H -- Candidate 2... --> N[...];
    M & N --> O[3. SELECT Candidate with Highest Score];
    O --> P[Return Best HidingSpot];

AI Personality: TacticalAISettings vs. SearchProfile

You have two ways to define an AI's behavior:

  • TacticalAISettingsSO (The Rulebook): This is the AI's default "personality." It's a static asset that defines the complete set of providers and scorers the AI uses. You might have a SniperSettings asset that prefers high ground and long distances, and a GruntSettings asset that prefers staying close to allies.
  • SearchProfile (The "Mood"): This is a temporary, code-driven override for a single search. It allows you to dynamically change the AI's priorities. For example, when an AI's health is low, you can use a SearchProfile to temporarily crank up the weight of the DistanceScorer and PathSafetyScorer, making it act more desperately and cautiously without changing its base personality asset.

The Facade Pattern: Keeping Your Code Clean

The TacticalAgent component acts as a Facade. It provides a simple, clean API (RetreatToHide(), IsThreatened, etc.) that hides the complex machinery underneath. Your AI controller only ever needs to talk to the TacticalAgent, keeping your game logic decoupled and easy to manage.

➡️ Next: See practical examples in the How-To Guides (Cookbook)

TacticalAgent Class API Documentation

The TacticalAgent is the central component of the AI Hiding System. Attach it to your AI agent to enable intelligent cover-finding and retreat behaviors. This documentation covers all public properties, methods, and events you can use to control and query the AI's state.

1. Core Properties (Status & State)

These properties provide real-time information about the AI's current state. They are read-only and are perfect for driving animations, behavior trees, or UI debugging.


IsSearching

Returns true if the AI is currently in the process of finding a hiding spot.

  • Use Case: Prevent the AI from performing other actions (like attacking or reloading) while it's busy thinking about where to hide. It's an essential flag for state management in a Behavior Tree or State Machine.
  • How to Use:
    TacticalAgent hider = GetComponent<TacticalAgent>();
    
    void Update()
    {
        // Example: Don't allow the AI to shoot while it's looking for cover.
        if (hider.IsSearching)
        {
            myWeaponController.HoldFire();
        }
    }

IsInCover

Returns true if the agent has successfully reached its destination and is stationary.

  • Use Case: Use this to trigger "in cover" behaviors, such as crouching, peeking, or starting a timer before re-engaging. It confirms that the AI is not only safe but also in position.
  • How to Use:
    TacticalAgent hider = GetComponent<TacticalAgent>();
    
    void Update()
    {
        // If the AI is in cover, make it crouch.
        if (hider.IsInCover)
        {
            animator.SetBool("IsCrouching", true);
        }
        else
        {
            animator.SetBool("IsCrouching", false);
        }
    }

IsThreatened

A convenient alias for IsNeedToHide(). Returns true if the agent is currently visible to any player.

  • Use Case: This is the primary trigger for deciding when to start a search. It's a lightweight check that can be called every frame in an Update loop or as a condition in a Behavior Tree to initiate a retreat.
  • How to Use:
    TacticalAgent hider = GetComponent<TacticalAgent>();
    
    void Update()
    {
        // If the AI is threatened and not already moving to cover, tell it to hide.
        if (hider.IsThreatened && !hider.IsMoving)
        {
            hider.RetreatToHide();
        }
    }

IsMoving

Returns true if the NavMeshAgent is actively following a path to a destination.

  • Use Case: Useful for controlling animations (e.g., switching between idle/run) and for preventing the AI from starting a new search while it's already on its way to cover.
  • How to Use:
    TacticalAgent hider = GetComponent<TacticalAgent>();
    
    void Update()
    {
        // Control the "IsRunning" parameter in the animator.
        animator.SetBool("IsRunning", hider.IsMoving);
    }

HasValidHidingSpot

Returns true if the agent has a valid CurrentHidingSpot, even if it hasn't reached it yet.

  • Use Case: Differentiate between an AI that has no plan (HasValidHidingSpot is false) and an AI that has a plan but is still executing it (HasValidHidingSpot is true, but IsInCover is false).
  • How to Use:
    // In a Behavior Tree, this could be a condition:
    // "Does the AI have a valid destination?"
    if (hider.HasValidHidingSpot)
    {
        // Proceed with movement task.
    }
    else
    {
        // Trigger a search task.
    }

CurrentHidingSpot

The HidingSpot struct the agent is currently moving to or located at. It's HidingSpot.Invalid if no spot has been found.

  • Use Case: Access detailed information about the AI's destination, such as its position, the direction of cover (coverNormal), and whether it can peek left or right.
  • How to Use:
    if (hider.IsInCover && hider.CurrentHidingSpot.canPeekRight)
    {
        // The AI is in cover and can peek right, so trigger a peek-and-shoot action.
        myCombatLogic.PerformPeek(hider.CurrentHidingSpot.peekRightPosition);
    }

LastKnownPlayerPosition

The position where the AI last had a line of sight to a player. This acts as the AI's short-term memory.

  • Use Case: This is crucial for tactical decisions. When the AI gets into cover, it should aim or throw grenades towards this position, not the player's new, unseen location.
  • How to Use:
    // When the AI decides to throw a grenade from cover:
    if (hider.IsInCover && hider.LastKnownPlayerPosition.HasValue)
    {
        Vector3 target = hider.LastKnownPlayerPosition.Value;
        myGrenadeController.ThrowAt(target);
    }

TimeInCover

Returns the number of seconds the agent has been stationary in its current valid cover spot.

  • Use Case: Make the AI's behavior more dynamic. For example, an aggressive AI might decide to pop out of cover after 2 seconds, while a cautious one might wait for 5 seconds.
  • How to Use:
    // A cautious AI might only re-engage after being in cover for a while.
    if (hider.IsInCover && hider.TimeInCover > 5.0f)
    {
        // It's been 5 seconds, time to look for an opportunity to attack.
        myCombatLogic.SeekAttackOpportunity();
    }

2. Core Methods (Actions)

These methods instruct the TacticalAgent to perform an action, such as starting a search or stopping its current behavior.


RetreatToHide()

Initiates a search for the best hiding spot using the default settings. This is the primary and most common way to make the AI find cover.

  • Use Case: This is your go-to method. Call it when the AI is exposed and needs to find safety.
  • How to Use:
    TacticalAgent hider = GetComponent<TacticalAgent>();
    
    void OnTookDamage()
    {
        // When the AI takes damage, it should immediately try to find cover.
        hider.RetreatToHide();
    }

RetreatToHide(SearchProfile profile)

Initiates a search using a temporary SearchProfile to override the default settings for a single search.

  • Use Case: This is powerful for creating dynamic, context-aware AI. An AI might use a "cautious" profile (preferring distant cover) when its health is low, or an "aggressive" profile (preferring ambush spots) when it has a shotgun.
  • How to Use:
    // Create a profile for an aggressive flank.
    var flankProfile = new SearchProfile
    {
        // Create a dictionary to override scorer weights.
        ScorerWeightOverrides = new Dictionary<TacticalScorerSO, float>
        {
            { crossfireScorerAsset, 5.0f }, // Heavily prioritize crossfire angles
            { distanceScorerAsset, 0.5f }   // Care less about being far away
        }
    };
    
    // Tell the AI to find a flanking position using this custom profile.
    hider.RetreatToHide(flankProfile);

RetreatToHideInArea(Vector3 areaCenter, float areaRadius)

A convenient wrapper that tells the AI to find cover, but only within a specific circular area in the world.

  • Use Case: Perfect for mission objectives or squad tactics. For example, "Defend this control point" means the AI should find cover inside the control point's radius, not run across the map.
  • How to Use:
    Vector3 controlPointCenter = new Vector3(10, 0, 50);
    float controlPointRadius = 15f;
    
    // The AI must find cover within the designated objective area.
    hider.RetreatToHideInArea(controlPointCenter, controlPointRadius);

StopAndClear()

Immediately cancels any ongoing search, stops all movement, and clears the current hiding spot.

  • Use Case: Use this to interrupt the AI's current action. For example, if a player gets too close, you might want the AI to stop retreating and switch to close-quarters combat, regardless of its previous plan.
  • How to Use:
    float distanceToPlayer = Vector3.Distance(transform.position, player.position);
    
    if (distanceToPlayer < 5.0f)
    {
        // The player is too close! Stop running and fight back.
        hider.StopAndClear();
        myCombatLogic.EngageInMelee();
    }

CancelSearch()

Cancels an ongoing asynchronous search. It does not stop the agent if it's already moving.

  • Use Case: This is a more subtle interruption than StopAndClear(). Use it if you want to abort the thinking process (which can take a few frames) without necessarily stopping the AI's current movement.
  • How to Use:
    // The AI started a search, but the player suddenly died.
    void OnPlayerDied()
    {
        // Cancel the search since the threat is gone. The AI might continue to its last spot.
        hider.CancelSearch();
    }

3. Query Methods (Checks & Information)

These methods allow you to ask questions and get information from the system without changing the AI's state. They are ideal for decision-making logic.


IsNeedToHide()

Performs a lightweight line-of-sight check to see if the agent is currently visible to any player.

  • Use Case: This is the most common query. Use it in Update or a Behavior Tree to decide if a retreat is necessary. It's much cheaper than running a full RetreatToHide() every frame.
  • How to Use:
    // A classic "tick" for a Behavior Tree node.
    public NodeStatus CheckThreatStatus()
    {
        if (hider.IsNeedToHide())
        {
            return NodeStatus.FAILURE; // We are exposed, the "Stay Safe" check fails.
        }
        return NodeStatus.SUCCESS; // We are hidden, the check passes.
    }

CanFindHidingSpot(out HidingSpot potentialSpot)

Performs a quick, synchronous search to see if any valid hiding spot exists from the current position. It does not make the AI move.

  • Use Case: A crucial "look before you leap" check. Before deciding to retreat, you can use this to see if there's anywhere to retreat to. If not, the AI might choose to stand and fight instead of running into a dead end.
  • How to Use:
    // Before committing to a retreat, check if it's even possible.
    if (hider.IsThreatened)
    {
        if (hider.CanFindHidingSpot(out HidingSpot spot))
        {
            // A safe spot exists, so let's go!
            hider.RetreatToHide();
        }
        else
        {
            // There's nowhere to hide. Stand and fight!
            myCombatLogic.HoldPositionAndFight();
        }
    }

GetTopNHidingSpotsSync / GetTopNHidingSpotsAsync

Performs a full search and returns a list of the best hiding spots found, sorted by score. The Sync version blocks the main thread for a frame, while the Async version is non-blocking.

  • Use Case: Advanced usage for squad tactics or complex behaviors. A squad leader could call this to find the top 3 spots and then assign each squad member to one of them, ensuring they don't all run to the same piece of cover.
  • How to Use (Async):
    private async void AssignSquadPositions()
    {
        // Find the 3 best cover spots near the squad leader.
        List<HidingSpot> bestSpots = await squadLeader.hider.GetTopNHidingSpotsAsync(3);
    
        if (bestSpots.Count >= 3)
        {
            squadMember1.hider.MoveToSpot(bestSpots[0]);
            squadMember2.hider.MoveToSpot(bestSpots[1]);
            squadMember3.hider.MoveToSpot(bestSpots[2]);
        }
    }

ReEvaluateCurrentSpot()

Checks if the agent's CurrentHidingSpot is still safe from all players. This is much cheaper than a full new search.

  • Use Case: Periodically check if the AI's cover has been flanked. If a player moves and gets a new angle, this method will return false, signaling that the AI needs to find a new, better spot.
  • How to Use:
    // Run this check every couple of seconds while in cover.
    IEnumerator CheckCoverValidity()
    {
        while(true)
        {
            yield return new WaitForSeconds(2.0f);
            if (hider.IsInCover && !hider.ReEvaluateCurrentSpot())
            {
                // Our cover is blown! Find a new spot.
                hider.RetreatToHide();
            }
        }
    }

4. Configuration Methods

These methods are used to set up and change the TacticalAgent's behavior and context at runtime.


SetPlayers(IReadOnlyList<Transform> playerList) / SetAllies(IReadOnlyList<Transform> allyList)

Updates the list of player targets and allies that the AI should be aware of.

  • Use Case: This is fundamental for setup. Your game manager or AI spawner should call this to tell each AI who its enemies and friends are. It should be updated if players or allies enter or leave the AI's awareness range.
  • How to Use:
    TacticalAgent myAI = GetMyAI();
    List<Transform> allPlayers = FindAllPlayerTransforms();
    List<Transform> nearbyAllies = FindNearbyAllies();
    
    myAI.SetPlayers(allPlayers);
    myAI.SetAllies(nearbyAllies);

UpdateTacticalAISettings(TacticalAISettingsSO newSettings)

Swaps the currently used TacticalAISettingsSO asset at runtime.

  • Use Case: Allows for profound changes in AI behavior. You could have different settings assets for different enemy types (e.g., "GruntSettings", "SniperSettings") or for different game states (e.g., "StealthSettings", "CombatSettings").
  • How to Use:
    public TacticalAISettingsSO cautiousSettings;
    public TacticalAISettingsSO aggressiveSettings;
    
    void OnHealthLow()
    {
        // When health is low, switch to the cautious behavior set.
        hider.UpdateTacticalAISettings(cautiousSettings);
    }

SetCurrentEyeHight(float eyeHeight)

Updates the AI's eye height, which is used for all visibility calculations.

  • Use Case: Make the AI's visibility checks more accurate based on its stance. If the AI is crouching, its eye height should be lower, which might make shorter objects valid cover.
  • How to Use:
    void Crouch()
    {
        animator.SetBool("IsCrouching", true);
        hider.SetCurrentEyeHight(0.8f); // Set eye height for crouching.
    }
    
    void StandUp()
    {
        animator.SetBool("IsCrouching", false);
        hider.SetCurrentEyeHight(1.7f); // Reset to default standing height.
    }

ForgetPlayerLocation()

Manually clears the AI's memory of the LastKnownPlayerPosition.

  • Use Case: Useful in stealth scenarios. If the player successfully hides for a certain amount of time, you can call this to make the AI "give up" the search and return to its patrol, making it seem less omniscient.
  • How to Use:
    // Called by a stealth manager after the player has been hidden for 10 seconds.
    public void OnPlayerLost()
    {
        aiHider.ForgetPlayerLocation();
        aiHider.ReturnToPatrol();
    }

Advanced API & Architecture Documentation

This guide covers the core architectural components of the AI Hiding System. Mastering these concepts will allow you to extend the system, define custom AI personalities, and deeply integrate it into your game's unique mechanics.

The system's core philosophy is a simple three-step process:

  1. GENERATE: Potential hiding spots (CandidateInfo) are generated by Providers.
  2. SCORE: Each candidate is evaluated and scored by multiple Scorers.
  3. SELECT: The candidate with the highest total score is chosen as the best HidingSpot.

1. Creating Custom TacticalSpotProviderSO (Generation)

What is it?
A TacticalSpotProviderSO is a ScriptableObject whose sole purpose is to generate a list of potential hiding spots. Providers are the "eyes" of the system, finding candidate locations in the world based on a specific strategy (e.g., looking behind obstacles, sampling the NavMesh, finding tagged objects).

Why is it useful?
Your game might have unique types of cover that the default providers don't know about.

  • Want AI to hide in tall grass? Create a TallGrassProvider.
  • Want AI to use teleporter exits as escape points? Create a TeleporterProvider.
  • Want AI to hide near health packs? Create a HealthPackProvider.

How to use it:

  1. Create the C# Script: Create a new script that inherits from TacticalSpotProviderSO.
  2. Implement GenerateCandidates: This is the only method you need to override. Your logic should add CandidateInfo structs to the results list.
  3. Create the Asset: In the Unity Editor, right-click in your project folder and create an instance of your new provider asset via the CreateAssetMenu attribute.
  4. Assign the Asset: Drag your new provider asset into the Providers array in your TacticalAISettingsSO.

Example: A SafeZoneProvider

Let's imagine you have designer-placed "Safe Zones" in your level. This provider will generate candidate spots inside those zones.

Step 1 & 2: C# Script (SafeZoneProviderSO.cs)

using UnityEngine;
using MaharajaStudio.TacticalAI;
using MaharajaStudio.TacticalAI.Providers;
using System.Collections.Generic;

// Assume you have a simple component to tag your safe zones
public class SafeZoneVolume : MonoBehaviour { }

[CreateAssetMenu(menuName = "HidingSystem/Providers/Custom/Safe Zone Provider")]
public class SafeZoneProviderSO : TacticalSpotProviderSO
{
    public override void GenerateCandidates(in HidingContext ctx, List<CandidateInfo> results)
    {
        // Find all active safe zones in the scene.
        // Note: A real implementation should use a static registry for better performance.
        var safeZones = Object.FindObjectsByType<SafeZoneVolume>(FindObjectsSortMode.None);

        foreach (var zone in safeZones)
        {
            Bounds bounds = zone.GetComponent<Collider>().bounds;
            int samples = ctx.lodSettings.candidatesPerProvider;

            for (int i = 0; i < samples; i++)
            {
                // Generate a random point within the zone's bounds
                Vector3 randomPoint = new Vector3(
                    Random.Range(bounds.min.x, bounds.max.x),
                    Random.Range(bounds.min.y, bounds.max.y),
                    Random.Range(bounds.min.z, bounds.max.z)
                );

                // Add the point as a candidate. We don't know the cover normal.
                results.Add(new CandidateInfo { position = randomPoint, coverNormal = Vector3.zero });
            }
        }
    }
}

Step 3 & 4: In the Editor

  1. Create the SafeZoneProviderSO asset in your project.
  2. Drag this asset into the Providers list on your TacticalAISettingsSO. Now, whenever the AI searches for cover, it will include points from your safe zones.

2. Creating Custom TacticalScorerSO (Scoring)

What is it?
A TacticalScorerSO is a ScriptableObject that evaluates a single CandidateInfo and returns a score. This is the "brain" of the AI. The score typically ranges from 0.0 (bad) to 1.0 (good). A score of float.NegativeInfinity will instantly disqualify the spot.

Why is it useful?
Scorers define an AI's personality and priorities. By creating custom scorers and adjusting their weights in the TacticalAISettingsSO, you can create vastly different behaviors.

  • Cautious AI: Give a high weight to DistanceScorerSO.
  • Aggressive AI: Give a high weight to AmbushScorerSO and a custom ProximityToEnemyScorer.
  • Stealth AI: Give a high weight to DarknessScorerSO or a custom InBushesScorer.

How to use it:

  1. Create the C# Script: Create a new script that inherits from TacticalScorerSO.
  2. Implement Score: Override the Score method. This method receives the HidingContext (for world information) and the CandidateInfo to evaluate. Return your calculated score.
  3. Create the Asset: Create a ScriptableObject asset from your new scorer class.
  4. Assign and Weigh: Add the asset to the Scorers list in your TacticalAISettingsSO and set its weight. The weight determines how much influence this scorer has on the final decision.

Example: An AmmoProximityScorerSO

Let's create a scorer that makes the AI prefer hiding spots that are near ammo pickups, especially if its ammo is low.

Step 1 & 2: C# Script (AmmoProximityScorerSO.cs)

using UnityEngine;
using MaharajaStudio.TacticalAI;
using MaharajaStudio.TacticalAI.Scorer;

// Assume a central registry knows where all ammo boxes are.
public static class AmmoBoxRegistry
{
    public static List<Transform> AllAmmoBoxes = new List<Transform>();
}

[CreateAssetMenu(menuName = "HidingSystem/Scorers/Custom/Ammo Proximity Scorer")]
public class AmmoProximityScorerSO : TacticalScorerSO
{
    [Tooltip("The ideal distance to be from an ammo box.")]
    public float optimalDistance = 3f;
    [Tooltip("Beyond this distance, the scorer has no effect.")]
    public float maxDistance = 20f;

    public override float Score(in HidingContext ctx, in CandidateInfo candidate)
    {
        // For this to work, you need a way to get the AI's ammo count.
        // Let's assume the TacticalAgent has a sibling component `WeaponSystem`.
        var weaponSystem = ctx.enemyTransform.GetComponent<WeaponSystem>();
        
        // If ammo is full, this scorer is irrelevant. Return a neutral score.
        if (weaponSystem == null || weaponSystem.IsAmmoFull())
        {
            return 0.5f; 
        }

        if (AmmoBoxRegistry.AllAmmoBoxes.Count == 0) return 0f;

        // Find the closest ammo box to the candidate hiding spot.
        float minDstSq = float.MaxValue;
        foreach (var ammoBox in AmmoBoxRegistry.AllAmmoBoxes)
        {
            float d = (ammoBox.position - candidate.position).sqrMagnitude;
            if (d < minDstSq)
            {
                minDstSq = d;
            }
        }

        float distance = Mathf.Sqrt(minDstSq);

        if (distance > maxDistance) return 0.0f;

        // The score is higher the closer we are.
        float score = 1.0f - (distance / maxDistance);
        
        return Mathf.Clamp01(score);
    }
}

Step 3 & 4: In the Editor

  1. Create the AmmoProximityScorerSO asset.
  2. Add it to the Scorers list in TacticalAISettingsSO and give it a weight (e.g., 1.5). Now, when an AI with this setting is low on ammo, it will strongly prefer cover spots near ammo boxes.

3. Dynamic Behavior with SearchProfile

What is it?
SearchProfile is a class that allows you to temporarily override any aspect of a TacticalAISettingsSO for a single search. You can override the search radius, providers, and most importantly, scorer weights.

Why is it useful?
It allows you to change an AI's "mood" or "tactic" at runtime without needing to swap out its entire TacticalAISettingsSO asset. This is the key to creating responsive and dynamic AI.

  • Low Health: Create a profile that cranks up the weight of DistanceScorerSO and PathSafetyScorerSO.
  • Squad Order to Flank: Create a profile that uses only the CrossfireScorerSO.
  • Player is Distracted: Create a profile that heavily favors the AmbushScorerSO.

How to use it:

You create an instance of SearchProfile in your code, set its properties, and then pass it to enemyHider.RetreatToHide(profile).

Example: "Desperate Retreat" Profile

When an AI's health is critically low, we want it to prioritize safety above all else, even if it means running far away.

using UnityEngine;
using MaharajaStudio.TacticalAI;
using MaharajaStudio.TacticalAI.Scorer;
using System.Collections.Generic;

public class AIHealth : MonoBehaviour
{
    public TacticalAgent hider;
    public TacticalAISettingsSO defaultSettings;
    public int health = 100;

    // References to the specific scorer assets you want to override.
    public DistanceScorerSO distanceScorer;
    public PathSafetyScorerSO pathSafetyScorer;

    public void TakeDamage(int amount)
    {
        health -= amount;
        if (health < 25)
        {
            // Health is critical! Trigger a desperate retreat.
            TriggerDesperateRetreat();
        }
    }

    private void TriggerDesperateRetreat()
    {
        // 1. Create a new profile.
        var desperateProfile = new SearchProfile
        {
            // 2. Create the dictionary for overrides.
            ScorerWeightOverrides = new Dictionary<TacticalScorerSO, float>
            {
                // 3. Add overrides. We use the asset reference as the key.
                // Massively increase the importance of being far away.
                { distanceScorer, 4.0f }, 
                // Also be extra sure the path to get there is safe.
                { pathSafetyScorer, 3.0f }
            },
            // You can also override other things, like the search radius.
            SearchRadius = 50f 
        };

        // 4. Call the search with the custom profile.
        hider.RetreatToHide(desperateProfile);
    }
}

4. Integrating with Game Systems via Services

What are they?
The hiding system includes several static "Service" classes (HazardRegistry, ObjectiveService, ThreatMapService). These are global registries that your own game code must populate with data. The AI system then reads from these services to gain world context.

Why are they useful?
They decouple the AI from your game logic. The AI doesn't need to know what a "control point" is; it only needs to know that the ObjectiveService has a point to defend. This makes integration clean and simple.

How to use them:
From your own game manager, objective controller, or item scripts, call the Register, Unregister, or Update methods on these services.

Example: Registering a Hazard

The HazardAvoidanceScorerSO will make AI avoid any area registered as a hazard. To make it work, your fire/poison/etc. game object needs to tell the HazardRegistry about itself. The provided Hazard.cs component does this automatically.

// Simplified Hazard script logic (as provided in the files)
public class Hazard : MonoBehaviour
{
    public float radius = 5f;

    // When the hazard (e.g., a fire) is created, it registers itself.
    private void OnEnable()
    {
        HazardRegistry.Register(this);
    }

    // When the fire goes out, it unregisters itself.
    private void OnDisable()
    {
        HazardRegistry.Unregister(this);
    }
}

By simply adding the Hazard component to your fire prefab, the AI system will now automatically know to avoid it, provided the HazardAvoidanceScorerSO is in its settings. The same principle applies to ObjectiveService (call ObjectiveService.RegisterObjective(...) from your mission script) and others.


5. Performance Optimization with IMultiThreadedScorer

What is it?
IMultiThreadedScorer is a "marker interface". It has no methods. If your custom TacticalScorerSO implements this interface, the system will automatically run its scoring logic on a background worker thread using Unity's C# Job System and Burst Compiler.

Why is it useful?
Performance. For complex scorers or a large number of AI agents, moving calculations off the main thread is essential to prevent frame rate drops. Scorers that perform heavy math or iterate over many objects are prime candidates for this optimization.

How to use it:

  1. Add IMultiThreadedScorer to your scorer's class definition.
  2. Crucially, you must ensure your Score method is "Burst-compatible". This means:
    • No managed types: You cannot use List<>, Dictionary<>, strings, etc.
    • No Unity main-thread APIs: You cannot call GetComponent<>, GameObject.Find, Debug.Log, or access most parts of Transform beyond its position/rotation.
    • Your logic must rely only on the data already prepared for the job system (like playerPositions, threatPoints, etc., which are passed as NativeArrays).

Example: A Job-Safe Darkness Scorer

The provided DarknessScorerSO is a perfect example.

using UnityEngine;
using MaharajaStudio.TacticalAI.Scorer;

// Note the addition of IMultiThreadedScorer
[CreateAssetMenu(menuName = "HidingSystem/Scorers/Darkness")]
public class DarknessScorerSO : TacticalScorerSO, IMultiThreadedScorer
{
    // The regular Score method is still here.
    public override float Score(in HidingContext ctx, in CandidateInfo candidate)
    {
        // The main-thread logic gets the ambient intensity.
        float lightLevel = RenderSettings.ambientIntensity; 
        return Mathf.Clamp01(1f - lightLevel);
    }
}

When the AsyncHidingSystemCore runs, it sees that DarknessScorerSO implements IMultiThreadedScorer. It fetches RenderSettings.ambientIntensity once on the main thread, passes it to the ScoreCandidatesJob, and the actual calculation (1f - lightLevel) happens for hundreds of candidates in parallel on a background thread.

Core Engine API Documentation

This documentation covers the internal engine of the AI Hiding System. You will rarely call these APIs directly, as the TacticalAgent component manages them for you. However, understanding how they work is crucial for deep customization, performance profiling, and debugging.

The Core Data Flow

At its heart, every search follows this sequence:

  1. The TacticalAgent gathers all relevant world data.
  2. It packages this data into a single, comprehensive HidingContext struct.
  3. This HidingContext is sent to one of the two core processors: SynchronousHidingSystemCore or AsyncHidingSystemCore.
  4. The core processor uses the context to run the Generate -> Score -> Select pipeline.
  5. The result, a HidingSpot struct, is returned to the TacticalAgent, which then acts upon it.

1. Core Data Structures (The "Nouns")

These structs are the lifeblood of the system, carrying information between different stages.

HidingContext

What is it?
The HidingContext is a comprehensive "snapshot" of the world at the exact moment a search is initiated. It contains all the information that providers and scorers need to make their decisions.

Why is it important?
It is the single source of truth for a search operation. By bundling all data together, the system ensures that every provider and scorer is working with the exact same information. It also makes the Score and GenerateCandidates methods clean and predictable, as they receive all their necessary data in one package.

Key Properties:

  • enemyPosition / enemyTransform: The agent's current location.
  • players / allies: Lists of all relevant friendly and enemy transforms.
  • playerLastKnownPosition: The AI's "memory" of where it last saw a threat.
  • settings / lodSettings: The configuration assets that define search parameters.
  • visibilityService: The service used for line-of-sight checks.
  • activeProfile: The temporary SearchProfile used for overrides.
  • Helper Properties (EffectiveSearchRadius, etc.): These automatically resolve whether to use a value from the settings or the override profile.

CandidateInfo

What is it?
A CandidateInfo is the most basic representation of a potential hiding spot. It is the raw output from a TacticalSpotProviderSO.

Why is it important?
This simple, lightweight struct is the common language between all providers and the core system. Providers generate lists of these, and the core system feeds them one-by-one to the scorers for evaluation.

Key Properties:

  • position: The world-space position of the potential spot.
  • coverNormal: The direction pointing away from the surface of the cover object. It is Vector3.zero if the provider couldn't determine a cover direction (e.g., NavMeshRingProviderSO). This normal is crucial for scorers like DirectionalCoverScorerSO.

HidingSpot

What is it?
A HidingSpot is the final, actionable result of a successful search. It's an enhanced CandidateInfo with additional tactical data calculated after the best spot has been chosen.

Why is it important?
This is the "mission plan" given back to the TacticalAgent. It contains not just where to go, but how to get there safely and what the AI can do once it arrives.

Key Properties:

  • position & coverNormal: The position and cover direction of the winning spot.
  • canPeekLeft / canPeekRight: Booleans indicating if the AI can lean out from cover for a shot.
  • peekLeftPosition / peekRightPosition: The world-space positions to move to for peeking.
  • safePath: (Crucial) A pre-calculated NavMeshPath. The PathSafetyScorerSO evaluates this path to ensure it's safe. The TacticalAgent then uses agent.SetPath() to follow this exact route, preventing the agent from taking a different, more dangerous route than the one that was scored.
  • IsValid: A boolean to check if the spot is valid (i.e., not HidingSpot.Invalid).

2. Core Interfaces & Services

IVisibilityService

What is it?
An interface that defines the contract for how the system performs line-of-sight (LOS) checks. DefaultVisibilityService is the standard implementation, which uses Physics.Raycast.

Why is it important?
It centralizes all visibility logic. By using an interface, you could theoretically replace the default physics-based system with something else (e.g., a custom voxel-based visibility grid, a portal-based system) without having to change any of the scorers. The scorers simply ask the service, "Is this point visible?" and get a true/false answer.

Key Methods:

  • AnyPlayerHasLineOfSight(ctx, point): The most common method. Returns true if any player can see the given point.
  • InAnyPlayerFOVAndVisible(ctx, point): A stricter check that also ensures the point is within a player's Field of View cone.

3. Core Processors (The "Verbs")

These are the static classes that contain the main logic loop. TacticalAgent chooses which one to call based on the ExecutionMode.

SynchronousHidingSystemCore

What is it?
This processor runs the entire Generate -> Score -> Select pipeline on the main thread in a single frame.

How does it work?

  1. It receives the HidingContext.
  2. It loops through all EffectiveProviders and collects all CandidateInfos into a single, pooled list.
  3. It then iterates through every candidate. For each candidate, it iterates through every scorer in the settings, calculates the raw score, applies the weight, and sums them up.
  4. If a scorer returns NegativeInfinity, the candidate is immediately disqualified.
  5. It keeps track of the candidate with the highest score.
  6. Finally, it converts the winning CandidateInfo into a HidingSpot by calculating the path and peek points.

API:

  • TryFindBestSpot(in HidingContext, out HidingSpot, out float): Finds the single best spot.
  • TryFindTopSpots(in HidingContext, int count, out List<HidingSpot>): Finds the top N spots, sorted by score.

When to use it:
This mode is used by TacticalAgent for the Synchronous ExecutionMode and for quick, non-blocking queries like CanFindHidingSpot(). It's simpler and easier to debug, but can cause performance issues with many AIs.


AsyncHidingSystemCore

What is it?
This is the high-performance processor that offloads the most expensive parts of the search (raycasting and scoring) to background worker threads using Unity's C# Job System and Burst Compiler.

How does it work?
The process is more complex but significantly faster:

  1. (Main Thread) Preparation:
    • It receives the HidingContext.
    • It generates all CandidateInfos from providers, just like the synchronous core.
    • Crucially, it deconstructs all complex objects (like Transform) into simple, "blittable" data types (Vector3, float) and copies them into NativeArrays. This is a requirement for the Job System.
    • It prepares huge batches of RaycastCommands for every visibility check needed by the scorers (e.g., 3 body points per candidate vs. every player).
  2. (Worker Threads) Execution:
    • It schedules all the RaycastCommands to run in parallel.
    • It then schedules a custom ScoreCandidatesJob. This job receives all the NativeArrays of data and the raycast results.
    • Inside the job, each core of the CPU processes a chunk of the candidates. The scoring logic (for scorers marked with IMultiThreadedScorer) is executed here. This code is often Burst-compiled into highly optimized machine code.
  3. (Main Thread) Finalization:
    • The main thread awaits the completion of the jobs.
    • Once the jobs are done, it retrieves the raw scores from the ScoreCandidatesJob.
    • It then runs any scorers that were not marked as IMultiThreadedScorer (like those requiring NavMesh APIs).
    • Finally, it finds the best score and calculates the final HidingSpot, just like the synchronous version.

API:

  • FindBestSpotAsync(HidingContext, CancellationToken): Asynchronously finds the single best spot. Returns a Task or UniTask.
  • FindTopSpotsAsync(HidingContext, int, CancellationToken): Asynchronously finds and returns a list of the top N spots.

When to use it:
This is the default and recommended mode for production. It ensures that the heavy calculations of the AI have minimal impact on your game's frame rate, allowing for more agents and more complex environments.

Core Engine API Documentation

This documentation covers the internal engine of the AI Hiding System. You will rarely call these APIs directly, as the TacticalAgent component manages them for you. However, understanding how they work is crucial for deep customization, performance profiling, and debugging.

The Core Data Flow

At its heart, every search follows this sequence:

  1. The TacticalAgent gathers all relevant world data.
  2. It packages this data into a single, comprehensive HidingContext struct.
  3. This HidingContext is sent to one of the two core processors: SynchronousHidingSystemCore or AsyncHidingSystemCore.
  4. The core processor uses the context to run the Generate -> Score -> Select pipeline.
  5. The result, a HidingSpot struct, is returned to the TacticalAgent, which then acts upon it.

1. Core Data Structures (The "Nouns")

These structs are the lifeblood of the system, carrying information between different stages.

HidingContext

What is it?
The HidingContext is a comprehensive "snapshot" of the world at the exact moment a search is initiated. It contains all the information that providers and scorers need to make their decisions.

Why is it important?
It is the single source of truth for a search operation. By bundling all data together, the system ensures that every provider and scorer is working with the exact same information. It also makes the Score and GenerateCandidates methods clean and predictable, as they receive all their necessary data in one package.

Key Properties:

  • enemyPosition / enemyTransform: The agent's current location.
  • players / allies: Lists of all relevant friendly and enemy transforms.
  • playerLastKnownPosition: The AI's "memory" of where it last saw a threat.
  • settings / lodSettings: The configuration assets that define search parameters.
  • visibilityService: The service used for line-of-sight checks.
  • activeProfile: The temporary SearchProfile used for overrides.
  • Helper Properties (EffectiveSearchRadius, etc.): These automatically resolve whether to use a value from the settings or the override profile.

CandidateInfo

What is it?
A CandidateInfo is the most basic representation of a potential hiding spot. It is the raw output from a TacticalSpotProviderSO.

Why is it important?
This simple, lightweight struct is the common language between all providers and the core system. Providers generate lists of these, and the core system feeds them one-by-one to the scorers for evaluation.

Key Properties:

  • position: The world-space position of the potential spot.
  • coverNormal: The direction pointing away from the surface of the cover object. It is Vector3.zero if the provider couldn't determine a cover direction (e.g., NavMeshRingProviderSO). This normal is crucial for scorers like DirectionalCoverScorerSO.

HidingSpot

What is it?
A HidingSpot is the final, actionable result of a successful search. It's an enhanced CandidateInfo with additional tactical data calculated after the best spot has been chosen.

Why is it important?
This is the "mission plan" given back to the TacticalAgent. It contains not just where to go, but how to get there safely and what the AI can do once it arrives.

Key Properties:

  • position & coverNormal: The position and cover direction of the winning spot.
  • canPeekLeft / canPeekRight: Booleans indicating if the AI can lean out from cover for a shot.
  • peekLeftPosition / peekRightPosition: The world-space positions to move to for peeking.
  • safePath: (Crucial) A pre-calculated NavMeshPath. The PathSafetyScorerSO evaluates this path to ensure it's safe. The TacticalAgent then uses agent.SetPath() to follow this exact route, preventing the agent from taking a different, more dangerous route than the one that was scored.
  • IsValid: A boolean to check if the spot is valid (i.e., not HidingSpot.Invalid).

2. Core Interfaces & Services

IVisibilityService

What is it?
An interface that defines the contract for how the system performs line-of-sight (LOS) checks. DefaultVisibilityService is the standard implementation, which uses Physics.Raycast.

Why is it important?
It centralizes all visibility logic. By using an interface, you could theoretically replace the default physics-based system with something else (e.g., a custom voxel-based visibility grid, a portal-based system) without having to change any of the scorers. The scorers simply ask the service, "Is this point visible?" and get a true/false answer.

Key Methods:

  • AnyPlayerHasLineOfSight(ctx, point): The most common method. Returns true if any player can see the given point.
  • InAnyPlayerFOVAndVisible(ctx, point): A stricter check that also ensures the point is within a player's Field of View cone.

3. Core Processors (The "Verbs")

These are the static classes that contain the main logic loop. TacticalAgent chooses which one to call based on the ExecutionMode.

SynchronousHidingSystemCore

What is it?
This processor runs the entire Generate -> Score -> Select pipeline on the main thread in a single frame.

How does it work?

  1. It receives the HidingContext.
  2. It loops through all EffectiveProviders and collects all CandidateInfos into a single, pooled list.
  3. It then iterates through every candidate. For each candidate, it iterates through every scorer in the settings, calculates the raw score, applies the weight, and sums them up.
  4. If a scorer returns NegativeInfinity, the candidate is immediately disqualified.
  5. It keeps track of the candidate with the highest score.
  6. Finally, it converts the winning CandidateInfo into a HidingSpot by calculating the path and peek points.

API:

  • TryFindBestSpot(in HidingContext, out HidingSpot, out float): Finds the single best spot.
  • TryFindTopSpots(in HidingContext, int count, out List<HidingSpot>): Finds the top N spots, sorted by score.

When to use it:
This mode is used by TacticalAgent for the Synchronous ExecutionMode and for quick, non-blocking queries like CanFindHidingSpot(). It's simpler and easier to debug, but can cause performance issues with many AIs.


AsyncHidingSystemCore

What is it?
This is the high-performance processor that offloads the most expensive parts of the search (raycasting and scoring) to background worker threads using Unity's C# Job System and Burst Compiler.

How does it work?
The process is more complex but significantly faster:

  1. (Main Thread) Preparation:
    • It receives the HidingContext.
    • It generates all CandidateInfos from providers, just like the synchronous core.
    • Crucially, it deconstructs all complex objects (like Transform) into simple, "blittable" data types (Vector3, float) and copies them into NativeArrays. This is a requirement for the Job System.
    • It prepares huge batches of RaycastCommands for every visibility check needed by the scorers (e.g., 3 body points per candidate vs. every player).
  2. (Worker Threads) Execution:
    • It schedules all the RaycastCommands to run in parallel.
    • It then schedules a custom ScoreCandidatesJob. This job receives all the NativeArrays of data and the raycast results.
    • Inside the job, each core of the CPU processes a chunk of the candidates. The scoring logic (for scorers marked with IMultiThreadedScorer) is executed here. This code is often Burst-compiled into highly optimized machine code.
  3. (Main Thread) Finalization:
    • The main thread awaits the completion of the jobs.
    • Once the jobs are done, it retrieves the raw scores from the ScoreCandidatesJob.
    • It then runs any scorers that were not marked as IMultiThreadedScorer (like those requiring NavMesh APIs).
    • Finally, it finds the best score and calculates the final HidingSpot, just like the synchronous version.

API:

  • FindBestSpotAsync(HidingContext, CancellationToken): Asynchronously finds the single best spot. Returns a Task or UniTask.
  • FindTopSpotsAsync(HidingContext, int, CancellationToken): Asynchronously finds and returns a list of the top N spots.

When to use it:
This is the default and recommended mode for production. It ensures that the heavy calculations of the AI have minimal impact on your game's frame rate, allowing for more agents and more complex environments.

Environmental Helpers

These components are added to objects in your scene to provide more tactical information to the AI. This includes CoverNode (manual cover spots), Hazard (danger zones), TacticalZone (strategic areas), and VolumetricCover (soft cover like smoke).

Feature Guide: CoverNode

The CoverNode component is a simple and direct way for level designers to manually place a high-quality, explicit hiding spot in the world. It gives you precise, pixel-perfect control over where an AI can take cover, ensuring that tactically important locations are always available for the AI to use.

Why Use It?

  • Designer Control: Guarantees that specific locations are always considered as potential cover, overriding procedural generation.
  • Tactical Precision: Perfect for creating specific flanking routes, ambush points, or defensive strongholds.
  • Reliability: Ensures that cover is available even in complex or geometrically noisy environments where automatic providers might struggle.
  • Intelligence: When used with the DirectionalCoverNodeProviderSO, the AI can intelligently select only the nodes that provide cover relative to the current threat direction.

Setup Guide

  1. In your scene, create a new Empty GameObject at the exact position where you want the AI to stand.
  2. Select the new GameObject and click Add Component. Search for CoverNode and add the script.
  3. In the Scene View, you will now see a cyan arrow gizmo originating from your GameObject. This arrow represents the direction of safety. An AI at this spot is considered "in cover" when the threat is behind them (and the arrow points away from the wall/cover).
  4. Adjust the Cover Direction vector in the Inspector, or simply rotate the GameObject itself, to orient the cover correctly. The arrow should point away from the physical object that provides cover.

Parameters

  • coverDirection (Vector3): This vector defines the direction an agent should be facing to be considered "in cover." It's visualized by the cyan arrow in the editor. The DirectionalCoverNodeProviderSO uses this to check if the node is a valid hiding spot from the current threat.

Best Practices

  • Placement: Position the CoverNode slightly away from the wall or object providing cover. This ensures the NavMesh Agent has enough space to stand without clipping into the geometry.
  • Use with Directional Provider: For the best results, use the DirectionalCoverNodeProviderSO in your TacticalAISettings. This provider will intelligently filter the nodes, selecting only those whose coverDirection is pointing away from the player, resulting in much more believable AI behavior.

Feature Guide: TacticalAgent Component

The TacticalAgent component is the central brain and primary interface for the Tactical Cover & Retreat AI System. You attach this component directly to your AI agent's GameObject. It acts as a "facade," simplifying the complex process of finding cover into a set of easy-to-use commands and events.

Why Use It?

The TacticalAgent is the bridge between your AI's decision-making logic (like a Behavior Tree or State Machine) and the hiding system's core functionality. It manages the search process, handles asynchronous operations, and translates the final result into movement for your AI.

  • Centralized Control: Manages all settings, targets, and state for a single AI agent.
  • Performance Options: Choose between a high-performance, multi-threaded Asynchronous mode or a simple, easy-to-debug Synchronous mode.
  • Event-Driven: Provides UnityEvents to easily hook up animations, sound effects, or other gameplay logic without needing to write extra code.
  • Extensible: Can be linked with custom ICombatAction scripts to execute complex tactical maneuvers after finding cover, instead of just moving.

Setup Guide

  1. Select your AI agent's GameObject in the scene.
  2. Click Add Component and search for TacticalAgent. Add the script.
  3. Drag your TacticalAISettingsSO asset into the Settings field. This asset acts as the "rulebook" for the AI's decisions.
  4. Drag the NavMeshAgent component from your AI into the Agent field. The system will use this to move the AI.
  5. (Optional) If you have a custom script that implements the ICombatAction interface, assign its GameObject to the Combat Action Game Object field.

Inspector Parameters

System Settings

  • Execution Mode:
    • Asynchronous: The recommended mode for best performance. It uses the Unity Job System and Burst Compiler to perform searches on background threads, preventing game freezes.
    • Synchronous: A simpler mode where the search happens on the main thread. Useful for debugging as the code flow is easier to follow, but may cause performance hitches with many agents or complex searches.

Fallback Settings

  • Allow Desperate Retreat: If checked, the AI will perform a second, less strict search if the first one fails to find a perfectly safe path. This is crucial for preventing the AI from getting "stuck" and doing nothing when it's surrounded or in a very open area.

Component References

  • Settings: The TacticalAISettingsSO asset that defines the AI's behavior. This is the most critical reference.
  • Agent: The NavMeshAgent component on this AI, used for pathfinding and movement.
  • Combat Action Game Object: An optional field for advanced users. Assign a GameObject that has a component implementing the ICombatAction interface to override the default movement behavior with your own custom logic (e.g., peek-and-shoot, suppressive fire).
  • Players / Allies: Lists for Transform components. These are primarily for quick testing in the editor. In a real game, you will likely set these lists at runtime using the public API.

Events

  • On Search Started: A UnityEvent that fires the moment RetreatToHide() is called. Useful for triggering "taking cover!" voice lines or changing the AI's state to "retreating."
  • On Search Success: A UnityEvent that fires when a valid hiding spot is found. It passes the HidingSpot data, which you can use for custom logic.
  • On Search Failed: A UnityEvent that fires if no suitable hiding spot could be found, even after a desperate retreat. Useful for triggering a fallback behavior, like a last-stand fight.

Public API for Programmers

The TacticalAgent is controlled from your own AI scripts.

// In your AI's main controller script (e.g., EnemyFSM.cs)

using MaharajaStudio.TacticalAI;

public class MyAIController : MonoBehaviour
{
    private TacticalAgent hider;
    public Transform player;

    void Start()
    {
        hider = GetComponent();
    }

    void Update()
    {
        // Your logic decides WHEN to hide.
        if (IsUnderFire() && !hider.IsSearching)
        {
            // Tell the hider who to hide from.
            hider.SetSinglePlayerTarget(player);

            // Command the hider to find a spot and move.
            hider.RetreatToHide();
        }
    }

    bool IsUnderFire() { /* ... your custom logic ... */ return true; }
}
  • RetreatToHide(): The main function. Initiates the search for a hiding spot based on the current settings and targets.
  • CancelSearch(): Immediately stops an ongoing Asynchronous search.
  • SetPlayers(IReadOnlyList playerList): Sets the list of enemies to hide from.
  • SetAllies(IReadOnlyList allyList): Sets the list of teammates for coordinated tactics.
  • IsSearching (property): A read-only boolean that is true while a search is in progress.

Feature Guide: Hazard

The Hazard component allows you to designate a spherical area in your scene as dangerous. The AI system will actively avoid choosing any hiding spots that fall within the radius of a Hazard object, making the AI appear smarter and more aware of environmental dangers.

Why Use It?

  • Environmental Awareness: Prevents the AI from making foolish mistakes, like hiding in a pool of fire, an acid spill, or an active minefield.
  • Dynamic Obstacles: Can be added at runtime to temporarily block off areas. For example, you could spawn a GameObject with a Hazard component when a grenade explodes to prevent AI from running into the blast zone.
  • Designer-Friendly: Provides a simple and visual way to define "no-go" zones for the AI without complex NavMesh modifications.

Setup Guide

  1. Select the GameObject in your scene that represents a dangerous area (e.g., a fire particle effect, a poison cloud).
  2. Click Add Component and search for Hazard. Add the script.
  3. Adjust the Radius property in the Inspector to define the size of the dangerous area.
  4. When the GameObject is selected, a semi-transparent red sphere gizmo will be drawn in the Scene View, clearly visualizing the hazard's area of effect.

Parameters

  • radius (float): The radius of the dangerous spherical area, measured in meters from the GameObject's transform origin.

How It Works

The Hazard component works in conjunction with the HazardAvoidanceScorerSO. If this scorer is included in your TacticalAISettingsSO, it will perform a check for every potential hiding spot. If a spot is found to be inside the radius of any active Hazard in the scene, the scorer immediately returns a score of NegativeInfinity, which instantly disqualifies that spot from being chosen.

Feature Guide: PlayerHeightInfo

The PlayerHeightInfo component is a simple but powerful tool that allows you to specify the eye height of individual player characters. This is essential for creating varied gameplay scenarios where players might have different character models, stances (crouching, prone), or even be children or large monsters. The AI system will use this information to perform highly accurate line-of-sight (LOS) checks, making it smarter and more reactive to the actual tactical situation.

Why Use It?

  • Accurate Visibility: If a player is crouching, the AI will correctly understand that they can't see over certain obstacles.
  • Support for Different Characters: Allows for players of different sizes (e.g., a tall robot vs. a small goblin) to be evaluated correctly.
  • Dynamic Stances: Works perfectly with player controllers that allow crouching or going prone. By changing the currentEyeHeight value at runtime, you can make the AI's perception dynamically adapt to the player's actions.

Setup Guide

  1. Select your Player Prefab or the player character GameObject in your scene.
  2. Click Add Component and search for PlayerHeightInfo. Add the script.
  3. That's it! The AI system will now automatically detect and use this component.

Parameters

  • currentEyeHeight (float): The most important field. This value represents the height of the player's "eyes" or camera, measured in meters from the GameObject's transform origin (pivot).
  • Default Value: 1.7 (a reasonable height for an average standing adult).
  • Runtime Changes: If you have a player controller script, it can get a reference to this component and change this value when the player crouches or stands up. The AI will immediately use the new value in its next calculation.

Example: Integrating with a Player Controller

Here is a small code snippet showing how a player's script might update this value.

// In your PlayerController.cs or similar script

public PlayerHeightInfo playerHeight; // Assign this in the Inspector
public float standingHeight = 1.7f;
public float crouchingHeight = 0.9f;

void OnCrouch()
{
    // When the player crouches, update the height info.
    playerHeight.currentEyeHeight = crouchingHeight;
}

void OnStandUp()
{
    // When the player stands up, set it back.
    playerHeight.currentEyeHeight = standingHeight;
}

If a player GameObject does not have this component, the AI will gracefully fall back to using the default playerEyeHeight value defined in your TacticalAISettingsSO asset for that player.

Feature Guide: TacticalZone

The TacticalZone component allows you to define a large volume of space with a specific tactical purpose. It's a powerful tool for level designers to guide the AI's high-level strategy, encouraging it to choose hiding spots that align with a broader team objective or a specific combat style.

Why Use It?

  • Strategic Guidance: Nudge AI to find cover in tactically advantageous areas. For example, you can create an "Overwatch" zone on a balcony to encourage sniper-type AI to hide there.
  • Define Combat Areas: Create "Ambush" zones in forests or "Defensive" zones around a capture point to make the AI's behavior context-aware.
  • Improves Provider Efficiency: The TacticalZoneProviderSO can be set to only generate candidates within a specific zone type, focusing the AI's search and improving performance.

Setup Guide

  1. Create an empty GameObject to represent your zone.
  2. Add a Collider component to it (e.g., a BoxCollider or SphereCollider). This collider defines the boundaries of the zone.
  3. Crucially, check the Is Trigger box on the Collider component. This prevents it from physically blocking characters.
  4. Click Add Component and add the TacticalZone script.
  5. In the TacticalZone component, select the desired Zone Type from the dropdown. The gizmo color in the scene will update to reflect your choice.

Parameters

  • zoneType (TacticalZoneType): The tactical purpose of this area.
    • Defensive: Best for holding a critical position or objective.
    • Overwatch: Ideal for elevated positions with good sightlines (e.g., sniper nests).
    • Ambush: Suited for concealed areas intended for flanking or surprise attacks.
    • FlankRoute: Can be used to designate paths or areas that are good for repositioning.

How It Works

The TacticalZone component is used by the TacticalZoneProviderSO. In your TacticalAISettingsSO, you add this provider and configure its Target Zone Type to match the zones you want the AI to use. When the AI searches for cover, this provider will only generate potential hiding spots inside the bounds of TacticalZones that match its target type.

Feature Guide: VolumetricCover

The VolumetricCover component designates a volume of space (like a smoke cloud, dense fog, or thick foliage) as "soft cover." This type of cover provides concealment and can block an AI's line of sight, but it does not physically stop movement or projectiles.

Why Use It?

  • Dynamic Gameplay: Allows the AI to intelligently use temporary cover like smoke grenades.
  • Richer Environments: Makes natural environments more tactically interesting by allowing AI to hide in thick bushes or foggy areas.
  • Enhanced Stealth: Provides more opportunities for both players and AI to break line of sight and reposition, leading to more dynamic stealth and combat encounters.

Setup Guide

  1. Select the GameObject that represents your soft cover (e.g., a smoke grenade particle effect, a large bush model).
  2. Add a Collider component to it (BoxCollider, SphereCollider, etc.) to define its boundaries.
  3. Crucially, check the Is Trigger box on the Collider. This ensures it provides concealment without physically blocking characters.
  4. Click Add Component and add the VolumetricCover script.
  5. A semi-transparent gray gizmo will be drawn in the Scene View, showing the volume of the soft cover.

How It Works

This component is used by the VolumetricCoverScorerSO. This scorer has special logic:

  • It first checks if a potential hiding spot is visible to any player based on normal, solid geometry.
  • If the spot is visible, the scorer then performs another check: it casts a ray from the player to the hiding spot.
  • If this ray intersects with the collider of any active VolumetricCover object, the scorer considers the line of sight to be blocked.
  • It then returns a high score, effectively turning an otherwise exposed position into a viable hiding spot.

Providers

Providers are specialized ScriptableObjects that generate a list of potential hiding spots (candidates). Each provider has a unique method for finding spots, allowing you to mix and match them to create complex AI behavior.

Provider: Automatic Cover Finder

AutomaticCoverProviderSO is an intelligent provider that automatically finds cover spots without manual tagging by sampling the environment and verifying that hidden locations are actually concealed by a nearby obstacle.

How It Works

This provider intelligently searches the area around the agent to find tactically sound cover positions. It operates in several steps:

  • Sample Generation: It generates sample points in a series of concentric rings around the agent's current position.
  • NavMesh Validation: For each sample point, it finds the closest valid location on the NavMesh to ensure the agent can reach it.
  • Visibility Check: It then performs a critical check to see if the NavMesh point is visible to any player. If it has a clear line of sight to a player, the point is discarded as it is not "in cover."
  • Cover Verification: If the point is hidden, the provider performs a final, crucial verification. It casts a ray from the hidden spot towards the nearest player. If this ray hits a nearby object (on the Occluder Mask layer), it confirms that there is a physical obstacle providing the cover.
  • Candidate Creation: If all checks pass, the point is added as a valid candidate. Importantly, it also records the normal of the obstacle's surface that was hit. This coverNormal (the direction pointing away from the cover) is invaluable information for advanced scoring and the peeking system.

Parameters

This provider has its own specific settings and also uses global settings from your TacticalAISettings asset.

  • Rings: The number of concentric rings to sample around the agent. More rings mean a wider and more thorough search.
  • Max Cover Distance: The maximum distance from the hidden sample point that the provider will check for a wall or obstacle. This prevents the AI from thinking it's in cover when it's just hidden in an open field far from any object.
  • Search Radius (from TacticalAISettings): Defines the maximum radius of the outermost search ring.
  • Occluder Mask (from TacticalAISettings): A layer mask that determines which objects are considered valid cover obstacles.
  • Candidates Per Provider (from LOD Settings): Determines how many sample points are generated per ring.

Use Cases

  • Dynamic Environments: Perfect for levels where cover positions can change or are not known in advance, such as procedural worlds or maps with destructible environments.
  • Reducing Design Workload: A powerful "set it and forget it" provider that removes the need for designers to manually place hundreds of cover nodes.
  • High-Fidelity AI: Because it verifies cover and provides a surface normal, it enables more advanced behaviors like peeking and directional cover scoring, leading to more believable AI.

Performance

This is one of the more computationally intensive providers due to its multi-step verification process. For each potential candidate, it performs: NavMesh.SamplePosition, IVisibilityService.AnyPlayerHasLineOfSight (which involves raycasts), and an additional Physics.Raycast to verify the cover. The total performance cost scales with the Rings and Candidates Per Provider settings. While extremely effective, it should be used with care, especially if you have many AI agents searching simultaneously. Consider lowering the candidate count in higher-level LOD settings.

Provider: Behind Obstacle

BehindObstacleProviderSO is a provider that finds potential hiding spots by looking for locations directly behind environmental obstacles, relative to the agent's position.

How It Works

This provider operates by casting a series of rays outward from the agent's current position in a 360-degree circle. When a ray hits an object on a layer specified by the Occluder Mask (in your TacticalAISettings), the provider then calculates a point a short distance behind that obstacle.

It then checks if this calculated point is on a valid NavMesh area. If it is, the point is added to the list of potential hiding spots.

A key feature of this provider is that it also stores the normal of the surface it hit. This information can be used by certain scorers (like the DefensivePositionScorerSO) and by the system's built-in peeking logic to evaluate not just the position, but also the quality and direction of the cover.

Parameters

This provider's behavior is controlled by the global settings in your TacticalAISettings asset.

  • Search Radius: Defines how far the rays are cast from the agent to look for obstacles. A larger radius means the AI will consider cover that is farther away.
  • Occluder Mask: A physics layer mask that determines which objects are considered valid obstacles (e.g., walls, large props).
  • NavMesh Area Mask: Defines which NavMesh areas are considered valid for hiding spots.
  • Candidates Per Provider (from LOD Settings): Controls the number of rays cast in the 360-degree circle. A higher number increases the search density and the likelihood of finding small pieces of cover, but it also increases the performance cost.

Use Cases

  • Classic Stealth: This is a fundamental provider for any stealth scenario where you want AI to hide behind walls, crates, pillars, or other large objects.
  • Dynamic Cover Finding: Excellent for situations where the environment is not explicitly marked up with cover points. The AI will dynamically find cover based on the physical geometry of the level.

Performance

The performance cost of this provider is directly related to the Candidates Per Provider setting. Each candidate results in a Physics.Raycast call. While generally efficient, using a very high number of candidates on many agents simultaneously could impact performance. Adjust this setting based on your target platform and game's needs.

Provider: Cover Nodes

CoverNodeProviderSO is a provider that uses a pre-defined set of hiding spots that have been manually placed in the scene by a designer.

How It Works

This provider relies on a network of "Cover Nodes" that you place throughout your level. Each node is a simple GameObject with a specific component (CoverNode, which registers it to the CoverNodeRegistry) that marks it as a potential hiding spot.

When this provider is active, it doesn't search the environment dynamically. Instead, it gets a list of all registered CoverNode objects from the central CoverNodeRegistry. It then uses the position of each node as a candidate hiding spot.

Before adding a spot, it verifies that the node's position is on a valid NavMesh area, ensuring the AI can actually reach it.

This provider does not provide a cover normal, so scorers that rely on the direction of cover will not benefit from it, and the system's peeking logic will not be engaged for spots found by this provider.

Setup

To use this provider, you must manually place CoverNode components in your scene.

  1. Create an empty GameObject in your scene.
  2. Place it where you want the AI to be able to hide (e.g., behind a crate, at the corner of a wall).
  3. Add the CoverNode component to this GameObject.
  4. Repeat for all desired hiding spots.

Parameters

  • NavMesh Area Mask (from TacticalAISettings): Defines which NavMesh areas are considered valid. If a CoverNode is placed on an invalid area, it will be ignored.

Use Cases

  • Highly Controlled Level Design: This is the perfect provider when you want exact, deliberate control over where your AI can hide. It removes the guesswork of dynamic systems.
  • Performance Optimization: This is one of the most performant providers. It avoids costly physics queries or complex calculations by relying on a simple, pre-computed list of points. It's an excellent choice for performance-critical scenarios or mobile platforms.
  • Defining Tactical Positions: Use it to mark specific strategic locations in your level that AI should use for cover, ambushes, or defensive positions.

Performance

This provider has a very low performance overhead. Its main cost is iterating through the list of registered nodes. The performance impact is minimal, even with hundreds of nodes in a scene, making it significantly faster than dynamic providers like BehindObstacleProviderSO or VoxelGridProviderSO.

Provider: Current Position

CurrentPositionProviderSO is a simple but essential provider that adds the agent's own current position to the list of candidate hiding spots.

How It Works

This provider does not search the environment. Its only job is to take the agent's current position (enemyPosition from the HidingContext), check that it's on a valid NavMesh, and add it to the list of potential spots to be evaluated by the scorers.

The purpose of this is to establish a baseline. By including its current location in the evaluation, the AI can compare all other potential hiding spots to the one it's already in. If no other spot offers a significant advantage, the agent's current position might receive the highest score, causing the AI to "choose" to stay put.

This provider does not generate a cover normal.

Parameters

  • NavMesh Area Mask (from TacticalAISettings): Ensures the agent's current position is on a valid NavMesh area.

Use Cases

  • Preventing Unnecessary Movement: This is a crucial provider for almost every configuration. It prevents the AI from "fidgeting" or abandoning a perfectly good hiding spot for another that isn't substantially better. It adds stability to the AI's decision-making.
  • Establishing a "Cost of Moving": By forcing new spots to be better than the current one, you implicitly create a cost for moving. The AI will only move if it finds a spot that is demonstrably superior according to the active scorers.

Performance

This is one of the highest-performance providers available. It adds only a single candidate to the list and performs just one NavMesh.SamplePosition call. Its performance impact is negligible, and it is highly recommended to include it in most TacticalAISettings configurations.

Provider: Directional Cover Nodes

DirectionalCoverNodeProviderSO is an intelligent version of the CoverNodeProviderSO. It selects pre-placed CoverNode objects only if they are oriented correctly, providing cover that faces away from the player's position.

How It Works

Like the standard CoverNodeProvider, this provider uses the central CoverNodeRegistry to get a list of all manually placed cover points in the scene. However, it adds a crucial filtering step.

For each CoverNode, it checks the node's defined "cover direction." It then compares this direction to the direction from the AI agent to the player(s). The node is only considered a valid candidate if its cover direction is pointing generally away from the player.

This ensures that the AI doesn't try to take cover on the wrong side of an object, making for much more believable and effective tactical behavior.

This provider does not provide a cover normal for scorers or the peeking system to use.

Setup

The setup is the same as the CoverNodeProvider, but with one extra step:

  1. Create an empty GameObject and add the CoverNode component.
  2. Crucially, adjust the Cover Direction vector in the CoverNode component's settings. This vector should point in the direction the cover is "facing." For example, for a chest-high wall, the direction should point straight up from the wall's top edge, away from the side the AI should stand on. A gizmo in the scene view will help you visualize this direction.

Parameters

  • Acceptance Angle: This is a key setting on the provider asset itself. It defines how forgiving the directional check is. The value represents the total angle of the cone used for the check.
    • A small angle (e.g., 30) means the cover must be almost perfectly oriented away from the player to be considered. This is strict but very precise.
    • A large angle (e.g., 180) means the cover can be facing in any direction in the hemisphere away from the player. This is very lenient.
  • NavMesh Area Mask (from TacticalAISettings): Ensures the selected node is on a valid NavMesh area.

Use Cases

  • Smart Tactical Cover: This is the primary use case. It allows you to design levels with clear tactical positions (e.g., L-shaped corners, barricades) and have the AI use them intelligently based on the threat direction.
  • Flanking and Positioning: By ensuring cover is used correctly, you can create more advanced behaviors where AI effectively suppresses the player from safe positions.

Performance

The performance is excellent and very close to the standard CoverNodeProvider. It adds a simple vector math calculation (Vector3.Angle) for each node, but this is extremely fast. It remains one of the most performant providers and is a great choice for nearly any project that uses manually placed cover points.

Provider: Hide Spot Provider (Base Class)

TacticalSpotProviderSO is not a functional provider itself. It is an abstract base class that serves as the template for all other provider assets.

How It Works

The Hiding System is designed to be modular. Instead of having a single, monolithic way of finding hiding spots, it uses a collection of smaller, specialized "providers." Each provider is a ScriptableObject that knows one specific way to generate a list of potential hiding spots (candidates).

The TacticalSpotProviderSO class defines the contract that every provider must follow. It contains a single abstract method:

public abstract void GenerateCandidates(in HidingContext ctx, List results);
  • HidingContext ctx: A struct containing all the information the provider might need, such as the agent's position, player locations, and system settings.
  • List results: The list that the provider must add its generated candidate spots to.

By inheriting from this class, you can create your own custom logic for finding cover, which will seamlessly plug into the main hiding system alongside the built-in providers.

Setup

To create a new, custom provider:

  1. Create a new C# script.
  2. Make the class inherit from TacticalSpotProviderSO.
  3. Add the [CreateAssetMenu] attribute to allow you to create instances of it in the Unity Editor.
  4. Implement the GenerateCandidates method with your custom logic for finding spots.
  5. Create an instance of your new provider asset via the Assets -> Create menu.
  6. Drag your new provider asset into the Providers array in your TacticalAISettingsSO.

Use Cases

  • System Extensibility: The primary use is to allow developers to extend the hiding system with novel techniques for finding cover without modifying the core system code.
  • Project-Specific Logic: You can create providers tailored to the unique mechanics of your game. For example, a provider that finds hiding spots in magical portals, under brush, or in areas designated by a special trigger volume.

Performance

As a base class, it has no performance impact. The performance of a provider is entirely dependent on the logic implemented in its GenerateCandidates method.

Provider: Opposite Players Direction

OppositePlayersProviderSO is a provider designed to find hiding spots that are located in the general direction away from the player or players.

How It Works

This provider first determines the average direction from the AI agent to all known player positions. It then inverts this vector to get a primary direction that points directly away from the threat.

It then generates a number of candidate points along this "away" vector, at various distances up to the Search Radius. A small amount of random jitter is added to each point to spread the candidates out and prevent them from all falling on a single line, allowing for more flexible positioning.

Finally, each candidate point is checked to ensure it's on a valid NavMesh area before being added to the list of potential spots. This provider does not generate a cover normal.

Parameters

This provider's behavior is controlled by the global settings in your TacticalAISettings asset.

  • Search Radius: Defines the maximum distance the provider will look for spots along the "away" vector.
  • Candidates Per Provider (from LOD Settings): Controls how many sample points are generated. More candidates will create a denser search pattern in the "away" direction.
  • NavMesh Area Mask: Restricts the search to specific types of NavMesh areas.

Use Cases

  • Retreating and Kiting: This is the ideal provider for AI that needs to create distance from a threat. It's perfect for ranged characters who want to kite their enemies, or for "cowardly" AI that tries to flee from combat.
  • Strategic Repositioning: When an AI is in a bad spot, this provider can help it find a safer location that is generally farther away from the player, forcing the player to close the distance again.
  • Breaking Line of Sight: While it doesn't explicitly check for line of sight, by moving in the opposite direction of a player, the AI naturally increases its chances of putting obstacles between itself and the player.

Performance

The performance of this provider is very good. Its cost is directly proportional to the Candidates Per Provider setting, as each candidate requires a NavMesh.SamplePosition call. It does not perform any expensive physics raycasts, making it significantly cheaper than providers like BehindObstacleProviderSO. It is a lightweight and effective way to generate directional movement.

Provider: Shadow Provider

ShadowProviderSO is a specialized provider that finds potential hiding spots by locating areas on the NavMesh that are currently in shadow.

How It Works

This provider simulates how light and shadow work in the environment. It requires a reference to a dominant Light source in the scene (typically a Directional Light representing the sun).

For a number of sample points around the agent, it performs the following check:

  1. It finds a random, reachable point on the NavMesh within the Search Radius.
  2. It casts a ray from a position high above this point, aiming downwards along the direction of the dominant light.
  3. If this ray hits an object (on a layer specified by the Occluder Mask) before it reaches the ground point, it means the point is in shadow.
  4. Shadowed points that are on the NavMesh are added to the list of candidate hiding spots.

This provider does not generate a cover normal.

Parameters

  • Dominant Light: A direct reference to the Light component that should be treated as the primary shadow-casting light source in the scene. If this is not assigned, the provider will try to find the first available Light component, but it is highly recommended to assign this manually for predictable behavior.
  • Raycast Height Offset: Determines how far above the sample point the shadow-checking raycast begins. This value should be large enough to ensure the ray starts above any potential geometry that could cast a shadow.
  • Search Radius (from TacticalAISettings): The radius around the agent in which to search for shadowed spots.
  • Occluder Mask (from TacticalAISettings): A physics layer mask that determines which objects are capable of casting shadows.
  • Candidates Per Provider (from LOD Settings): The number of random points to test for shadow coverage.

Use Cases

  • Light-and-Shadow Stealth: This is the core provider for any game where stealth is based on hiding in the dark (e.g., Thief, Splinter Cell).
  • Nocturnal or Light-Sensitive AI: Perfect for creating creatures of the night, vampires, or other AI that are harmed by or actively avoid light.
  • Ambiance and Believability: Can be used subtly to make AI seem more intelligent, as they will appear to naturally use shadows to their advantage when approaching or hiding from the player.

Performance

The performance cost of this provider is directly related to the Candidates Per Provider setting. Each candidate requires a NavMesh.SamplePosition call followed by a Physics.Raycast. This makes it one of the more computationally intensive providers, with a cost similar to BehindObstacleProviderSO.

For best performance:

  • Keep the Candidates Per Provider value as low as you can while still getting good results.
  • Always assign the Dominant Light reference manually to avoid the overhead of the system having to search for it.

Provider: Tactical Zone

TacticalZoneProviderSO is a provider that generates candidate hiding spots within specific, designer-placed volumes called Tactical Zones. This allows for a high degree of control over the AI's positioning, guiding it to use areas intended for specific tactical purposes.

How It Works

This system requires designers to place TacticalZone components on GameObjects with colliders in the scene. Each zone is assigned a type (e.g., Overwatch, Ambush, Defensive).

The TacticalZoneProviderSO asset is configured to look for a specific TacticalZoneType. When it runs, it queries the central TacticalZoneRegistry to find all zones of that type within the agent's search radius.

For each matching zone, the provider generates a number of random points within the zone's collider bounds. It then finds the closest valid point on the NavMesh for each random sample. These valid NavMesh points become the candidate hiding spots.

This effectively lets a designer say, "When you are in a defensive state, find cover inside one of these designated defensive areas."

Setup

  1. Create an empty GameObject in your scene to act as the zone.
  2. Add a collider component (e.g., BoxCollider). Mark it as a Trigger.
  3. Add the new TacticalZone component to this GameObject.
  4. In the TacticalZone component, set the Zone Type to the desired tactical purpose (e.g., Defensive).
  5. Adjust the size and position of the collider to cover the tactical area. A gizmo will show the zone's color and type in the Scene view.
  6. Create an instance of the TacticalZoneProviderSO asset in your project files.
  7. In the provider asset's Inspector, set the Target Zone Type to match the zones you want it to find.
  8. Add this provider asset to the Providers list in your TacticalAISettingsSO.

Use Cases

  • Designer-Controlled Encounters: The primary use case. Force AI snipers to use sniper nests, or ensure ambushing enemies hide in designated ambush locations.
  • Controlling Flow of Combat: Guide the AI to fall back to specific, pre-defined defensive lines or to use flanking routes that you have explicitly laid out.

Performance

Since it only samples within a few known zones instead of the entire world, it can be more performant than wide-area environmental scanners if used correctly.

Provider: Vertical Layer

VerticalLayerProviderSO generates candidate hiding spots on different vertical levels from the agent, making it ideal for multi-floor environments like buildings, canyons, or areas with catwalks.

How It Works

This provider expands the search for cover into 3D space, allowing the AI to consider hiding spots above or below its current position.

  • Define Layers: It uses the agent's current position as a starting point and calculates the center points for layers above and below based on the Layer Height setting.
  • Generate Points: On each of these vertical layers, it generates a series of sample points in concentric rings, similar to the NavMeshRingProvider.
  • NavMesh Validation: For each sample point, it uses NavMesh.SamplePosition to find a valid, reachable location on the NavMesh. The search range for this check is limited to half the layer height to prevent it from finding spots on the wrong floor.
  • Optional Cover Normal: To add more intelligence, it performs a quick check for nearby cover by casting a ray from the valid NavMesh spot back towards the center of the layer. If it hits an occluder, it records the hit.normal as the coverNormal. If not, the spot is still added, but without cover information.

Parameters

This provider's behavior is primarily controlled by its own settings.

  • Layer Height: The vertical distance between floors or layers. This should be set to match the typical floor height in your level architecture.
  • Layers Above: The number of floors to check above the agent's current position.
  • Layers Below: The number of floors to check below the agent's current position.
  • Samples Per Ring: The number of points to generate in each search ring on a given layer.
  • Rings: The number of concentric search rings to generate on each layer.
  • Search Radius (from TacticalAISettings): Controls the radius of the search rings on each layer.
  • NavMesh Area Mask (from TacticalAISettings): Ensures the spots are found on a valid NavMesh area.

Use Cases

  • Multi-Story Buildings: The primary use case. It allows an AI on the ground floor to consider hiding on the second floor, and vice-versa.
  • Environments with Verticality: Excellent for levels with bridges, underpasses, catwalks, large staircases, or natural cliffs.
  • Smarter Tactical Positioning: Encourages AI to use height to its advantage, creating more dynamic and challenging combat or stealth encounters.

Performance

The performance cost is directly related to the number of points it generates. The total number of candidates is (Layers Above + Layers Below) * Rings * Samples Per Ring. For each of these potential points, it performs a NavMesh.SamplePosition and a Physics.Raycast.

While very effective, it can become moderately expensive if the settings are high. It's recommended to balance the number of layers and samples with your performance budget. It can be a great addition to a high-quality LOD setting for when players are close.

Provider: Voxel Grid

VoxelGridProviderSO is a unique provider that finds potential hiding spots by sampling points in a 3D grid (a "voxel grid") around the agent and checking if these points are located inside solid geometry.

How It Works

This provider creates a uniform 3D grid of points centered on the agent's position. The size of this grid is determined by the Search Radius. For each point in the grid, it performs a Physics.OverlapBox check.

If this check returns any colliders on the Occluder Mask layer, it means the point is inside or touching a piece of geometry. That point is then added to the list of candidate hiding spots.

Unlike other providers that find spots behind or near objects, this one is specifically designed to find spots within them. This provider does not generate a cover normal.

Important Note: This provider does not check for NavMesh reachability. It is intended to be used with scorers like the ReachabilityScorerSO to filter out any points that the AI cannot actually get to.

Parameters

  • Resolution: This is a key setting on the provider asset. It defines the number of points to sample along each of the three axes (X, Y, Z). The total number of sample points is resolution * resolution * resolution. Be careful with this value, as it has a major impact on performance.
  • Collision Check Size: The size of the small box used for the OverlapBox check at each grid point. A larger value is more likely to detect nearby geometry but is less precise.
  • Search Radius (from TacticalAISettings): Defines the overall size of the 3D grid. The grid will be a cube with sides of length Search Radius * 2.
  • Occluder Mask (from TacticalAISettings): The physics layer mask that determines which objects are considered valid geometry to hide inside.

Use Cases

  • Hiding in "Soft" Cover: This is the perfect provider for finding spots inside non-solid or semi-solid objects like thick bushes, tall grass, or magical concealment zones.
  • Complex Geometry: Can be useful for finding spots inside unusual or complex meshes that don't have a clear "behind," such as a hollowed-out log or a small cave opening.

Performance

This is potentially the most performance-intensive provider in the entire system. The number of physics checks it performs is resolution³.

  • resolution = 8 (default) -> 8 * 8 * 8 = 512 physics checks.
  • resolution = 10 -> 10 * 10 * 10 = 1000 physics checks.
  • resolution = 16 -> 16 * 16 * 16 = 4096 physics checks.

It is critical to keep the resolution as low as possible. This provider should be used sparingly and only when its specific functionality is required. For general-purpose cover finding, providers like BehindObstacleProviderSO or CoverNodeProviderSO are far more performant. Always profile your game when using this provider.

Provider: Advance Cover

AdvanceCoverProviderSO is an offensive provider designed to generate hiding or cover points that facilitate forward movement towards a specific target or direction. It is the core component for "leapfrogging" or aggressive territory claiming behaviors.

How It Works

This provider calculates a "Forward" vector using the following priority logic:

1. Direction to Last Known Enemy Position: If the AI remembers where the player was, it prioritizes moving towards that memory.

2. Agent Forward: If no target data exists, it uses the agent's current forward facing direction.

Once the direction is established, the provider generates candidate points within a defined cone (the Search Angle) along that vector. Unlike simple movement providers, this system explicitly checks for local cover availability:

1. Cone Sampling: It casts random rays within the search angle.

2. Distance Randomization: It selects a point between 50% and 100% of the Advance Distance. This ensures the AI makes significant progress and doesn't pick a spot just 1 meter away.

3. Obstacle Validation: After sampling a valid NavMesh position, it performs a Physics.CheckSphere at that location. It only accepts the point if an obstacle is detected nearby (simulating "wall hugging" logic). This ensures the agent moves *to cover*, not just into the open.

Parameters

  • Advance Distance: The maximum distance forward the provider will search. The AI will try to move at least 50% of this distance.
  • Search Angle: The width of the cone in degrees. A narrower angle forces a direct approach; a wider angle allows for diagonal advancement.

Use Cases

  • Closing the Gap: Ideal for melee units or shotgun wielders who need to move safely from cover to cover to get into effective range.
  • Leapfrogging: When used by a squad, agents can take turns suppressing the enemy while others use this provider to advance to the next barricade.
  • Aggressive Patrols: Allows an AI to push through a level while sticking to walls and obstacles rather than walking down the center of a corridor.

Performance

Moderate. While the math for cone generation is cheap, this provider performs a Physics.CheckSphere for every generated candidate to ensure cover exists at the destination. While efficient for standard candidate counts (e.g., 10-20), setting the Candidates Per Provider count extremely high in your LOD settings may impact performance.

Provider: Choke Point Finder

ChokePointProviderSO is a tactical provider designed to identify constricted areas in the environment, such as hallways, bridges, doorways, or narrow alleys. It is particularly useful for setting up ambushes or identifying defensible bottlenecks where enemy movement is restricted.

How It Works

This provider scans the local environment to find points where the geometry constricts the NavMesh on opposing sides.

1. Random Sampling: It first generates random points on the NavMesh within the Search Radius.

2. Constriction Check: For each valid point, it performs a "star pattern" raycast check:

  • It casts rays to the Left and Right (X-axis relative to world).
  • It casts rays Forward and Backward (Z-axis relative to world).

3. Validation: A point is considered a "Choke Point" if it detects obstacles on *both* opposing sides (e.g., Left AND Right or Forward AND Back) within the defined Max Passage Width.

4. Cover Normal: If a choke point is found, the cover normal is set to Vector3.up (or generic), as the tactical value comes from the constriction itself rather than a specific cover face.

Parameters

  • Max Passage Width: The maximum width of a passage to be considered a choke point. Passages wider than this value will be ignored.
  • Wall Scan Range: The distance rays are cast to detect walls, effectively derived from the passage width limit.

Use Cases

  • Ambushes: AI can identify choke points to place traps or wait for the player to pass through.
  • Defensive Holds: A heavy unit might position itself in a narrow doorway to block player progress.
  • Grenade Targets: AI can use this provider to find narrow areas to throw grenades into, knowing the player has limited movement options.

Performance

Moderate. This provider performs 4 raycasts for every candidate point generated. While not as heavy as a full 360-degree scan, high Candidates Per Provider counts can add up. It is recommended to use this with a reasonable Search Radius.

Provider: Corner Edges

CornerEdgeProviderSO is a high-fidelity "AAA" tactical provider that scans the environment to find the exact vertical edges of walls and obstacles. This allows AI agents to perform "Tactical Leaning" maneuvers, peeking around corners without fully exposing their bodies.

How It Works

This provider performs a high-resolution angular sweep around the agent to detect geometric discontinuities.

1. Radial Scan: It casts a series of rays in a 360-degree circle around the agent at chest height.

2. Edge Detection: It identifies an edge when:

  • A ray hits an object, but the *next* ray does not (Transition from Hit -> Miss).
  • A ray does not hit, but the *next* ray does (Transition from Miss -> Hit).
  • The depth (distance) between two adjacent hits jumps significantly (e.g., a gap between two buildings or objects).

3. Corner Placement: Once an edge is detected, it calculates a position slightly offset from the wall normal using the Corner Offset parameter. This places the candidate point just "behind" or "around" the corner.

4. Filtering: It uses the NavMesh to ensure the point is walkable and applies a distance check to prevent stacking multiple points on the same corner.

Parameters

  • Scan Resolution: The number of rays to cast in the 360-degree circle (e.g., 64). Higher values provide more accurate corner detection but cost more performance.
  • Scan Range: The maximum distance to check for walls.
  • Corner Offset: How far the candidate point should be placed from the actual geometry corner (e.g., 0.6 meters behind the edge).
  • Edge Depth Sensitivity: The distance difference required between two adjacent rays to consider them as hitting different objects.

Use Cases

  • Tactical Peeking: The primary use case. AI moves to the edge, leans out to shoot, and leans back in.
  • Urban Combat: Essential for city environments with many building corners and alleyways.
  • Cover-to-Cover: Allows AI to move from one building edge to another while minimizing exposure time.

Performance

Heavy. This provider casts a large number of rays (Scan Resolution) every time it runs. It is recommended to use this provider selectively (e.g., only when near combat) or with a lower frequency/LOD setting compared to simpler providers.

Provider: Flank Points

FlankPointProviderSO is a tactical provider that generates candidate positions specifically located at the sides (flanks) or rear of a target. It actively ignores positions directly in front of the target.

How It Works

This provider requires a target (either a currently visible player or a LastKnownPlayerPosition). If no target is available, it generates no candidates.

1. Target Orientation: It determines the forward vector of the target. If the target is only a memory (position), it estimates the orientation based on the vector from the AI to that position.

2. Arc Calculation: It calculates an angle offset based on the Min Flank Angle. For example, if set to 90 degrees, the search starts directly to the target's right and left.

3. Spread: It adds a random variation within the defined Arc Width.

4. Alternating Sides: The provider alternates between generating a point on the Left Flank and the Right Flank to ensure a balanced set of options.

5. NavMesh Validation: It projects these calculated points onto the NavMesh to ensure they are walkable.

Parameters

  • Min Flank Angle: The starting angle from the target's forward vector.
  • 90 = Starts at the immediate side (Perpendicular).
  • 135 = Starts at the rear-diagonal.
  • 180 = Directly behind the target.
  • Arc Width: How much variation is allowed from the minimum angle.
  • Flank Distance: The fixed distance from the target where the points will be generated.

Use Cases

  • Ambushing: Used to position stealth units behind a player.
  • Breaking Stalemates: If a player is hunkered down behind cover, this provider forces the AI to move to a position where the player's cover is ineffective.
  • Encirclement: When combined with other providers, this ensures that not all AI agents stack up in front of the player.

Performance

Very Good. This provider relies on simple vector mathematics and standard NavMesh.SamplePosition calls. It is very lightweight and can be run frequently without performance concerns.

Provider: Pincer Formation

PincerFormationProviderSO is a cooperative squad provider. It generates candidate positions based on the location of *other* allies, naturally creating a surrounding "net" or pincer movement around the target without complex communication protocols.

How It Works

This provider requires both a target and active allies (defined in the TacticalAgent component).

1. Ally Analysis: It iterates through all known allies and calculates the vector from the Target to each Ally.

2. Average Direction: It calculates the average direction from which the squad is currently engaging the target.

3. Inversion: It inverts this average direction. This vector points to the "empty" side of the battlefield—the area least occupied by teammates.

4. Formation Generation: It generates candidate points in a 60-degree arc around this "empty" vector at a specific Engagement Distance.

By picking points generated by this provider, the agent naturally fills the gaps in the squad's formation, leading to the target being surrounded.

Parameters

  • Engagement Distance: The distance from the target where the agent will attempt to hold formation.

Use Cases

  • Boss Battles: Ensures that minions or adds spread out evenly around the player rather than clustering in one spot (which makes them vulnerable to Area of Effect attacks).
  • Squad Tactics: Useful for military AI to perform "Hammer and Anvil" tactics, where one group holds the front (using OppositePlayersProvider or AdvanceCoverProvider) and another group uses PincerFormationProvider to hit the exposed side.
  • Dynamic Spacing: Automatically adjusts if allies die or move; the "average direction" shifts, causing the pincer point to rotate dynamically.

Performance

Good. The cost is proportional to the number of active allies (linear complexity O(N)), followed by standard NavMesh sampling. For typical squad sizes (3-10 agents), the calculation overhead is negligible.

Provider: Room Clearance (Pie Slicing)

RoomClearanceProviderSO is designed for Close Quarters Battle (CQB) scenarios. It generates a "fan" of points around a corner, allowing an AI to methodically "slice the pie"—gradually clearing a room from the outside before entering.

How It Works

This provider combines edge detection with arc generation.

1. Corner Scan: Similar to CornerEdgeProvider, it scans for depth discontinuities to identify corners within the Scan Range using a radial raycast sweep.

2. Arc Generation: Once a corner is found, instead of placing a single point, it generates a series of points in a semi-circle (arc) around that corner.

3. Outward Facing: The arc is oriented based on the corner's normal, ensuring the points fan out into the open space/room being cleared.

4. Visibility Check: It performs a Linecast from the candidate point back to the corner to ensure the AI actually has a line of sight to the edge it is supposed to be clearing (prevents points spawning inside walls).

Parameters

  • Scan Range: The distance to scan for corners.
  • Ray Density: The number of rays used to find the initial corners.
  • Stand Off Distance: The distance from the corner where the AI will stand while clearing.
  • Points Per Corner: How many points to generate in the "fan" for each detected corner.
  • Arc Angle: The total angle of the fan (e.g., 90 degrees).

Use Cases

  • Breach and Clear: AI stacks up on a door, then uses these points to check the room's corners methodically.
  • Cautious Advance: AI moves slowly around a blind corner, checking for threats at each step of the arc.
  • SWAT / Tactical Shooters: Essential for realistic police or special forces behavior.

Performance

Heavy. Like the Corner Edge provider, this relies on a radial scan plus additional calculations and linecasts for the arc generation. Use strictly for tactical phases (e.g., when IsThreatened is false but the AI is in "Search" mode).

Provider: Squad Formation

SquadFormationProviderSO is a specialized provider that generates candidate points based on a strict tactical formation relative to a Squad Leader. It relies on the SquadCoordinator system to determine each agent's specific slot in the formation.

How It Works

1. Leader Identification: It queries the SquadCoordinator to find the leader of the agent's current squad. If the agent *is* the leader, or no leader exists, it runs the specified Fallback Provider.

2. Slot Calculation: It determines the agent's index in the squad (e.g., Member 1, Member 2) and calculates the ideal local position offset based on the selected Formation Shape (Wedge, Line, File, etc.).

3. World Transformation: It transforms this local offset into world space relative to the Leader's position and orientation.

  • *Note:* It can be configured to use either the Leader's body rotation or their velocity vector for orientation.

4. Jitter & Sampling: It generates points around this ideal "slot" with a small amount of random jitter (Radius ~2.0f). This ensures that if the perfect formation spot is inside a wall, the AI can still find a valid NavMesh position nearby.

Parameters

  • Shape: The type of formation to use (Wedge, Line, File, Diamond, Vee).
  • Spacing: The distance between agents in the formation.
  • Wedge Angle: The angle of the formation (applicable for Wedge/Vee shapes).
  • Use Body Orientation: If true, the formation rotates with the leader's body transform. If false, it rotates with their movement direction (velocity).
  • Fallback Provider: The provider to use if the agent is the leader or not in a squad (usually a standard cover provider like CoverProviderSO).

Use Cases

  • Military Squads: Moving in a strict Wedge or File formation while patrolling.
  • Convoy Protection: Agents moving in a Diamond formation around a VIP.
  • Organized Assault: An entire squad advancing in a Line formation to maximize firepower.

Performance

Excellent. The math for calculating formation offsets is trivial. The only cost is the standard NavMesh sampling for the candidate points. This is one of the cheapest providers available.

Provider: Vantage Point

VantagePointProviderSO is designed to find positions that offer a height advantage, such as balconies, rooftops, or hilltops. It is essential for "Hunting" behaviors where the AI wants to acquire targets from a superior position.

How It Works

This provider searches for NavMesh positions that are significantly higher than the agent's current location.

1. Biased Sampling: It generates random search points but specifically biases the Y-coordinate upwards by adding a random value between Min Height Advantage and half the range.

2. Height Check: It samples the NavMesh at these elevated coordinates. If a valid point is found, it verifies that the point's Y-position is indeed higher than the agent's current Y-position by the required amount.

3. View Check: To ensure the high ground is actually useful (and not just a high shelf inside a closet), it casts a ray forward/down from the point. If the ray hits nothing for a short distance, it assumes the point has a clear view overlooking an area.

Parameters

  • Min Height Advantage: The minimum height difference required above the agent's current Y position for a point to be considered a vantage point.

Use Cases

  • Snipers: AI snipers will naturally seek out rooftops or towers using this provider.
  • Ambushers: Melee units might wait on a ledge to drop down on the player.
  • Scouting: A squad leader might move to high ground to get a better view of the battlefield.

Performance

Good. It uses standard NavMesh sampling with a simple height check and one raycast per candidate. It is efficient enough for general use.

Scorers

Scorers are specialized ScriptableObjects that evaluate the hiding spots found by the providers. Each scorer assigns a score based on a specific criterion, such as distance from the player, visibility, or path safety. The final score for a spot is a weighted average of all active scorers.

Scorer: Ambush Potential

AmbushScorerSO is a sophisticated scorer that evaluates hiding spots based on their tactical potential for an ambush. It rewards spots that are concealed from the player's current position but maintain a clear line of sight to a strategic location.

How It Works

This scorer operates on a simple but powerful principle for setting up an ambush: hide from where the player IS, but watch where the player WAS.

It checks three conditions in order:

  1. Is the spot hidden? It first uses the IVisibilityService to check if the candidate spot is visible to any player from their current, real-time position. If the AI can be seen, the spot is immediately disqualified and gets a score of 0.
  2. Can the spot see the target? If the spot is hidden, it then determines an "ambush target." This is the player's last known position (LKP) if available, otherwise it falls back to the closest player's current position. It then performs a raycast to see if there is a clear line of sight from the candidate spot to this ambush target. If the view is obstructed, the spot is disqualified with a score of 0.
  3. Is it at the right distance? If the spot is both hidden and has a clear view of the target, it receives a score based on how close it is to the Optimal Ambush Distance. The closer to this ideal distance, the higher the score (maxing out at 1.0).

This logic creates an intelligent behavior where the AI will try to find a flanking position or overlook that allows it to surprise the player.

Parameters

  • Weight: As with all scorers, this determines how much influence the AmbushScorer has on the final decision. A high weight means the AI will strongly prioritize setting up ambushes.
  • Optimal Ambush Distance: This float value, set on the scorer asset, defines the ideal range in meters for the AI to be from the ambush target point.

Use Cases

  • Lying in Wait: The primary use case. Create AI that hides around a corner and waits for the player to walk past the location where they were last seen.
  • Tactical Overwatch: Excellent for snipers or other ranged AI. They will find concealed positions that have a good vantage point over key areas where the player is likely to be.
  • Smart Repositioning: When an AI loses sight of the player, this scorer helps it choose a new hiding spot that anticipates the player's movements, rather than just running away.

Performance and Dependencies

This is a moderately expensive scorer. For each candidate spot, it performs:

  • A visibility check via the IVisibilityService (which often involves its own raycasts).
  • An additional Physics.Raycast to check the line of sight to the ambush target.

Because of this, its performance cost is higher than simple scorers like DistanceScorerSO. It's highly effective but should be used when its specific tactical behavior is desired. It also requires the TacticalAgent to have seen a player to generate a playerLastKnownPosition to be maximally effective.

Scorer: Cover Height

CoverHeightScorerSO evaluates a candidate spot based on the height of the cover in front of it, ensuring the AI does not try to hide behind objects that are too short to conceal it properly.

How It Works

This scorer adds a layer of intelligence to cover selection by checking not just if there is cover, but if that cover is tall enough. It operates only on candidates that have a coverNormal provided by their generator (like AutomaticCoverProviderSO or BehindObstacleProviderSO).

For a given candidate spot, the scorer simulates the agent's head position. It then casts a short ray from just in front of the agent's head directly towards the primary threat (the player's last known position or current position).

  • If the ray hits an occluder, it means there is a solid object at head-level between the agent and the threat. The spot receives a high score (1.0), indicating good, tall cover.
  • If the ray does not hit anything, it implies the cover is too low (e.g., a waist-high crate), and the agent's head would be exposed. The spot receives a low score (0.0).
  • If a candidate has no coverNormal, the scorer returns a neutral score (0.5) so as not to penalize providers that don't generate this data.

Parameters

  • Check Offset: How far in front of the agent's head to start the raycast. This should be a small value to ensure the check is close to the agent.
  • Check Distance: The maximum length of the raycast. This should be short, as it's only meant to verify the immediate cover, not see across the map.

Use Cases

Realistic Cover Selection: This is a crucial scorer for preventing silly AI behavior, like crouching behind a small rock or a low railing while thinking it's completely hidden.

Enhancing Tactical Realism: In shooters or tactical games, this ensures that AI agents prioritize cover that offers full protection, leading to more believable and challenging encounters.

Performance

The performance impact of this scorer is very low. It performs a single, very short Physics.Raycast for each candidate that has cover information. In most scenarios, its cost is negligible.

Scorer: Crossfire

CrossfireScorerSO evaluates hiding spots based on their potential to create a tactical crossfire with allied units. It rewards spots that provide a flanking angle on the player's position relative to the positions of other AI agents.

How It Works

This scorer promotes intelligent squad behavior by moving beyond simple grouping. Instead of just huddling together, it encourages agents to spread out and attack from multiple angles.

For a given candidate spot, the scorer does the following:

  1. It identifies the primary threat location (the player's last known or current position).
  2. It iterates through all of the agent's allies.
  3. For each ally, it calculates the angle formed at the player's position between the ally and the candidate spot (ally -> player -> candidate).
  4. The scorer finds the maximum angle created among all allies.
  5. The final score is based on how close this maximum angle is to the Optimal Angle defined in the asset. An angle of 90 degrees (a perfect "L" shape flank) would receive the highest score, while positions that are directly in line with an ally receive a low score.

Setup

  • Ensure your TacticalAgent components have their allies list populated correctly.
  • Create an instance of the CrossfireScorerSO asset.
  • In the asset's Inspector, configure the Optimal Angle (90 is a good default) and Minimum Angle (e.g., 20, to ignore insignificant angles).
  • Add this scorer asset to the Scorers list in your TacticalAISettingsSO and give it a moderate weight (e.g., 1.0 to 2.0). Its influence should be a significant factor in repositioning decisions.

Use Cases

  • Advanced Squad Tactics: The primary use case. Transform a group of AI from a simple mob into a coordinated squad that actively tries to flank and suppress the player.
  • Making Combat More Challenging: A crossfire is a difficult situation for a player to deal with. This scorer directly increases the tactical challenge of combat encounters.
  • Dynamic Encounters: The AI will dynamically create flanking maneuvers based on the current positions of all units, leading to less predictable and more engaging fights.

Scorer: Darkness

DarknessScorerSO is a very simple scorer that evaluates hiding spots based on the overall ambient light level of the scene. It rewards spots in environments that are generally darker.

How It Works

This scorer operates on a very simple principle: it reads the scene's global ambient light intensity value from RenderSettings.ambientIntensity.

  • If the ambient intensity is high (a bright scene), it returns a low score.
  • If the ambient intensity is low (a dark scene), it returns a high score.

The score is calculated as 1.0f - ambientIntensity, clamped between 0 and 1.

Important Note: This is a global scorer. At any given moment, it will return the exact same score for every single candidate spot regardless of their position. It does not check for dynamic lights or shadows cast by objects. Its purpose is to give a general "bias" towards darkness for the AI's decision-making process.

Parameters

  • Weight: Determines how much the AI should care about the overall darkness of the scene when choosing a spot. If you want AI to strongly prefer hiding when the level is dark, give this a high weight.

Use Cases

  • Time of Day Behavior: This scorer is perfect for influencing AI behavior based on a day/night cycle. If you change the ambient intensity at runtime to simulate the transition from day to night, this scorer will automatically make your AI more likely to hide during the night.
  • General "Creature of the Dark" AI: For AI that should be more active or more likely to take cover in dark levels (e.g., caves, nighttime scenes), this scorer provides a simple and extremely performant way to achieve that.

Performance

The performance of this scorer is excellent. It is one of the fastest scorers available because it only involves reading a single float value from the render settings. Its impact on performance is negligible, making it safe to use in any configuration.

Scorer: Defensive Position Bonus

DefensivePositionScorerSO is a powerful scorer that provides "inertia" or a defensive bonus. It evaluates the agent's current position to determine if it's safe, making the AI less likely to abandon a good spot and more likely to flee a compromised one.

How It Works

This scorer is unique because it only cares about one specific candidate: the one that represents the agent's current location. For all other candidate spots, it returns a score of 0.

When it evaluates the agent's current position, it performs a critical check:

  1. Is the agent currently visible to any player? It uses the IVisibilityService to check if the agent can be seen from its current spot.

The result of this check leads to one of two extreme outcomes:

  • If VISIBLE: The agent's cover is blown. The scorer returns float.NegativeInfinity. This is a powerful penalty that effectively disqualifies the current position, forcing the AI to choose a different hiding spot. It creates a strong "get to cover!" instinct.
  • If NOT VISIBLE: The agent is safe. The scorer returns the small, positive inertiaBonus. This gives a slight advantage to staying put, ensuring the AI won't move to a new spot unless it's significantly better than the one it's already in. This prevents fidgety, unnecessary movement.

Parameters

  • Weight: Determines the influence of this scorer. Because it can return NegativeInfinity, its penalty effect is absolute regardless of weight. The weight primarily affects how influential the inertiaBonus is when compared to scores from other active scorers.
  • Acceptance Radius: A small radius to determine if a candidate point is close enough to be considered the agent's current position.
  • Inertia Bonus: The small score bonus (e.g., 0.1 or 0.2) applied if the agent is currently safe. This value should be kept relatively low.
  • See The Warning Section Below

Use Cases

  • Creating Stable AI: This is the primary solution for preventing AI from constantly shuffling between two or more hiding spots that have very similar scores. The inertia bonus makes them "prefer" to stay still.
  • Intelligent Cover Response: This scorer is essential for creating AI that reacts believably to being discovered. The moment it becomes visible, its top priority will be to move to a new, safer location.
  • Establishing a Defensive Baseline: It should be included in most configurations to create a base level of intelligent self-preservation.

Note

  • Warning: Chose the parameters value and weight in the Tactical AI Settings for this scorer very carefully because it's very sencitive one and a wrong weight for this scorer may cause the agent will never move. so try to give it's weight as low as possible like 0.1 or less than 0.1

Performance and Dependencies

This scorer's performance is excellent. It only performs its main logic (the visibility check) for a single candidate spot per evaluation cycle.

  • It is highly dependent on a correctly configured IVisibilityService.
  • It is designed to be used with the CurrentPositionProviderSO, which ensures that the agent's current position is always included in the list of candidates to be evaluated. Without this provider, the DefensivePositionScorer will do nothing.

Scorer: Distance From Players

DistanceScorerSO is a fundamental scorer that evaluates hiding spots based on their distance from the nearest player. It encourages AI to move farther away from threats.

How It Works

This scorer calculates the distance from the candidate hiding spot to every player currently known to the system. It finds the shortest of these distances and uses that value to calculate a score.

The score is normalized by the Search Radius from your TacticalAISettings. The formula is essentially:

Score = (Distance to Nearest Player) / (Search Radius * normalizeByRadius)

This means a spot right next to a player will have a score near 0, while a spot at the edge of the search radius will have a score near 1.0. In short, farther away is better.

Negative Weight Trick

This scorer has a special interaction with negative weights. If you give it a negative weight in the TacticalAISettings (e.g., -1.0), you will invert its behavior. Instead of rewarding distance, it will reward proximity, encouraging the AI to find the closest possible (safe) hiding spot. This is useful for aggressive or hunter-style AI.

Parameters

  • Weight: Determines how much the AI prioritizes distance. A high positive weight creates cowardly AI. A high negative weight creates aggressive AI.
  • Normalize By Radius: This multiplier on the scorer asset adjusts the normalization.
    • 1.0 (default): The score scales linearly with the Search Radius. A spot at the search radius distance gets a score of 1.0.
    • < 1.0: The score will reach 1.0 before hitting the full search radius. For example, at 0.5, a spot at half the search radius already gets a score of 1.0.
    • > 1.0: The score will only approach 1.0 for distances beyond the search radius.

Use Cases

  • Cowardly AI: Use with a high positive weight. This is the primary scorer for AI that should run away from the player.
  • Ranged Units: Use with a moderate positive weight for archers or mages who need to maintain a safe distance.
  • Stealthy Hunters: Use with a high negative weight to make the AI try to sneak as close to the player as possible while remaining hidden.

Performance

The performance of this scorer is excellent. For each candidate spot, it performs a simple Vector3.Distance calculation for each player. This is a very fast operation, and the scorer has a negligible impact on performance.

Scorer: FOV Avoidance

FOVScorerSO is a critical scorer for stealth gameplay. It evaluates spots based on whether they fall within any player's field of view (FOV). It heavily penalizes spots that the player is actively looking towards.

How It Works

This scorer performs a simple but crucial binary check using the IVisibilityService:

  1. It asks the IVisibilityService: "Is this candidate spot inside any player's viewing cone AND visible from their eyes?"
  2. Based on the answer, it returns a score:
    • If YES: The spot is in the player's direct field of view. The scorer returns 0.0.
    • If NO: The spot is outside the player's direct field of view (either behind them, to the side, or obscured by an object). The scorer returns 1.0.

This creates a strong incentive for the AI to stay out of the player's forward-facing view cone, encouraging flanking and sneaking behaviors.

Distinguishing from VisibilityScorer

It's important to understand the difference between this scorer and the general VisibilityScorer:

  • VisibilityScorer: Checks for a clear line of sight from the player's eyes to the spot, regardless of which direction the player is facing. A spot directly behind the player would be considered "visible" if there are no walls in the way.
  • FOVScorer: Also checks for line of sight, but only if the spot is within the player's view cone angle. A spot directly behind the player will always get a perfect score of 1.0 from this scorer, because it's not in the FOV.

For true stealth gameplay, you typically use both scorers together. This tells the AI to:

  1. Strongly prefer spots outside the player's FOV (FOVScorer).
  2. Of those spots, prefer the ones that are also physically hidden behind objects (VisibilityScorer).

Parameters

  • Weight: Determines how strongly the AI will prioritize staying out of the player's view cone. For stealthy AI, this should usually have a very high weight.

Use Cases

  • Classic Stealth Mechanics: This is the cornerstone of any system where AI needs to avoid a player's vision cone (like in Metal Gear Solid or Dishonored).
  • Flanking Behavior: By heavily penalizing spots in front of the player, this scorer naturally encourages AI to move to the player's sides or back.
  • Creating "Blind Spots": This scorer is what makes hiding behind a player a valid and rewarding tactic for the AI.

Performance and Dependencies

The performance of this scorer is entirely dependent on the underlying implementation of IVisibilityService.InAnyPlayerFOVAndVisible. This check typically involves fast vector math (a dot product to check the angle) and potentially a Physics.Raycast to check for obstructions.

It is moderately expensive, more so than simple math-based scorers, but essential for this type of gameplay. It has a hard dependency on a correctly configured IVisibilityService.

Scorer: Hazard Avoidance

HazardAvoidanceScorerSO prevents the AI from choosing hiding spots inside known environmental dangers, such as fires, poison clouds, or the blast radius of a grenade.

How It Works

This system relies on Hazard components being placed on objects that represent a threat. Any GameObject with a Hazard component will register itself with a central HazardRegistry when it becomes active.

The HazardAvoidanceScorer queries this registry to get a list of all active hazards. For each candidate hiding spot, it checks the distance to every hazard.

If the candidate's position is within the defined radius of any Hazard object, the scorer returns float.NegativeInfinity. This instantly and absolutely disqualifies the spot, forcing the AI to look for cover elsewhere. This is a critical self-preservation behavior.

Setup

On any GameObject that represents a danger (e.g., a fire particle system, a grenade prefab), add the new Hazard component.

Adjust the Radius property on the Hazard component to match the area of effect of the danger. A red gizmo in the Scene view will help you visualize this area.

Create an instance of the HazardAvoidanceScorerSO asset in your project files.

Add this scorer asset to the Scorers list in your TacticalAISettingsSO. It is highly recommended to give this scorer a high weight (e.g., 5.0) to ensure its decisions are always prioritized.

Use Cases

  • Dynamic Dangers: Essential for making AI react to dynamic events like grenades, molotovs, or artillery strikes.
  • Persistent Hazards: Prevents AI from unintelligently pathing through fires, acid pools, or other static level hazards when looking for cover.
  • Enhanced Believability: Stops the AI from making immersion-breaking mistakes, making them feel more aware and intelligent.

Scorer: Height Difference

HeightDifferenceScorerSO scores a candidate based on its vertical distance from the agent, discouraging movement to different floors or extreme heights unless the spot is significantly better in other ways.

How It Works

This scorer introduces a preference for the AI to stay on its current vertical plane. It calculates the absolute difference in the Y-axis value between the agent's current position and the candidate spot's position.

The scoring is inversely proportional to this height difference:

A spot on the same level (height difference is zero) receives a perfect score of 1.0.

As the vertical distance increases, the score decreases linearly.

If the height difference exceeds the maxAcceptableHeightDifference, the score becomes 0.0.

This acts as a penalty for vertical movement, meaning a spot on another floor must be significantly better in other aspects (like visibility, safety, etc.) to overcome this penalty and be chosen.

Parameters

  • Max Acceptable Height Difference: The maximum vertical distance the AI will consider before the score drops to zero. This should typically be set to a value slightly greater than the height of a single floor in your level (e.g., 4-5 meters).

Use Cases

  • Controlling Vertical Movement: Its primary use is to prevent AI from erratically running up and down stairs or jumping off ledges just because a spot is slightly better. It promotes more deliberate and logical movement.
  • Improving Performance: By penalizing distant vertical spots, it can indirectly favor closer, more relevant hiding locations, potentially reducing the need for long and complex pathfinding calculations.
  • Defining Agent Behavior: You could give different AI types different settings. A nimble, spider-like robot might have a high maxAcceptableHeightDifference, while a heavy ground soldier would have a very low one.

Performance

This scorer is extremely fast. It only involves a few floating-point subtractions and divisions (Mathf.Abs). Its performance impact is virtually zero, making it a safe and effective scorer to add to any configuration.

Scorer: Objective Proximity

ObjectiveProximityScorerSO scores candidate spots based on their distance and relevance to a mission objective. This makes the AI "mission-aware," preventing it from abandoning a critical location it's supposed to be defending.

How It Works

This scorer relies on a new static service called ObjectiveService. Your game's own mission scripts are responsible for registering and unregistering objective locations with this service.

The ObjectiveProximityScorerSO asset is configured to care about a specific ObjectiveType (e.g., Defend, Attack).

When scoring a candidate, it finds the nearest objective of the matching type. The scoring logic then rewards proximity:

A spot very close to the objective receives a high score (1.0).

As the distance to the objective increases, the score decreases linearly.

If the distance exceeds the Max Distance setting on the scorer, the score becomes 0.0.

This creates a powerful incentive for the AI to choose cover that keeps it relevant to the mission, adding a crucial layer of tactical intelligence.

Setup

From your game logic scripts (e.g., a "King of the Hill" manager, a "Bomb Defusal" script), get a reference to the objective's position.

When the objective becomes active, call ObjectiveService.RegisterObjective(...), passing in its position and type.

When the objective is completed or moves, call ObjectiveService.UnregisterObjective(...) or ClearObjectives().

Create an instance of the ObjectiveProximityScorerSO asset.

Configure its Target Type, Optimal Distance, and Max Distance in the Inspector.

Add this scorer asset to the Scorers list in your TacticalAISettingsSO and give it an appropriate weight.

Use Cases

Defensive Scenarios: Essential for AI that needs to guard a flag, control point, or VIP. This scorer will force them to hide near the objective instead of running away.

Offensive Scenarios: By setting the type to Attack, you can encourage AI to select hiding spots that move them progressively closer to their target.

Dynamic Objectives: Works perfectly for objectives that move, as long as your game logic continually updates the objective's position in the ObjectiveService.

Scorer: Partial Visibility

PartialVisibilityScorerSO provides a more thorough visibility check than the standard VisibilityScorerSO. It ensures the agent's entire body is concealed by checking multiple points against player line of sight.

How It Works

While the basic VisibilityScorerSO might only check one or two points, PartialVisibilityScorerSO checks three distinct points on the agent's body relative to the candidate position:

  • Head Point: At the agent's full eye height.
  • Chest Point: At the agent's center of mass (e.g., 60% of eye height).
  • Feet Point: Near the ground (e.g., 0.2 meters up).

It then uses the IVisibilityService to check if any player has a direct, unobstructed line of sight to any of these three points.

If all three points are hidden from all players, the spot is considered fully concealed and receives a perfect score of 1.0.

If even one point is visible to any player, the spot is considered compromised and receives a score of 0.0.

This creates a strict, all-or-nothing evaluation of cover.

Use Cases

  • Creating Cautious AI: This is the ideal scorer for stealth games or for AI archetypes (like snipers or scouts) that prioritize complete concealment above all else.
  • Eliminating Exploits: It prevents situations where a player can see and shoot an AI's feet or head poking out from cover, even though the AI's center point is hidden.
  • High-Difficulty Settings: This can be used to make AI on higher difficulty levels much harder to spot and engage, as they will only choose locations that offer total body cover.

Performance

The performance cost is slightly higher than the basic VisibilityScorerSO because it makes up to three calls to AnyPlayerHasLineOfSight per candidate instead of one or two. However, because the underlying visibility checks are often optimized (especially in the asynchronous system), the impact is still relatively low and is a worthwhile trade-off for the increased fidelity.

Scorer: Path Safety

PathSafetyScorerSO is one of the most intelligent and important scorers for creating believable stealth AI. It evaluates the safety of the path an agent would take to reach a hiding spot, not just the destination itself. It heavily penalizes paths that would expose the agent to player view.

How It Works

For each candidate hiding spot, this scorer performs a detailed analysis of the journey from the agent's current position to that spot.

  1. Calculate Path: It first calculates the NavMeshPath to the candidate spot. If no complete path exists, the spot is unreachable and is disqualified with a score of NegativeInfinity.
  2. Sample Path Points: It gathers all the corners of the path. To ensure safety on long, straight sections, it also adds extra sample points in the middle of any path segment longer than the Long Segment Threshold.
  3. Check Visibility: It then iterates through all these sample points along the path. For each point, it uses the IVisibilityService to check if that point is visible to any player.
  4. Calculate Exposure: It determines the visibilityRatio—the percentage of sample points that were visible to a player.
  5. Apply Penalties: The scoring logic is applied in two stages for maximum control:
    • Hard Disqualification: If the visibilityRatio is greater than the Visibility Threshold, the path is considered too dangerous. The scorer returns NegativeInfinity, completely disqualifying it. This acts as a hard "do not cross" rule. (This threshold can be dynamically overridden per agent via the HidingContext for the "desperate retreat" feature).
    • Exponential Penalty: If the path is not disqualified, its score is calculated based on its safety (1.0 - visibilityRatio). This value is then raised to the power of the Penalty Exponent. Using an exponent greater than 1 means that even small amounts of exposure are heavily penalized, making the AI extremely cautious.

Parameters

  • Weight: Determines how much the AI prioritizes a safe path. For stealthy characters, this should have a very high weight.
  • Visibility Threshold: A hard limit (0.0 to 1.0) for how much of the path can be exposed. If the visible portion exceeds this, the path is invalid.
  • Penalty Exponent: Controls the scoring curve for paths that pass the threshold check. A value of 1 is a linear penalty. A value of 2 or higher makes the AI extremely averse to even minimal exposure.
  • Long Segment Threshold: The length of a straight path segment that will trigger an extra visibility check at its midpoint. This prevents AI from feeling safe running down a long, open hallway just because its corners are hidden.

Use Cases

  • Essential Stealth Behavior: This is a cornerstone scorer for any AI that needs to be sneaky. It's the difference between an AI that intelligently skirts cover and one that foolishly runs across an open room.
  • Creating Cautious AI: By tuning the exponent, you can create AI personalities ranging from slightly cautious to extremely risk-averse.
  • Preventing "Suicidal" Traverses: It stops the AI from choosing a theoretically perfect hiding spot if the only way to get there is through a field of fire.

Performance and Dependencies

This is one of the most performance-intensive scorers in the system. Its cost is significant and should be considered carefully. For every candidate spot, it must:

  1. Calculate a NavMesh path.
  2. Perform multiple visibility checks (which often involve raycasts) along the path's length.

The total cost scales with the number of candidate spots, the number of players, and the complexity (number of corners) of the potential paths.

  • Recommendation: Use this scorer when high-quality stealth is required, but be mindful of the performance cost. Consider reducing the number of candidate spots if this scorer is active.
  • Dependencies: It has hard dependencies on a valid NavMesh in the scene and a correctly configured IVisibilityService.

Scorer: Player Path Prediction

PlayerPathPredictionScorerSO is a highly intelligent scorer that allows an AI to ambush the player by predicting their future position based on their current movement velocity.

How It Works

This scorer elevates the AmbushScorer from reacting to the player's past position to anticipating their future actions. It leverages a new PlayerTrackerService to get the player's current velocity.

The scoring logic follows two critical ambush principles:

  • Stay Hidden Now: The candidate spot must be hidden from the player's current position. If the AI can be seen while moving into its ambush spot, the ambush fails. This check receives a score of 0.
  • See Them Later: The candidate spot must have a clear line of sight to the player's predicted future position. This position is calculated by projecting the player's current velocity forward in time by a set amount (Prediction Time).

If both conditions are met, the candidate is a perfect ambush spot and receives a high score (1.0). Otherwise, it receives a score of 0.0.

Setup

  • Crucial Step: In the TacticalAgent.cs script, you must add one line to the CreateHidingContext method: PlayerTrackerService.Update(players);. This is required to feed the tracker the data it needs. The updated script is provided below.
  1. Create an instance of the PlayerPathPredictionScorerSO asset.
  2. In the asset's Inspector, set the Prediction Time (e.g., 1.0 to 2.0 seconds, depending on how fast your players move).
  3. Add this scorer asset to the Scorers list in your TacticalAISettingsSO. It works well with a moderate to high weight.

Use Cases

  • Creating "Smart" AI: This is a hallmark of intelligent-feeling AI. It gives the impression that the AI is thinking ahead and outsmarting the player.
  • Effective Ambushes: Perfect for stealth games or for AI archetypes like assassins or trappers. The AI will correctly lead its target, hiding behind a corner and preparing to engage as the player sprints past.
  • Punishing Predictable Player Behavior: This scorer naturally punishes players who run in straight lines without checking corners, as the AI will be waiting for them.

Scorer: Proximity Penalty

ProximityPenaltyScorerSO scores a candidate based on its raw proximity to the nearest player. It creates a "danger zone" around players, heavily penalizing or disqualifying spots that are too close, regardless of visibility.

How It Works

This scorer enforces a critical self-preservation behavior by ensuring the AI maintains a safe distance from threats. It doesn't care about cover or line of sight; its only concern is raw distance.

  • Finds Nearest Threat: It first calculates the distance from the candidate hiding spot to every player. It only considers the distance to the closest player for its evaluation.
  • Disqualification Check: It checks if this distance is within the disqualificationRadius. If it is, the spot is considered suicidally close. The scorer returns float.NegativeInfinity, which instantly removes the spot from consideration, no matter how good other scorers think it is.
  • Penalty Calculation: If the spot is outside the disqualification zone but inside the larger dangerRadius, it is considered hazardous. The scorer calculates a penalty that scales linearly.

A spot right on the edge of the dangerRadius will get a score of 1.0 (no penalty).

A spot right on the edge of the disqualificationRadius will get a score of 0.0 (maximum penalty).

  • Safe Zone: If the spot is outside of the dangerRadius, this scorer considers it perfectly safe and gives it a full score of 1.0.

Parameters

  • Disqualification Radius: The absolute "no-go" zone. Any candidate spot within this distance of a player will be instantly rejected. This should be a small value representing the agent's immediate personal space (e.g., 2-3 meters).
  • Danger Radius: A larger warning zone. Spots within this radius will be penalized. This should be a larger value representing the distance at which the agent starts to feel threatened (e.g., 7-10 meters).

Use Cases

  • Essential Self-Preservation: This is a highly recommended scorer for nearly every configuration. It prevents the AI from making the naive mistake of hiding right next to an enemy.
  • Enforcing Engagement Ranges: It stops the AI from retreating to a "hidden" spot that is still within melee or shotgun range of a player.
  • Creating Skittish AI: By using a large dangerRadius, you can create AI characters that are more cautious and prefer to keep their distance.

Performance

This scorer is extremely high-performance. It involves a simple loop and distance calculations (Vector3.SqrMagnitude). Its impact on performance is negligible, and the behavioral improvement it provides is immense.

Scorer: Reachability

ReachabilityScorerSO is a fundamental utility scorer that evaluates spots based on whether they are reachable by the agent on the NavMesh and within a maximum travel distance.

How It Works

This scorer acts as a critical filter to ensure the AI only considers valid, reachable hiding spots. For each candidate spot, it performs the following checks:

  1. Path Calculation: It attempts to calculate a complete NavMeshPath from the agent's current position to the candidate spot.
  2. Path Validity: It checks the status of the calculated path. If the status is not PathComplete (meaning the spot is on a separate NavMesh island or is blocked), the spot is considered unreachable.
  3. Path Distance: If the path is valid, it calculates the total length of the path by summing the distances between all its corners. It then compares this length to the Path Max Distance defined in the TacticalAISettings.

The scoring logic is as follows:

  • If the path is invalid, incomplete, or longer than Path Max Distance: The scorer returns float.NegativeInfinity. This is a hard penalty that completely disqualifies the candidate spot, preventing the AI from ever choosing it.
  • If the path is valid and within the distance limit: It returns a score between 0 and 1. The score is higher for shorter paths, calculated as 1.0 - (pathLength / pathMaxDistance). This gives a natural preference for closer spots.

Parameters

  • Weight: Determines how much the AI should prioritize spots with shorter path distances.
  • Path Max Distance (from TacticalAISettings): This is the crucial setting for this scorer. It defines the maximum travel distance (in meters) that the AI is allowed to consider. Any spot requiring a longer path will be disqualified.
  • NavMesh Area Mask (from TacticalAISettings): Used to constrain the path calculation to specific NavMesh area types.

Use Cases

  • Filtering for "Dumb" Providers: This scorer is essential when using providers that don't perform their own NavMesh checks, such as VoxelGridProviderSO. It acts as the filter that removes all the unreachable points generated by such providers.
  • Enforcing a "Leash" on AI: By setting Path Max Distance, you can effectively "leash" your AI, preventing it from choosing a hiding spot that is technically within its search radius but would require a very long and winding path to reach. This keeps the AI's movement tactically relevant.
  • Performance Optimization: By running this scorer early (by placing it high in the scorer list), you can quickly disqualify many invalid or distant spots. This saves more performance-intensive scorers (like PathSafetyScorerSO) from having to run their complex logic on spots the AI could never reach anyway.

Performance and Dependencies

This scorer is moderately expensive. NavMesh.CalculatePath is a non-trivial operation and its cost will add up when run on many candidate spots. It is more expensive than simple math-based scorers but generally cheaper than those involving multiple physics raycasts.

  • Dependencies: It has a hard dependency on a valid NavMesh being present and correctly baked in the scene. Without a NavMesh, it will fail to find any valid paths.

Scorer: Team Proximity

TeamProximityScorerSO is a scorer that encourages agents to hide near their allies, promoting group cohesion and squad-based behavior.

How It Works

This scorer evaluates a candidate hiding spot based on its distance to the nearest friendly unit.

  1. Find Nearest Ally: It iterates through the list of allies provided in the HidingContext (sourced from the TacticalAgent's allies list) and finds the one closest to the candidate spot.
  2. Compare to Optimal Distance: It then compares this closest distance to the Optimal Ally Distance defined on the scorer asset.
  3. Calculate Score: The score is highest (1.0) when the spot is exactly the optimal distance from the nearest ally. The score decreases as the spot gets either closer to or farther away from the ally than this ideal distance.

If the agent has no allies, this scorer returns a neutral score of 0.5, having no effect on the decision. This logic encourages agents to "stick together" but not "stand on top of each other."

Parameters

  • Weight: Determines how strongly the AI will prioritize staying near its teammates. A high weight will cause agents to form tight groups.
  • Optimal Ally Distance: This float value on the scorer asset defines the ideal distance (in meters) the agent should maintain from its closest ally.

Use Cases

  • Squad Behavior: This is the primary use case. It makes soldier AI maintain formations and hide as a unit, rather than scattering across the map.
  • Pack Animals: Perfect for creating pack-based AI like wolves or raptors that hunt and move together.
  • Support Roles: Can be used to make support units (like medics or officers) stay close to the combat units they are supposed to be assisting.
  • Discouraging Isolation: Prevents a single AI from wandering off on its own, which can make the overall group feel more intelligent and coordinated.

Performance and Dependencies

The performance of this scorer is excellent. For each candidate spot, it performs a series of fast distance calculations. Its impact on performance is negligible.

  • Dependency: This scorer's functionality is entirely dependent on the allies list on the TacticalAgent component being correctly populated by your own controller scripts. If the list is empty, it will have no effect.

Scorer: Threat Avoidance

ThreatScorerSO is an advanced scorer that makes AI aware of and avoid areas of known danger. It evaluates spots based on their proximity to a dynamic, global "threat map."

How It Works

This scorer relies on an external system, the ThreatMapService, which is assumed to hold a list of active "threat points." These points could represent locations where a player was recently seen, where an explosion just occurred, or an area under suppressive fire.

For each candidate hiding spot, the scorer:

  1. Finds the Closest Threat: It iterates through all active points in the ThreatMapService and finds the one closest to the candidate spot.
  2. Calculates Score Based on Distance: The score is determined by how far the candidate spot is from this closest threat point.
    • A spot right on top of a threat point gets a score of 0.0.
    • A spot that is farther away than the Safe Distance gets a score of 1.0.
    • The score scales linearly in between.

In short, this scorer makes AI want to move as far away from known danger zones as possible. If there are no active threats on the map, it returns a perfect score of 1.0.

Parameters

  • Weight: Determines how strongly the AI will avoid known threats. For most AI, this should have a high weight to encourage self-preservation.
  • Safe Distance: A float value on the scorer asset that defines the radius of influence for a threat point. Once a spot is farther than this distance from a threat, the threat no longer affects its score.

Use Cases

  • Creating Persistent Awareness: This is the primary use case. It allows AI to "remember" that a certain area is dangerous, even after the player has left. If a player shoots from a window, you can add a threat point there, and AI will avoid that window for a period of time.
  • Responding to Grenades and Explosions: When a grenade is thrown, you can add a temporary threat point at its location. This scorer will then naturally make all nearby AI flee from the blast radius.
  • Suppressing Fire: An AI under fire can add a threat point at the source of the shots, encouraging it and its allies to find cover that is not exposed to that direction or location.

Performance and Dependencies

The performance of this scorer is very good. Its cost is proportional to the number of active threat points in the ThreatMapService, as it involves a series of distance calculations.

  • Crucial Dependency: This scorer is completely dependent on the ThreatMapService. It does nothing on its own. The service is automatically updated by the TacticalAgent when it creates a HidingContext, but you can also add your own threats to it from other game systems (e.g., an explosion manager).

Scorer: Visibility

VisibilityScorerSO is one of the most essential scorers in the system. It evaluates hiding spots with a simple binary question: "Can a player see this spot?" It heavily penalizes any spot that is not completely concealed from all players.

How It Works

This scorer uses the IVisibilityService to determine if a candidate hiding spot is exposed. To make the check more robust and simulate the physical presence of an agent, it checks two separate points at the candidate location:

  1. A high point (simulating the agent's head).
  2. A low point (simulating the agent's center of mass).

It then asks the IVisibilityService, "Does any player have a direct, unobstructed line of sight to either of these points?"

The scoring is binary:

  • If YES: At least one player can see the spot. It is considered compromised and receives a score of 0.0.
  • If NO: The spot is fully concealed from all players. It receives a perfect score of 1.0.

This scorer does not care about the player's field of view (FOV). It only checks for physical line of sight. A spot directly behind a player is still considered "visible" by this scorer if there are no walls or obstacles in between. For FOV-based logic, see the FOVScorerSO.

Parameters

  • Weight: Determines how much the AI prioritizes being physically hidden. For almost all hiding behaviors, this should have a very high weight. It's a core component of what it means "to hide."
  • Player Eye Height (from TacticalAISettings): This global setting is used to calculate the positions of the high and low points for the visibility check.

Use Cases

  • Core Hiding Mechanic: This is a foundational scorer and should be included in nearly every TacticalAISettings configuration. It is the primary driver for making AI take cover behind walls, crates, and other obstacles.
  • Breaking Line of Sight: When an AI needs to escape, this scorer ensures it chooses a location that will actually break the player's line of sight.
  • Working with Other Scorers: It's often used as the primary "filter." Other scorers then rank the spots that this scorer has already deemed "safe." For example, you can combine it with the DistanceScorerSO to find a spot that is both hidden and far away.

Performance and Dependencies

The performance of this scorer is entirely dependent on the implementation of the IVisibilityService.AnyPlayerHasLineOfSight method. This check almost always involves one or more Physics.Raycast calls.

  • The cost scales with the number of players and the number of candidate spots.
  • Because it performs at least two visibility checks per candidate, it is a moderately expensive scorer.
  • It has a hard dependency on a correctly configured IVisibilityService. Without it, this scorer cannot function.

Scorer: Volumetric Cover

VolumetricCoverScorerSO scores candidate spots based on their concealment inside "soft cover" volumes like smoke clouds, dense fog, or magical darkness. This teaches the AI to use visual obstruction to its advantage.

How It Works

This system is designed to complement the standard visibility system, which only understands solid objects. It uses a VolumetricCover component, which should be placed on objects like smoke cloud prefabs. These components register themselves with a VolumetricCoverRegistry.

The VolumetricCoverScorer evaluates a candidate spot that the normal visibility system considers "exposed." For each such spot, it performs its own line-of-sight check. It casts a ray from the player's eyes to the candidate spot. Then, it checks if that ray intersects with any active VolumetricCover collider.

  • If the spot is exposed to the normal system, but the line of sight is blocked by a volumetric cover, it means the AI can use that smoke cloud to hide. The spot receives a high score (1.0).
  • If the spot is exposed and not blocked by any soft cover, it is truly a bad spot and receives a low score (0.0).
  • If the spot is already hidden behind a solid wall (according to the normal system), this scorer returns a neutral score (0.5), as it doesn't need to add its own input.

Setup

  • On your smoke grenade prefab, fog effect, etc., add a collider (e.g., SphereCollider or BoxCollider) that approximates its volume. Mark it as a Trigger.
  • Add the new VolumetricCover component to this GameObject.
  • Create an instance of the VolumetricCoverScorerSO asset.
  • Add this scorer asset to the Scorers list in your TacticalAISettingsSO and give it a positive weight (e.g., 1.0 - 1.5).

Use Cases

  • Tactical Gameplay: Allows AI to intelligently use smoke grenades (either its own or the player's) to break line of sight and maneuver.
  • Environmental Storytelling: You can fill a swamp with VolumetricCover fog volumes, and the AI will naturally understand that it can use the fog to hide and ambush the player.
  • Magic Systems: Perfect for spells that create fields of magical darkness or obscuring mist.

Use Cases

These sections provide recipes for combining providers and scorers to create common AI archetypes.

Use Case: The Cowardly AI

This guide explains how to configure the Hide-And-Stealth-System to create a "Cowardly" or "Skittish" AI. This type of AI's primary goal is to run away from threats and maximize its distance from players.

Behavior Goal

  • When a threat appears, the AI should flee.
  • It should always try to find a hiding spot that is as far away as possible.
  • It doesn't need to be clever; its main instinct is self-preservation through distance.

TacticalAISettings Configuration

This configuration will heavily reward distance and penalize proximity.

Providers

The providers should generate a wide variety of potential points for the AI to run to.

  1. OppositePlayersProviderSO: This is the most important provider for this use case. It generates points in the direction away from the player, which is the core of the fleeing behavior.
  2. NavMeshRingProviderSO: This finds many reachable points around the AI at various distances. It ensures that even if there's no clear "away" direction, the AI still has plenty of places to run to.
  3. CurrentPositionProviderSO: Essential for stability. This prevents the AI from moving if it's already in a good, distant location.

Scorers

The scorers are configured to overwhelmingly prioritize being far from the player.

Scorer Weight Reason
DistanceScorerSO 1.0 CRITICAL. This is the main driver of the cowardly behavior. A maximum weight ensures the AI will always choose the farthest spot.
ReachabilityScorerSO 0.8 CRITICAL. This filters out any spots that are too far to reach, preventing the AI from getting stuck trying to run to an impossible location.
VisibilityScorerSO 0.5 Recommended. The AI isn't a brilliant tactician, but it should still prefer a spot behind a wall over one in the open. This gives it a basic sense of cover.
DefensivePositionScorerSO 0.3 Good for stability. This gives a small bonus for staying put if the AI is already safe and far away, preventing it from fidgeting unnecessarily.

Use Case: The Stealthy Hunter

This guide explains how to configure the Hide-And-Stealth-System to create a "Stealthy Hunter" or "Assassin" AI. This AI's goal is to get as close as possible to the player while remaining completely undetected.

Behavior Goal

  • Prioritize remaining unseen above all else.
  • Actively try to get closer to the player, not farther away.
  • Use shadows, bushes, and other forms of concealment, not just hard cover.
  • Move with extreme caution, never breaking stealth.

TacticalAISettings Configuration

This advanced configuration uses specialized providers and a negative weight on the DistanceScorer to make the AI actively seek proximity while staying hidden.

Providers

The providers should focus on finding non-obvious hiding spots that a stealthy character would use.

  1. ShadowProviderSO: Excellent for AI that can use darkness. This will make them find and stick to shadows.
  2. VoxelGridProviderSO: The perfect provider for finding spots inside "soft" cover like tall grass or thick bushes. This is a key part of the "hunter" feel.
  3. BehindObstacleProviderSO: A good fallback for finding standard hard cover spots.
  4. CurrentPositionProviderSO: Essential for stability, ensuring the AI doesn't abandon a great hiding spot.

Scorers

The scorers are weighted to create an AI that is obsessed with stealth and proximity.

Scorer Weight Reason
VisibilityScorerSO 1.0 CRITICAL. The AI must be physically concealed. This is non-negotiable for a stealth character.
FOVScorerSO 1.0 CRITICAL. The AI must stay out of the player's direct vision cone. This is the essence of sneaking.
PathSafetyScorerSO 1.0 CRITICAL. The path taken must be as concealed as the destination. The hunter cannot afford to be seen while moving into position.
DistanceScorerSO 1.0 CRITICAL. This is the key to the "hunter" behavior. By giving it a weight, you penalize distance, which effectively rewards proximity.
ReachabilityScorerSO 0.8 CRITICAL. Because VoxelGridProvider can generate points inside walls, this scorer is essential to filter out all the spots the AI cannot physically reach.
DefensivePositionScorerSO 0.5 Recommended. This provides stability. If the AI has found an excellent, hidden spot right next to the player, this encourages it to stay there and wait.

Use Case: The Tactical AI

This guide explains how to configure the Hide-And-Stealth-System to create a "Tactical" AI. This AI behaves like a trained soldier, prioritizing good cover, safe movement, and smart positioning over simply running away.

Behavior Goal

  • The AI should prefer to hide behind solid objects.
  • It must avoid running through open areas where the player can see it.
  • It should be able to use pre-defined tactical positions in the level.
  • It should be "sticky" to good cover and not abandon it unless necessary.

TacticalAISettings Configuration

This configuration focuses on providers that find high-quality cover and scorers that evaluate safety and tactical advantage.

Providers

The providers should focus on finding well-defined cover locations.

  1. BehindObstacleProviderSO: This is a core provider for this use case. It dynamically finds spots behind walls and other obstacles.
  2. DirectionalCoverNodeProviderSO: Use this if you have manually placed CoverNode objects in your scene. This allows the AI to use your pre-defined tactical points intelligently.
  3. CurrentPositionProviderSO: Absolutely essential for a tactical AI. It prevents the AI from abandoning good cover for a spot that isn't significantly better.

Scorers

The scorers are balanced to reward safety and tactical awareness. The weights are critical.

Scorer Weight Reason
PathSafetyScorerSO 1.0 CRITICAL. A tactical AI must not run through the open. This heavily penalizes unsafe paths, forcing the AI to move from cover to cover.
VisibilityScorerSO 1.0 CRITICAL. The entire point of cover is to be hidden. This ensures the destination is concealed.
DefensivePositionScorerSO 0.9 CRITICAL. This makes the AI "sticky." It will stay in its safe cover spot unless it's compromised.
FOVScorerSO 0.7 Highly Recommended. This makes the AI smarter by encouraging it to move to the player's flank or rear, outside their direct vision cone.
AmbushScorerSO 0.5 Optional, but powerful. Add this to give your AI the ability to lie in wait and ambush the player.
ReachabilityScorerSO 0.5 Recommended. Filters out spots that are too far away, keeping the AI engaged in the fight.
DistanceScorerSO 0.2 Use with low weight. A small weight here gives a gentle nudge for the AI to prefer farther cover spots, but it won't override the need for safety.

Scorer: Aggressive Line of Sight

AggressiveLOSScorerSO is an offensive scorer designed for units that need to engage the enemy. Unlike standard hiding scorers that punish visibility, this scorer strictly rewards spots that have a clear line of fire to the target.

How It Works

This scorer performs a raycast to determine if a shot is possible from the candidate position.

1. Target Acquisition: It identifies the target position using the AI's memory (playerLastKnownPosition). If no memory exists, it defaults to the current position of the first available player.

2. Origin & Aim Point: * Origin: The ray starts at the candidate position raised by the enemyEyeHeight.

  • Aim Point: The ray aims at the target's position. Depending on the settings, it aims either at the Head (full eye height) or the Center Mass (half eye height).

3. Raycast Check: It casts a physics ray using the OccluderMask defined in the settings.

  • Clear Path: If the ray hits nothing, the path is clear. The scorer returns 1.0.
  • Blocked: If the ray hits an obstacle, the scorer returns 0.0.

This binary scoring (All or Nothing) ensures that "Attacker" roles strictly filter out spots where they would be staring at a wall.

Parameters

  • Require Head Visibility:
  • True: The AI checks for line of sight specifically to the target's head (at playerEyeHeight). Useful for Snipers or units that need a perfect shot.
  • False: The AI checks for line of sight to the target's center/chest. Useful for general infantry or units with splash damage.

Use Cases

  • Snipers / Turrets: Use this with a high weight to ensure the AI only picks spots where it can shoot the player immediately.
  • "Stand and Fight": Use this on a profile where the AI decides to stop retreating and start engaging.
  • Support Units: Ensures machine gunners move to positions where they can suppress the enemy.

Performance

Moderate. This scorer performs one Physics.Raycast for every candidate spot generated. While not overly expensive, it is heavier than simple distance checks.

Scorer: Audio Stimulus

AudioStimulusScorerSO is a strategic scorer that influences the AI to investigate or move toward interesting events (Stimuli) in the world, such as gunshots, explosions, or footsteps.

How It Works

This scorer connects the hiding system to a global event system (StimulusRegistry).

1. Fetch Stimuli: It retrieves a list of recent events from the StimulusRegistry.

2. Filter: It ignores any stimuli that are "stale" (older than 5 seconds) or outside the agent's Hearing Range.

3. Evaluate: For the candidate spot, it calculates the distance to the relevant sound.

4. Score:

  • It calculates a score based on proximity: 1.0 - (Distance / HearingRange).
  • Closer spots get higher scores, effectively pulling the agent toward the source of the noise.
  • It returns the highest score found among all valid stimuli.

Parameters

  • Hearing Range: The maximum distance (in meters) at which the agent can "hear" or care about a stimulus.

Use Cases

  • "Third Partying": In a battle royale or free-for-all scenario, this draws AI agents toward ongoing firefights.
  • Investigating: If a player knocks over a physics object, this scorer can override standard cover logic to make the AI move toward the noise source to investigate.

Performance and Dependencies

  • Thread Safety: Main Thread Only. It accesses the static StimulusRegistry and Time.time, which are generally not safe or accessible within Burst jobs.
  • Requirement: Requires an external system populating StimulusRegistry.RecentStimuli.

Scorer: Material Thickness (Ballistics)

BulletPenetrationScorerSO is a high-fidelity "AAA" scorer that evaluates the physical thickness of the cover object protecting the agent. It penalizes thin cover (like plywood fences, sheet metal, or bushes) that bullets would likely penetrate, ensuring the AI seeks hard cover like concrete or thick walls.

How It Works

This scorer performs a ballistics simulation using raycasts to approximate object volume.

1. Line of Fire Check: It establishes a vector from the Threat (Player) to the Candidate Spot.

2. Entry & Exit Calculation: It performs a Physics.RaycastAll along this vector to find all intersection points with the cover object.

3. Thickness Measurement: It sorts the hits by distance to identify the "Entry" point (front of the wall) and the "Exit" point (back of the wall). It calculates the distance between these two points.

4. Scoring:

  • If the calculated thickness is less than Min Safe Thickness, the spot is deemed unsafe and receives the Thin Cover Penalty.
  • If the thickness meets or exceeds the threshold, it returns a perfect score of 1.0.

Parameters

  • Min Safe Thickness: The minimum thickness (in meters) required for an object to be considered "safe" (e.g., 0.5 for 50cm of concrete).
  • Thin Cover Penalty: The score multiplier applied to thin cover (e.g., 0.1). A low value effectively rejects thin objects unless no other options exist.

Use Cases

  • Destructible Environments: In games where wood or thin metal can be shot through.
  • Realistic Shooters: Ensures AI prefers engine blocks or brick walls over wooden fences.
  • Survival: Prevents AI from hiding behind foliage or chain-link fences that block vision but not bullets.

Performance

Heavy. This scorer uses Physics.RaycastAll and array sorting for every candidate check. It is computationally expensive and should be used judiciously, perhaps only when the AI is under active fire.

Scorer: Casualty Aversion

CasualtyAversionScorerSO is a high-level survival scorer that allows the AI to "learn" from the battlefield. It penalizes hiding spots that are near locations where allies have recently died, simulating the realization that an area is "zeroed-in" or compromised.

How It Works

This scorer acts as a dynamic "Fear Map" generator.

1. Fetch Data: It retrieves a list of recent death locations from the global CasualtyRegistry.

2. Distance Check: For every candidate spot, it checks the distance to the nearest known casualty.

3. Score:

  • 1.0 (Safe): If the spot is outside the Fear Radius of all dead bodies.
  • Gradient Penalty: If the spot is inside the radius, the score drops linearly.
  • 0.0 (Dangerous): If the spot is exactly where an ally died.

Parameters

  • Fear Radius: The radius (in meters) around a corpse that is considered "cursed" or dangerous.

Use Cases

  • Anti-Lemming Behavior: Prevents a line of AI agents from running to the exact same cover point one by one and getting shot by the same sniper.
  • Dynamic Flanking: As direct approaches become littered with casualties, the "negative score" pushes the AI naturally toward flanking routes that haven't been tried yet.

Performance and Dependencies

  • Thread Safety: Main Thread Only. Accesses the CasualtyRegistry.
  • Cost: Low. Uses simple distance-squared checks against a usually small list of recent deaths.

Scorer: Cover Cluster (Density)

CoverClusterScorerSO evaluates the immediate environment around a candidate spot to determine "Cover Density." It prefers areas rich in obstacles (junkyards, forests, urban debris) over isolated cover (a single rock in an open field). This gives the AI backup options if their primary spot is compromised.

How It Works

1. Proximity Scan: It performs a Physics.OverlapSphere check around the candidate position using the defined Check Radius.

2. Object Counting: It counts how many valid occluders (walls, crates, trees) are found in this radius.

3. Density Evaluation:

  • If the count is <= 1 (implying only the cover itself or the floor is detected), it returns a score of 0.0 (Isolated).
  • It scales the score linearly based on Ideal Cover Count. If the count meets or exceeds this ideal, it returns 1.0.

Parameters

  • Check Radius: The radius in meters to scan for additional cover objects.
  • Ideal Cover Count: The number of nearby objects required to achieve a maximum score.

Use Cases

  • Tactical Flexibility: Ensures the AI doesn't trap itself behind a "Lonely Rock" where it can be easily flanked with no escape.
  • Urban Combat: Draws AI towards piles of rubble or vehicle graveyards.
  • Forest Encounters: Encourages AI to move deeper into the treeline rather than staying at the edge.

Performance

Moderate. It relies on Physics.OverlapSphereNonAlloc, which is relatively efficient but involves checking all colliders in a small radius.

Scorer: Escape Route

EscapeRouteScorerSO is a survival scorer that ensures the agent doesn't corner themselves. It evaluates whether a hiding spot offers a valid path for further retreat if the situation worsens.

How It Works

This scorer tests the "navigability" of the terrain *behind* the cover.

1. Determine Threat: Calculates the average position of all known players to determine the "Threat Direction."

2. NavMesh Raycast: It casts a ray on the Navigation Mesh starting from the candidate spot and moving away from the threat.

3. Analyze Hit:

  • Edge Hit: If the ray hits a NavMesh edge (a wall, a cliff, the map boundary), it means retreat is blocked.
  • No Hit: If the ray travels the full Min Escape Distance without hitting an edge, the path is clear.

4. Score:

  • 1.0: Full escape route available.
  • 0.0 - 0.9: Scaled based on how far the agent can retreat before hitting a wall. If the distance is very short (<1m), it returns 0.0.

Parameters

  • Min Escape Distance: How far (in meters) the agent needs to be able to run backwards for the spot to be considered safe.
  • Escape Cone Angle: (Internal logic) Defines the direction away from the enemy.

Use Cases

  • Fluid Combat: Encourages AI to pick "open" cover (like a tree in a field) over "closed" cover (like the corner of a dead-end alley).
  • Hit and Run: Essential for skirmisher units that need to fire a few shots and then immediately fall back.

Performance and Dependencies

  • Thread Safety: Main Thread Only. Uses UnityEngine.AI.NavMesh.Raycast.
  • Cost: Low to Moderate. NavMesh raycasts are generally cheaper than Physics raycasts but still require main thread access.

Scorer: Exposure Volume

ExposureVolumeScorerSO calculates how "exposed" a candidate spot is to the open world. Unlike visibility checks (which check against specific enemies), this checks against empty space. It prevents the AI from choosing spots that are technically hidden from the current enemy but are otherwise wide open (e.g., the middle of a courtyard).

How It Works

This scorer creates a 360-degree "safety profile" of the spot.

1. Radial Sampling: It casts a specified number of rays (Samples) in a circle around the candidate spot at waist height.

2. Open Space Detection: For each ray, it checks if it hits any geometry within Check Distance.

  • Hit: The direction is "Closed/Safe."
  • No Hit: The direction is "Open/Exposed."

3. Ratio Calculation: It calculates the ratio of Open rays to Total rays.

  • A spot in an open field (0 hits) gets a high exposure ratio -> Low Score.
  • A spot in a tight alley (many hits) gets a low exposure ratio -> High Score.

Parameters

  • Samples: The number of rays to cast (e.g., 8 or 16). Higher values are more accurate but more expensive.
  • Check Distance: How far the ray must travel without hitting anything to be considered "Open."

Use Cases

  • General Survival: The most robust "default" behavior for avoiding open areas.
  • Corridors vs. Halls: Biases the AI towards tighter spaces where they are less likely to be sniped from unknown angles.
  • Avoiding "Skylining": Prevents AI from standing on top of ridges or hills where they are visible against the sky.

Performance

Scalable. The cost is directly proportional to the Samples parameter. 8 samples is usually sufficient for good results without heavy performance impact.

Scorer: Fatal Funnel (Doorway Avoidance)

FatalFunnelScorerSO is a tactical survival scorer designed to identify and penalize choke points. It detects if a candidate spot is located in a narrow corridor, doorway, or bridge—areas often referred to as "Fatal Funnels" in tactical doctrine.

How It Works

This scorer measures the lateral (side-to-side) space available at the candidate position.

1. Orientation: It determines the "Right" vector relative to the cover normal (or the agent's facing direction).

2. Width Scan: It casts two Physics Raycasts: one Left and one Right from the candidate spot.

3. Measure: It sums the distance to the nearest obstacle on both sides.

4. Score:

  • 1.0 (Safe): If the total width is greater than Min Corridor Width.
  • Penalty: If the total width is less than the minimum, it returns the Choke Point Penalty (default 0.0).

Parameters

  • Min Corridor Width: The minimum width (in meters) required for a space to be considered safe/maneuverable.
  • Choke Point Penalty: The score assigned to a narrow spot (0.0 = Reject, 1.0 = Ignore).

Use Cases

  • Doorway Safety: Stops AI from taking cover *inside* a door frame where they have no room to dodge or strafe.
  • Hallway Awareness: Discourages stopping in the middle of narrow bridges or catwalks.

Performance and Dependencies

  • Thread Safety: Main Thread Only. Uses Physics Raycasts.
  • Cost: Moderate. Two raycasts per candidate.

Scorer: High Ground

HighGroundScorerSO is a tactical scorer that evaluates the verticality of a hiding spot relative to the target. It creates a behavior where the AI naturally seeks elevation advantages, adhering to the "High Ground" tactical doctrine.

How It Works

The scorer compares the Y-coordinate (height) of the candidate spot against the target's Y-coordinate.

1. Elevation Calculation: It calculates Difference = Candidate.y - Target.y.

2. Above Target: If the difference is positive (AI is higher), the score scales linearly from 0.5 to 1.0.

  • A difference of 0 meters gives a score of 0.5.
  • A difference equal to or greater than Ideal Height Advantage gives a score of 1.0.

3. Below Target: If the difference is negative or zero (AI is lower):

  • If Penalize Low Ground is true: The score scales down from 0.5 to 0.0 based on how deep below the target the spot is.
  • If false: It returns a neutral score of 0.5.

Parameters

  • Ideal Height Advantage: The height difference (in meters) required to achieve a perfect score. For example, if set to 4.0, a spot 4 meters above the player gets the maximum bonus.
  • Penalize Low Ground: If checked, positions significantly lower than the player will be actively discouraged (receiving low scores). If unchecked, low ground is treated as neutral.

Use Cases

  • King of the Hill: AI will fight to reach the top of a structure or hill.
  • Grenadiers: Units with lobbed projectiles benefit from height; this scorer guides them to balconies or rooftops.
  • Snipers: Gives snipers a strong preference for towers and upper-story windows.

Performance

Very Fast. This scorer uses simple arithmetic comparisons. It has negligible performance cost.

Scorer: Light Probe (Shadows)

LightProbeScorerSO is a stealth-oriented scorer that leverages Unity's baked or dynamic Global Illumination (GI) data. It allows agents to detect and prioritize dark areas, such as shadows cast by static geometry.

How It Works

This scorer queries the scene's lighting data directly.

1. Probe Query: It calls LightProbes.GetInterpolatedProbe at the candidate position. This returns the lighting information (Spherical Harmonics) for that specific point in space.

2. Luminance Calculation: It estimates the average brightness (Luminance) from the probe data.

3. Score:

  • 1.0 (Stealthy): If the brightness is 0 (Pitch Black).
  • 0.0 (Exposed): If the brightness exceeds the Darkness Threshold.
  • Gradient: Scores linearly between 0 and the threshold.

Parameters

  • Darkness Threshold: The brightness value (approx 0.0 to 1.0+) above which a spot is considered "too bright" and receives 0 score.

Use Cases

  • Stealth Games: AI can hide in the shadows of buildings or trees.
  • Night Missions: AI will stick to unlit alleyways rather than walking under streetlights.

Performance and Dependencies

  • Thread Safety: Main Thread Only. LightProbes API is not thread-safe.
  • Cost: Low. Interpolating a light probe is very fast compared to raycasting.

Scorer: Line of Fire Safety

LineOfFireScorerSO is a squad-coordination scorer designed to prevent friendly fire incidents. It ensures that an AI agent does not choose a hiding spot or position that blocks the firing line of a teammate engaging a target.

How It Works

This scorer acts as a Binary Disqualifier. It does not grade spots on a curve; a spot is either safe (1.0) or obstructing fire (0.0).

The logic follows these steps for every candidate spot:

1. Identify the Target: The system determines the focal point of the squad's attention. This is typically the playerLastKnownPosition. If that is unavailable, it defaults to the current position of the nearest player.

2. Analyze Allies: The scorer iterates through all known allies (excluding the agent itself).

3. Project Fire Tunnel: For each ally, it mathematically constructs a line segment starting from the ally's eye position and ending at the target's center.

4. Check Obstruction: It calculates the shortest distance from the candidate spot to this line segment.

  • If the distance is less than the Safety Radius, the spot is considered to be "in the crossfire."
  • The scorer immediately returns 0.0, effectively disqualifying the spot (depending on the scorer's weight).

5. Pass: If the candidate spot is outside the safety radius of all ally fire lines, it returns 1.0.

Parameters

  • Safety Radius: A float value that defines the thickness of the "fire tube." A larger radius ensures the agent gives teammates a wider berth, while a smaller radius allows for tighter formations but increases the risk of clipping.

Use Cases

  • Corridor Combat: Prevents agents from clustering in a single line of sight within narrow hallways.
  • Coordinated Flanking: Ensures that while one unit suppresses the player, the flanking unit moves around the fire, not through it.
  • Heavy Weapons Support: Essential when one unit (e.g., a machine gunner) has a fixed lane of fire that must remain clear.

Performance and Dependencies

  • Complexity: $O(N)$ where $N$ is the number of allies.
  • Cost: Low. The calculation uses simple vector dot products to determine the distance to a line segment. It does not perform physics raycasts.
  • Requirement: The HidingContext must have the allies list populated, and there must be a valid target (playerLastKnownPosition or valid players list).

Scorer: Muzzle Discipline

MuzzleDisciplineScorerSO is a tactical movement scorer that simulates professional firearm safety rules (specifically "do not cover anything you are not willing to destroy"). It ensures agents do not cross directly in front of a teammate's weapon barrel, regardless of whether that teammate is currently firing at a target.

How It Works

Unlike the LineOfFireScorer (which cares about a specific target), MuzzleDisciplineScorer cares about the direction the ally is facing. It projects a "danger cylinder" extending forward from every ally.

The logic follows these steps:

1. Analyze Allies: Iterate through all squad members.

2. Project Forward Vector: Retrieve the ally's forward directional vector.

3. Cylinder Check: The scorer calculates the position of the candidate spot relative to the ally. It checks two conditions:

  • Depth: Is the spot in front of the ally (positive dot product) and within the defined Danger Distance?
  • Width: Is the perpendicular distance from the spot to the ally's forward line less than the Danger Radius?
  • *Note:* This calculation flattens the Y-axis, treating the check as 2D (top-down) to ensure robustness on slopes.

4. Result:

  • If both conditions are true, the agent is standing in front of the ally's gun. The scorer returns 0.0.
  • Otherwise, it returns 1.0.

Parameters

  • Danger Distance: How far forward (in meters) the "danger zone" extends from an ally. Beyond this distance, the scorer ignores the ally's facing direction.
  • Danger Radius: The width (radius) of the cylinder extending from the ally.

Key Differences: Line of Fire vs. Muzzle Discipline

| Feature | Line of Fire Scorer | Muzzle Discipline Scorer |

| :--- | :--- | :--- |

| Reference | Line from Ally to Enemy | Line from Ally to Forward Direction |

| Purpose | Prevent blocking active shooting | Prevent flagging/crossing lines during movement |

| Context | Best during active combat | Best during patrol, searching, or moving formations |

Use Cases

  • Tactical Formations: Keeps agents in a proper wedge or line formation while moving, preventing them from stepping on each other's toes.
  • CQB / Room Clearing: Ensures that when a team stacks up or enters a room, the rear agents do not walk in front of the point man.
  • Non-Combat Movement: Makes the AI look professional and trained by respecting personal space and weapon orientation even when no enemy is visible.

Performance and Dependencies

  • Burst Compatible: Yes. This scorer implements IMultiThreadedScorer, making it safe and highly efficient for use in the Asynchronous/Burst job system.
  • Cost: Extremely Low. Uses simple vector math.
  • Requirement: Requires the allies list in HidingContext to be populated.

Scorer: Player Gaze

PlayerGazeScorerSO is a visibility scorer that specifically avoids the player's direct focus. While standard visibility checks determines if the player *can* see the agent, this scorer determines if the player is *currently looking* at the spot.

How It Works

This scorer performs a vector analysis of the player's orientation.

1. Vector Calculation: It calculates the vector from the player's eyes to the candidate spot.

2. Angle Check: It compares this vector with the player's Transform.forward vector (their gaze).

3. Score:

  • Penalty: If the angle is smaller than Gaze Cone Angle (meaning the player is looking directly at the spot).
  • 1.0: If the spot is in the player's peripheral vision or behind them.

Parameters

  • Gaze Cone Angle: The angle (in degrees) representing the player's foveal (focused) vision. Usually narrow (e.g., 15 degrees).
  • Penalty Strength: How strictly to avoid the gaze. 1.0 means avoid completely; 0.5 means it's a soft preference.

Use Cases

  • Sniper Avoidance: If the player is aiming down a hallway, the AI will refuse to step into that hallway.
  • Sneaking: Encourages AI to move only when the player looks away.

Performance and Dependencies

  • Thread Safety: Main Thread Only.
  • Cost: Very Low. Simple vector math.

Scorer: Player Path Prediction (V2)

Offensive. PlayerPathPredictionScorerSO aims for where the player will be.

How It Works

It extrapolates the player's current velocity to predict their position in X seconds. It scores spots based on visibility/proximity to that future point.

Scorer: Push Forward

PushForwardScorerSO is a movement-drive scorer that incentivizes the AI to close the distance to the target. It creates "Rusher" or "Aggressive" behaviors by rewarding spots that physically move the agent closer to the enemy.

How It Works

This scorer measures the distance gain achieved by moving to a candidate spot.

1. Distance Comparison: It calculates two distances:

  • Current Distance: From the Agent's current position to the Target.
  • Candidate Distance: From the Candidate spot to the Target.

2. Evaluation:

  • Gaining Ground: If Candidate Distance < Current Distance, the spot is closer. The score starts at 0.5 and increases based on how much distance is closed (up to a max of ~5 meters for a full score).
  • Retreating/Static: If Candidate Distance >= Current Distance, the spot is further away or static. The scorer returns 0.0.

This effectively filters out any "retreating" options and prioritizes the spots that offer the most forward progress.

Parameters

  • This scorer currently has no exposed parameters. It uses an internal constant (approx. 5 meters) to normalize the "distance gained" score.

Use Cases

  • Melee Units: Essential for zombies or sword-wielders to force them to constantly close the gap while still using cover (if combined with other scorers).
  • Shotgunners: Drives units with short-range weapons to get into effective range.
  • Flanking: When combined with a flank provider, this ensures the flank maneuver tightens the noose rather than drifting away.

Performance

Fast. It relies on standard Vector3.Distance calculations.

Scorer: Rear Attack Bonus

RearAttackScorerSO is a positional scorer that evaluates the relative orientation of the candidate spot to the target. It heavily rewards positions located behind the target's back, facilitating stealth, backstabs, or flanking maneuvers.

How It Works

This scorer uses vector mathematics (Dot Product) to determine the angle of approach.

1. Vector Calculation: It calculates the direction vector from the Target to the Candidate Spot.

2. Facing Check: It compares this direction against the Target's Forward Vector.

3. Dot Product Scoring:

  • In Front: If the spot is directly in front of the target (Target is looking at it), the Dot Product is 1.0. This converts to a score of 0.0.
  • Behind: If the spot is directly behind the target (Target is looking away), the Dot Product is -1.0. This converts to a score of 1.0.
  • Side: Positions directly to the side receive a neutral score around 0.5.

Parameters

  • This scorer relies purely on relative geometry and has no tweakable parameters.

Use Cases

  • Stealth AI: Ideal for "Spy" or "Assassin" classes that need to stay out of the player's cone of vision.
  • Surrounding: Encourages a squad to fan out and wrap around a target, as spots behind the player become mathematically more valuable than spots in front.
  • Critical Hits: If your game has a damage bonus for back attacks, this scorer aligns the AI behavior with that mechanic.

Performance

Very Fast. Uses efficient vector math (Vector3.Dot). It is extremely lightweight.

Scorer: Repetition Penalty

RepetitionPenaltyScorerSO is a utility scorer that forces the AI to "rotate" positions. It penalizes candidate spots that are geographically close to where the agent has hidden recently.

How It Works

This scorer accesses the agent's short-term memory.

1. Fetch History: It retrieves the PositionHistory queue from the TacticalAgent.

2. Comparison: It compares the candidate spot's position against every position in the history.

3. Score:

  • 0.0 (Reject): If the candidate is within Memory Radius of *any* previous spot.
  • 1.0 (Accept): If the spot is fresh territory.

Parameters

  • Memory Radius: The radius (in meters) around previous spots that is considered "used ground."

Use Cases

  • Whac-A-Mole: Prevents the AI from popping up at the exact same corner over and over again.
  • Evasive Tactics: Forces the AI to constantly relocate after firing, making them a harder target.

Performance and Dependencies

  • Thread Safety: Main Thread Only. Accesses the TacticalAgent instance.
  • Cost: Very Low. Simple distance checks against a small history queue (usually size 3-5).

Scorer: Sector Watch (Flank Security)

SectorWatchScorerSO is a squad-coordination scorer designed to maximize the team's situational awareness. It encourages individual agents to face directions (sectors) that are not currently being watched by their teammates, effectively automating "Rear Security" or "Flank Watch" behaviors.

How It Works

This scorer operates by analyzing the "eyes" of the entire squad to find blind spots.

1. Divide the View: It conceptually divides the 360-degree view around the squad into a specific number of slices or "sectors" (defined by the sectors parameter).

2. Analyze Allies: It iterates through all teammates and calculates which sector they are currently facing based on their forward direction. It builds a "histogram" of coverage (e.g., "Sector 1 has 2 watchers, Sector 2 has 0 watchers").

3. Evaluate Candidate: For the candidate hiding spot, it determines the direction the agent *would* face if they took that spot.

  • *Primary Check:* If the spot has a cover normal (e.g., a wall), it assumes the agent will look outward away from the wall.
  • *Fallback:* If no cover normal exists, it assumes the agent will look outward away from the center of the squad's formation.

4. Score:

  • 1.0 (High Reward): If the candidate spot faces a "Blind Spot" (a sector with 0 allies watching it).
  • 0.5 (Neutral): If the sector is already watched by exactly one ally.
  • 0.0 (Penalty): If the sector is crowded (watched by 2 or more allies), the spot is considered redundant.

Parameters

  • Sectors: The integer number of slices to divide the 360-degree view into. A value of 8 (default) creates 45-degree sectors, which is standard for tactical games.

Use Cases

  • Perimeter Defense: When a squad holds a room, this ensures they spread out to watch all doors and windows rather than all staring at the same entrance.
  • Rear Security: If the whole squad is moving North, this scorer will highly reward a spot that allows an agent to look South (Rear), protecting the team's back.

Performance and Dependencies

  • Burst Compatible: Yes. Implements IMultiThreadedScorer.
  • Complexity: $O(N)$ where $N$ is the number of allies.
  • Cost: Low. Uses basic trigonometry (Vector3.SignedAngle) and array lookups.

Scorer: Skylining (Silhouette)

SkyliningScorerSO is a visual camouflage scorer. It detects "Skylining"—a tactical error where a soldier stands on a ridge or roof and is silhouetted against the bright sky, making them an easy target.

How It Works

This scorer checks the *background* of the candidate spot from the enemy's perspective.

1. Reverse Raycast: It calculates the direction from the Enemy -> Candidate Spot.

2. Background Check: It casts a ray starting *from* the candidate spot and continuing along that direction (away from the enemy).

3. Analyze Hit:

  • Hit: If the ray hits geometry (a wall, a mountain, a tree), it means the agent has a "Backstop." They will blend in with the background. (Score 1.0).
  • No Hit: If the ray hits nothing, it means the background is the Skybox. The agent is skylined. (Score = Skylining Penalty).

Parameters

  • Background Check Distance: How far behind the agent to check for geometry (e.g., 20m).
  • Skylining Penalty: The score received if skylined (0.0 = Reject, 1.0 = Ignore).

Use Cases

  • Ridge Safety: Prevents AI from standing on the very top of a hill; encourages them to move slightly down the reverse slope (Military Crest).
  • Rooftops: AI will avoid standing on the very edge of a roof where they stick out against the sky.

Performance and Dependencies

  • Thread Safety: Main Thread Only. Uses Physics Raycasts.
  • Cost: Moderate. One raycast per candidate.

Scorer: Smart Path Safety (Detour Aware)

SmartPathSafetyScorerSO is a highly advanced pathfinding scorer. Unlike basic path checks that simply reject unsafe paths, this scorer actively tries to find a "Detour" or "Flank" route to the destination if the direct path is dangerous.

How It Works

This is a multi-stage path analyzer:

1. Direct Check: It calculates a NavMesh path to the candidate spot. It samples points along the path and checks visibility to the player.

  • If safe, it returns the score immediately.

2. Detour Calculation: If the direct path is exposed, it calculates a heuristic "Via Point" to the side (flank) of the danger vector.

3. Two-Leg Validation: It calculates two separate paths:

  • Leg 1: Agent -> Detour Point.
  • Leg 2: Detour Point -> Candidate Spot.

4. Score:

  • If *both* legs are safe, it returns a passing score (slightly penalized to account for the longer travel time).
  • If either leg is unsafe, the spot is disqualified.

Parameters

  • Visibility Threshold: The percentage of path samples (0.0 to 1.0) allowed to be visible before the path is deemed unsafe.
  • Flank Offset Distance: How far sideways (in meters) to plot the detour point.
  • Penalty Exponent: Controls the curve of the safety penalty.

Use Cases

  • Smarter Movement: Allows AI to cross a dangerous street by running *down* the block and crossing at a safe point, rather than running straight across and getting shot.
  • Flanking: Naturally produces flanking behaviors as the AI seeks safe routes around the player's line of fire.

Performance and Dependencies

  • Thread Safety: Main Thread Only. Heavily reliant on NavMesh.CalculatePath and IVisibilityService.
  • Cost: High. This is an expensive scorer. It may perform multiple path calculations and dozens of visibility checks per candidate. Use sparingly or with lower candidate counts.

Scorer: Squad Reservation

SquadReservationScorerSO is a critical coordination scorer that prevents multiple agents from trying to claim the exact same hiding spot. It acts as the bridge between the individual agent's search and the central SquadCoordinator.

How It Works

This is a Binary Disqualifier. It does not output a gradient score; it either accepts a spot or strictly forbids it.

1. Check Coordinator: It first verifies that a SquadCoordinator is assigned and accessible.

2. Query Reservation: It asks the coordinator: *"Is this specific position already reserved by someone else in my squad?"*.

3. Result:

  • Negative Infinity: If the spot is reserved, it returns float.NegativeInfinity. This creates an immediate "Veto," ensuring this spot is removed from the candidate list regardless of how good its other scores are.
  • 1.0: If the spot is free, it returns a passing score.

Parameters

  • *None.* This scorer relies entirely on the external logic of the SquadCoordinator system.

Use Cases

  • Stacking Prevention: Essential for any game with multiple AI agents. Without this, the "best" cover point (e.g., the only concrete wall) would attract every single AI unit, causing them to clip inside each other.

Performance and Dependencies

  • Thread Safety: Main Thread Only. This scorer is not Burst compatible because it must access the SquadCoordinator class (a standard C# class) to check dynamic dictionaries and lists.
  • Requirement: The HidingContext must have a valid reference to squadCoordinator and requestingAgent.

Scorer: Squad Separation

SquadSeparationScorerSO is a spatial scorer designed to maintain a minimum physical distance between squad members. While TeamProximityScorer pulls agents together to form a cohesive unit, SquadSeparationScorer pushes them apart locally to prevent overcrowding.

How It Works

This scorer functions as a proximity filter.

1. Analyze Allies: It iterates through all squad members in the HidingContext.

2. Distance Check: It calculates the squared distance between the candidate spot and every ally's current position.

3. Score:

  • 0.0 (Disqualified): If *any* ally is closer than the defined Min Separation, the spot receives a 0 score. Note: Unlike Reservation (which checks *future* spots), this checks *current* positions.
  • 1.0 (Pass): If all allies are outside the minimum separation radius.

Parameters

  • Min Separation: The minimum distance (in meters) allowed between agents. Default is 3.0f.

Use Cases

  • Explosive Safety: Prevents the squad from clustering so tightly that a single grenade could kill everyone.
  • Pathfinding Clarity: Keeping agents 3+ meters apart ensures their NavMeshAgents don't fight for the same small patch of navigation mesh.

Performance and Dependencies

  • Burst Compatible: Yes. Implements IMultiThreadedScorer.
  • Cost: Extremely low. Uses simple distance-squared checks.

Scorer: Stalemate Breaker

StalemateBreakerScorerSO is a behavioral scorer designed to prevent "camping." It detects if an agent has been sitting in one spot for too long and dynamically increases the value of flanking positions to force movement.

How It Works

This scorer introduces the concept of "Boredom" to the AI.

1. Check Boredom: It checks the TimeInCover property of the agent.

  • If the time is less than Boredom Threshold, the agent is "content," and the scorer returns a neutral 0.5.

2. Calculate Flank Urgency: If the agent is "bored" (time > threshold), the scorer aggressively seeks movement.

3. Geometric Analysis: It compares the direction to the Enemy vs. the direction to the Candidate Spot.

  • It uses the Dot Product to determine the angle.
  • High Reward: Lateral movements (moving sideways relative to the enemy). This is a "Flank".
  • Low Reward: Moving directly forward (aggressive) or backward (retreat) is rewarded less than flanking in this specific scorer.

4. Scaling: The score is multiplied by a Boredom Factor. The longer the agent sits, the higher the score for flanking becomes, eventually overriding safety concerns to force a move.

Parameters

  • Boredom Threshold: The time (in seconds) the agent must sit in cover before this scorer activates.
  • Flank Urgency: A multiplier that determines how desperately the agent wants to flank once bored. Higher values create more aggressive, restless AI.

Use Cases

  • Anti-Camping: Prevents AI from finding one "perfect" spot and staying there forever, which is boring for the player.
  • Dynamic Flow: Creates a rhythm to combat where AI takes cover, shoots for a while, and then naturally decides to rotate to a new angle.

Performance and Dependencies

  • Thread Safety: Main Thread Only. It relies on accessing the TimeInCover property from the TacticalAgent instance provided in the context.

Scorer: Wall Hugging

WallHuggingScorerSO is a survival-focused scorer that strongly encourages the agent to stay physically close to geometry (walls, large obstacles). It combats "Open Field Syndrome," where an AI might hide in a technically valid spot that feels unnatural because it's floating in the middle of a room.

How It Works

1. Geometry Search: It performs an OverlapSphere check to find any colliders on the Wall Mask layers within the Scan Radius.

2. Nearest Point Calculation: If colliders are found, it calculates the ClosestPoint on the collider's surface to the candidate spot.

3. Distance Scoring:

  • Touching Wall: If the distance is near 0, the score is 1.0.
  • Far from Wall: As the distance approaches Scan Radius, the score drops to 0.0.
  • No Wall: If no walls are found within the radius, the score is 0.0.

Parameters

  • Scan Radius: The maximum distance from a wall the agent is allowed to be to receive any score.
  • Wall Mask: The LayerMask defining what counts as a "Wall" (usually Default, Static, or a specific Cover layer).

Use Cases

  • Indoor Movement: Keeps AI moving along the edges of corridors rather than the center.
  • Stealth: Spies/Assassins look smarter when sticking to the shadows at the edge of a room.
  • Cover Readiness: An agent hugging a wall is always closer to breaking line of sight than one standing 2 meters away from it.

Performance

Moderate. Uses OverlapSphere followed by ClosestPoint calculations. It is generally efficient enough for standard usage.

Scorer: Zone Control (LOS)

ZoneControlScorerSO is a strategic scorer designed for defensive or "Overwatch" roles. Unlike ObjectiveProximityScorer (which simply pulls the agent *physically close* to an objective), this scorer rewards positions that maintain a clear Line of Sight (LOS) to the objective, allowing the AI to guard it effectively from a distance.

How It Works

This scorer evaluates the tactical value of a spot based on its ability to "see" a key location.

1. Identify Target: It searches the ObjectiveService for a valid objective. (Currently, it defaults to the first active objective found, but supports ID filtering).

2. Distance Check: It first verifies if the candidate spot is within the Max Control Distance of the objective. If the spot is too far away to effectively guard the point, it is disqualified (Score 0.0).

3. Visibility Check: It performs a Physics Raycast from the candidate spot's eye position to the center of the objective.

4. Score:

  • 1.0 (High Value): If the raycast has a clear line of sight to the objective.
  • 0.0 (Disqualified): If the view is blocked by a wall or obstacle.

Parameters

  • Target Objective ID: An integer ID used to filter which specific objective to watch (matching your ObjectiveService IDs).
  • Max Control Distance: The maximum range (in meters) from which the AI is allowed to guard the point.

Use Cases

  • King of the Hill: AI defenders will find spots like balconies or windows that overlook the capture point, rather than standing directly inside the capture zone where they are vulnerable to grenades.
  • Sniper Overwatch: Forces sniper units to find high-visibility lines of sight on key choke points.

Performance and Dependencies

  • Thread Safety: Main Thread Only. It relies on the ObjectiveService, which is likely not thread-safe.
  • Cost: Moderate. Performs one Physics Raycast per candidate spot within range.