Really stuck - Enemy keeps chasing during Suspicion

I’ve been trying to debug this for well over an hour+ and not sure at all what’s going on. Seems to be very similar problem to: UPDATE- RPG Current Dependencies & My version of Suspicion + QUESTION - #4 by solideserenade

Here’s what I can tell:

  1. GetComponent().CancelCurrentAction(); is getting called at the right time to trigger suspicion mode
  2. Because the current action is Fighter, it’s calling Cancel() on the Fighter script
  3. The Mover script stays active! The line navMeshAgent.isStopped = true; doesn’t get called anywhere.

My player keeps getting chased until the suspicion clock runs out.

(Literally seconds before posting this thread) I managed to fix it by adding GetComponent < Mover>().Cancel();

to my Mover script as shown:

        private void StopAttack()
        {
            GetComponent<Mover>().Cancel();
            GetComponent<Animator>().ResetTrigger("attack");
            GetComponent<Animator>().SetTrigger("stopAttack");
        }

Ok so it seems obvious to need to stop the nav agent for suspicion to work. By why did no one else encounter this? As I look through the course’s github repository it does seem like everyone should be encountering this. What am I missing?

OK - so this morning I decided to uncomment that line GetComponent< Mover>().Cancel(); and try again.

It worked as expected. I actually changed just one other thing - the suspicion time which was 2 seconds in my implementation I changed to the 4 seconds as per Sam’s video.

And then I figured it out.

Mover (as triggered by Fighter) keeps moving until it reaches its previously set destination. During this time the suspicion time clock keeps running. If it’s only 2 seconds, that time can be consumed during the last commanded move operation.

Honestly I struggled with this for hours. During the challenge phase I used " GetComponent< Fighter>().Cancel(); and couldn’t get it to work after an hour+. Then I watched the video and changed it to GetComponent< ActionScheduler>().CancelCurrentAction(); thinking that was the trick but again it didn’t work. I spent another hour++ And then I spent an hour+ this morning.

I’m not sure what the lesson learned here is other than it’s really important for the code to be debuggable. VERY weird stuff starts to happen when these time intervals start overlapping (e.g. suspicion time overlaps the time it takes for a character to walk). I think I much prefer state machines (or behavior trees which I am less experienced in) which seems far easier to debug.

Leaving this thread open for someone to comment if they have a good lesson learned out of all this.

EDIT: This also makes me want to implement my own logging class. This is what I did inside the AIController’s Update method to help catch this. A complete hack but it worked. I would love to have a reusable version of this. I’m thinking something more structured like a Behavior tree or FSM could offer this for free.

Again leaving this open for folks to comment with lessons learned I should take from this.

       private void Update()
        {
            if (health.IsDead()) return;

            if (InAttackRangeOfPlayer()  && fighter.CanAttack(player))
            {
                
                timeSinceLastLogChaseState += Time.deltaTime;
                if (timeSinceLastLogChaseState > logInterval)
                {
                    Debug.Log($"Chase Mode");
                    timeSinceLastLogChaseState = 0;
                }
                
                
                timeSinceLastSawPlayer = 0;
                AttackBehaviour();

            }
            else if (timeSinceLastSawPlayer < suspicionTime)
            {
                timeSinceLastLogSuspicionState += Time.deltaTime;
                if (timeSinceLastLogSuspicionState > logInterval) { 
                    Debug.Log($"Suspicion Mode with TimeSince {timeSinceLastSawPlayer} s    and max time: {suspicionTime} s");
                    timeSinceLastLogSuspicionState = 0;
                }


                SuspicionBehaviour();
            }
            else
            {
                GuardBehaviour();
            }

            timeSinceLastSawPlayer += Time.deltaTime;
        }

Yes, the original intention was that because the Fighter has set the current location of the target in every frame, if the target gets out of chase range, the Mover already has the last known position.

This means even though you outran the enemy, you still need to keep running far enough away that he doesn’t pick you right back up when he gets into range.

In a classic State Machine, we’d need to pass along or blackboard the last known position of the target to the SuspicionState to mimic the same behavior.

Thanks Brian. I definitely understand the current codebase now.

I’m very curious if you have any lessons learned that might apply to future work. I could foresee other weird bit of behavior cropping up - for example when we introduce ranged attacks, a certain attack range value might create unexpected behavior when combined with certain chase range values. I can’t help but think a minor bit of design up front would have us build the AI controller differently.

I know we’re doing stuff a certain way here because we’re all new at this and you’re trying not to introduce too much at once. That’s why I’m curious on the lesson learned for next time. I’ll add that recent experience (pre-gamedev.tv courses) have me extremely suspicious (no pun intended :wink: ) that a classic FSM is the right answer. But that means I’m not sure I know what pattern I would follow for my own work for coding up anything “state-machine-like.”

Actually, in my ideal prototype of the next version (no promises that this will ever be a new course), everything goes to state machines, but in a modular way, like for each enemy you’ll select certain default states in reaction to certain events within the statemachine.

In the current course, Archers are a weak link. If you get within range of an archer and he starts to shoot, take a step back and the archer will stop the shooting and move back into range… you can kite him like this for quite a while.

A simple solution is to have an Attack range, and an Abort range. Make the attack range say 8, make the abort range something like 10. For most archer animations that should allow the archer to finish the shot and then run to catch the player.

Because you have the benefit of directly engaging with so many students, I bet you’d make a pretty awesome advanced course (advanced as is for folks who finished 1-2 intermediate courses and don’t need to go over the basics). I should say “advanced” in quotes because I think they’d have broad appeal even to an indie developer making a simple game. I don’t mean advanced as in a AAA game developed by a studio.

That’s consistent with what I thought the solution could be. I’d make the chase range equal to the weapon range + a constant or the weapon range times a multiplier. I’d bet that for an RPG, a lot of the serialized field values we’re setting probably have some known relationship with each other and shouldn’t be set independently.

lol Brian! I see that a year ago someone had this similar issue (but in a later part of the course) and you recommended what I initially tried to here which is to have the Fighter script cancel the Mover script.

I’m going to put it back in my code because I could foresee having a similar issue crop up in the cinematic scenes.

You know what, I completely forgot about the effect that would have on suspicion behavior when I answered that question. There actually is a conflict between using Move’s destination as a “last known position” and the CinematicControlRemover.
A better solution for the CinematicControlRemover question would have been

player.GetComponent<Mover>().StartMoveAction(player.transform.position);
1 Like

Ah nice. That should do it.

Though this also feels like an example of how having named states (or behaviors) should make it easier to catch these sorts of issues up front.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms