Interesting Patrol Path Problem

Alright this is an interesting one. Out of my Respawn Manager and insane other systems I have integrated in my RPG, my Quest Giver is just a normal one that follows a normal Patrol path (which I also tuned to my needs… :stuck_out_tongue_winking_eye: )

My problem is, I just realized the Patrol path is only followed and works perfectly fine when my player is out of the path, but whenever he interrupts (i.e: he gets into the path. Not necessarily on a stopping point, just get into the path itself), the Patrol path vows not to work until I get out of it. I want my character to follow the patrol path out, independent of whether my player is in it or not (and if we can get him to stop walking when we talk, and then continue walking when we walk away or end the conversation, that would be amazing) How do we make him reach his destination regardless of whether my player is in his Patrol path or not? (i.e: I don’t want my player getting in the way stopping the entire process)

Not exactly. The Patrol Path is nothing more than data. It’s the AIController that follows the patrol path…
The problem lies in the AIController. When the AIController detects that the player is in range, instead of staying in PatrolState, it goes into CombatState, but the Fighter is deactivated, so the patrolling player stands around milling about. He works by the hour, he doesn’t care.

What we need is a way to avoid attacking if the Fighter is disabled… I think the best place for this is in the IsAggrevated() method. Simply add

if (!fighter.enabled) return false;

to the beginning of the method and the Ai Controller should carry on instead of stopping for the player when the fighter is disabled.

The second part is a bit trickier… but not as much as you might thing…
The PlayerConversant has a method called IsActive(), which simply returns true if the PlayerConversant is speaking with an AiConversant…
Let’s add another helper method to PlayerConversant:

public AIConversant GetCurrentConversant() => currentConversant;

From this, we can compare the currentConversant’s GameObject to our GameObject. If they are the same, then we should stop walking (and probably face the player). This, I would put between the check for health and the check for attack behaviour in the AIController…

And that's your challenge:

Test to see if all of these conditions are met:

  • This character has an AIConversant (I would create a bool isConversant in my global variables)
  • The playerConversant is currently active (playerConversant.IsActive())
  • The playerConversant’s GetCurrentConversant’s GameObject is the same as our GameObject.

If all of those conditions are met, then you should return. I suggest caching the playerConversant as well to avoid all those annoying GetComponent calls in an Update loop.

and… it didn’t work as expected, to stop the guard to talk when I talk to him (he does patrol regardless of my players’ existence though, so that’s good). Here’s what I did:

in ‘PlayerConversant.cs’, I put this line:

// Current Conversant, which will deactivate the NPC walking around when our player starts talking to him:

        public AIConversant GetCurrentConversant() => currentConversant;

and in AIController.Update() method, I put this line:

if (health.IsDead()) {
            return; // If the NPC is dead (OUT OF HEALTH), don't do anything else (cancel this script)
        }

        if (isConversant && playerConversant.IsActive() && playerConversant.GetCurrentConversant()) return;

        // Chase the player if he's in the Attack Range of the AI Enemy, and available to fight
        if (IsAggrevated() && fighter.CanAttack(player))
        {
            AttackBehaviour();
        }

I placed the surrounding AttackBehaviour check and health.IsDead(), just to highlight where I put my check to stop my guard to talk if we speak (P.S: all of this is in the ‘AIController.Update()’ function).

Edit 1: I went and marked my QuestGiver as well as ‘isConversant’, it just returned a Null Reference Exception error, and the guy just won’t patrol now

I still have a few issues here with the guard ignoring my talk attempts though. Any way we can fix this? I think my challenge solution was not sufficient

@Brian_Trotter this is the Patrolling issue you requested me to tag you in :slight_smile:

By now, you should know I’m going to want to know what line the null reference is on… :slight_smile:

if(isConversant && playerConversant.IsActive() && playerConversant.GetConversant().gameObject==gameObject)

That last clause needs to test to see if the player is talking to this character.
I believe the reason the character keeps moving is because the Mover still has a destination… So it might be better to do a little cleanup in this if clause

if (isConversant && playerConversant.IsActive() && playerConversant.GetCurrentConversant.gameObject = gameObject()) 
{
    GetComponent<ActionScheduler>().Cancel();
    transform.LookAt(player.transform.position);
}

The NRE disappeared recently (apologies, forgot to update that earlier), and I’m getting some yellow warnings instead (which don’t seem to be bothering the game)

But this block does not get my guard to physically stop, for some reason. It gets him to look at me, play the idle animation, but he’s still moving to his next waypoint, even with the idle animation playing (as if something else is triggering his Movement behavior):

if (isConversant && playerConversant.IsActive() && playerConversant.GetCurrentConversant().gameObject == gameObject) {

            GetComponent<ActionSchedular>().CancelCurrentAction();

            transform.LookAt(player.transform.position);

        }

Edit: Tried playing with the code and going through other functions that might help as well, still can’t figure it out…
Edit 2: Tried integrating the ‘Cancel’ function from ‘Mover.cs’ as well, and that failed too…

That may be because I left something out of my code by accident that was the result of your original if statement… We don’t want to continue past that if block if we’re the active Conversant, so we probably want a statement at the end of that block after transform.Lookat(player.transform.position)…

a simple ‘return’ did the job :sweat_smile: - thanks again Brian

OK I really hope you’re not mad at me yet, but I did realize that whilst this algorithm works well for non-respawnable characters, like my Quest Giver, it just flat out fails for my Respawnable Characters, like my Dialogue Guard. I tried fixing it, but that… failed terribly. Here’s what I tried adding to my ‘RespawnManager.cs’ to fix it (and it didn’t work):

3 Extra variables:

// Variables for code that ensures that our Dialogue Respawnable NPCs stop if our player tries talking to them (DELETE IF FAILED):
        [SerializeField] bool isConversant;
        [SerializeField] PlayerConversant playerConversant;
        [SerializeField] GameObject player;

and a new ‘Update’ function (because putting that in ‘Respawn’ failed:

private void Update() {

            // TEST CODE (to ensure patrol Dialogue NPC stops to talk to player if player talks to them): Delete if failed
            if (isConversant && playerConversant.IsActive() && playerConversant.GetCurrentConversant().gameObject == gameObject)
            {

                GetComponent<ActionSchedular>().CancelCurrentAction();
                transform.LookAt(player.transform.position);
                return;

            }

        }

Can we fix this? The main problem is… the guy won’t stop to talk (no issues of him following the patrol path when we enter it)

I’m honestly not sure we can. None of the code you just posted goes in RespawnManager, it has to be in AIController.cs.

Don’t try to serialize any of these variables. They should be discovered at runtime by the AIController

bool isConversant;
PlayerConversant playerConversant;
//You should already have the player cached in AIController
void Awake()
{
    //all your Awake code then
    playerConversant = player.GetComponent<playerConversant>();
    isConversant = TryGetComponent(out AIConversant conversant);
}

The Update code belongs in AIController’s update, not RespawnManager’s update.

Privacy & Terms