← Back to all addons

Advanced Chase & Alert AI Logic

Posted by MrMystery on Apr 15, 2025 at 3:28 PM

GameplayFeature3.0 Verified
💡 7 eyesshaking 2

This was a slight modification to the AIController, to add more features I wanted in my game, it adds a minimum chase timer, i.e., if you are at the edge of the range of a monster, and attack it and run away, it won’t just stop chasing you, it will chase you outside it’s range for at least the duration of the min chase timer. Secondly, I wanted to alert enemies around the provoked enemy, so there are alert settings, including a radius to alert, and a cooldown so they won’t just keep screaming HELP HELP HELP attack the player.

In Chase Settings Add: [SerializeField, Min(0.1f)] private float m_minimumChaseDuration = 6.0f;

Add a new Alert Settings:

[Header("Alert Settings")]
        public bool wasAlerted = false;
        [SerializeField, Min(1.0f)] private float m_alertRadius = 3.0f;
        [SerializeField, Min(1.0f)] private float m_alertCooldown = 5.0f;

Add two private variables:

private float m_alertCooldownTimer = 0.0f;
        private float m_chaseDurationTimer = 0.0f;

Modify OnProvoked:

private void OnProvoked(CharacterBase source)
        {
            if (source == null) return;

            if (source && !m_target && m_retargetCooldownTimer == 0.0f && CombatSolver.IsJudiciousTarget(m_subject, source))
            {
                m_target = source;
                GameManager.NotificationSystem.targetDetected.Invoke(this, m_subject);

                if (!wasAlerted)
                {
                    AlertNearbyAllies(source.transform.position);
                    wasAlerted = true;
                    m_alertCooldownTimer = m_alertCooldown;
                }

                m_chaseDurationTimer = m_minimumChaseDuration;  // Start the minimum chase timer
            }
        }

Add a new helper Method:

private void AlertNearbyAllies(Vector3 attackerPosition)
        {
            Collider2D[] nearbyAllies = Physics2D.OverlapCircleAll(transform.position, m_alertRadius);

            foreach (Collider2D allyCollider in nearbyAllies)
            {
                if (allyCollider.CompareTag("Enemy"))
                {
                    if (allyCollider.TryGetComponent<CharacterBase>(out CharacterBase allyCharacter))
                    {
                        if (allyCharacter.controller is AIController allyAI)
                        {
                            if (allyAI != this && allyAI.m_target == null && !allyAI.wasAlerted)
                            {
                                allyAI.m_target = this.m_target;
                                GameManager.NotificationSystem.targetDetected.Invoke(allyAI, m_target);
                                allyAI.wasAlerted = true;
                                allyAI.m_alertCooldownTimer = allyAI.m_alertCooldown;
                                allyAI.m_chaseDurationTimer = allyAI.m_minimumChaseDuration;
                            }
                        }
                    }
                }
            }
        }

Add a new timer into UpdateCooldowns():

if (m_alertCooldown > 0.0f)
            {
                m_alertCooldownTimer = Math.Max(m_alertCooldownTimer - Time.fixedDeltaTime, 0.0f);
            }

Modify CheckIfTargetOutOfRange() Method to use chase timer:

private void CheckIfTargetOutOfRange(float distanceToTarget)
        {
            // Update the timer
            if (m_chaseDurationTimer > 0.0f)
            {
                m_chaseDurationTimer -= Time.fixedDeltaTime;
            }

            // Only allow returning if the minimum chase duration has passed
            if (m_chaseDurationTimer <= 0.0f)
            {
                float distanceToInitialPosition = Vector2.Distance(m_initialPosition, transform.position);
                bool isTooFarFromInitialPosition = distanceToInitialPosition > m_resetFromInitialPositionRadius;
                bool isTooFarFromTarget = distanceToTarget > m_resetFromTargetDistanceRadius;

                if (isTooFarFromInitialPosition || isTooFarFromTarget)
                {
                    StopChase(m_targetOutOfRangeRetargetCooldown);
                }
            }
        }

In StopChase(), reset alerted flag: wasAlerted = false;

In OnFixedUpdate() add:

if (m_alertCooldownTimer <= 0.0f)
            {
                wasAlerted = false;
            }

Also in OnFixedUpdate(), replace the if (!m_target) section with:

if (!m_target)
            {
                if (m_retargetCooldownTimer == 0.0f)
                {
                    var foundTarget = FindTarget();
                    if (foundTarget)
                    {
                        OnProvoked(foundTarget);
                    }
                }
            }

💬 Comments (7)

SvanDark Apr 15, 2025 at 04:23 PM
Now THIS is amazing, thank you so much!
MrMystery Apr 15, 2025 at 07:50 PM

Glad you like it… I did notice one more optional change depending on how ‘aggressive’ and ‘persistent’ you want monsters to be in the CheckIfTargetOutOfRange() setting the final check to && instead of || - will make it so the monster continues to chase you unless it’s both too far from home and too far from you

if (isTooFarFromInitialPosition && isTooFarFromTarget)
{
    StopChase(m_targetOutOfRangeRetargetCooldown);
}

Adds a bit more - don’t pull more than you can chew 😉

SvanDark Apr 15, 2025 at 07:53 PM

It’s not that i’m stupid, i’m just lazy (as always), i can do all the steps if it’s really needed

But do you by any chance have the whole “aicontroller” script with all of those stuff added that you can share? 🤔

MrMystery Apr 15, 2025 at 07:59 PM
Yea, Gyvr just doesn’t want us dropping whole code, and it’s a bit too long, too many messages to do that 😉
SvanDark Apr 15, 2025 at 07:59 PM
Ahh gotcha, yeah fair enough lol, thank you!
MrMystery Apr 15, 2025 at 08:01 PM
Sent in DM.
🫡 1
Spellvamp Apr 21, 2025 at 04:04 AM
Id love to have it as well if possible.

Want to continue the conversation?