Why does my script not work when i turn from off to on?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class JubayerAi : MonoBehaviour
{
    float distance;
    public AudioSource jubuWarning;
    Transform nearestWayPoint;
    public float JubuWarningTime;
    public float jubuWalkDuration;
    public float searchDuration;
    public Transform RandomPoint;
    public Transform[] wayPoints;
    public Animator anim;
    public float ChaseSpeed;
    public float WalkSpeed;
    public float sightDistance;
    public float JubuSpawnRate;
    public bool isChasing, isSearching, isWalking, isInSight;
    public bool Spawn;
    public NavMeshAgent Jubayer;
    public GameObject Player;
    public GameObject JubayerObj;
    public GameObject JubayerWhole;

    void AlertPresence()
    {
        jubuWarning.Play();
    }

    public void StopChase()
    {
        isInSight = false;
        isChasing = false;
        isWalking = false;
        isSearching = true;
    }

    void JubuChase()
    {
        if (isChasing)
        {
            if (Jubayer != null)
            {
                anim.SetTrigger("chase");
                Jubayer.SetDestination(Player.transform.position);
                Jubayer.speed = ChaseSpeed;
            }
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player" && Player.GetComponent<PlayerMove>().isHiding == true)
        {
            StopChase();
        }
    }

    void Jumpscare()
    {
    }

    IEnumerator JubuSearch()
    {
        anim.SetTrigger("search");
        Jubayer.speed = 0;
        yield return new WaitForSeconds(searchDuration);
        isSearching = false;
        isWalking = true;
    }

    IEnumerator JubuWalk()
    {
        anim.SetTrigger("walk");
        Jubayer.speed = WalkSpeed;
        Jubayer.SetDestination(RandomPoint.position);
        yield return new WaitForSeconds(3);
        isWalking = false;
        JubayerObj.SetActive(false);
        JubayerWhole.transform.position = new Vector3(100, 100, 100);
        Spawn = true;
        Jubayer.enabled = false;
        StartCoroutine(JubuSpawner());
    }

    void JubuDie()
    {
        anim.SetTrigger("die");
        Destroy(this);
    }

    void Start()
    {
        Jubayer.enabled = false;
        JubayerWhole.transform.position = new Vector3(500, 100, 100);
        JubayerObj.SetActive(false);
        Spawn = true;
        StartCoroutine(JubuSpawner());
    }

    private void Update()
    {
        float shortestDistance = float.MaxValue;
        NavMeshPath path = new NavMeshPath();
        for (int i = 0; i < wayPoints.Length; i++)
        {
            if (NavMesh.CalculatePath(Player.transform.position, wayPoints[i].position, NavMesh.AllAreas, path))
            {
                distance = 0;
                if (path.corners.Length > 0)
                {
                    distance = Vector3.Distance(Player.transform.position, path.corners[0]);
                    for (int j = 1; j < path.corners.Length; j++)
                    {
                        distance += Vector3.Distance(path.corners[j - 1], path.corners[j]);
                    }
                    if (distance < shortestDistance)
                    {
                        shortestDistance = distance;
                        nearestWayPoint = wayPoints[i];
                        Debug.DrawLine(Player.transform.position, wayPoints[i].position, Color.red);
                    }
                }
            }
            else
            {
                Debug.DrawLine(Player.transform.position, wayPoints[i].position);
            }
        }
        Vector3 direction = (Player.transform.position - transform.position).normalized;
        RaycastHit hit;
        if (Physics.Raycast(transform.position, direction, out hit, sightDistance))
        {
            if (hit.collider.gameObject.tag == "Player")
            {
                isInSight = true;
            }
        }
        else
        {
            isInSight = false;
        }

        if (isInSight && Player.GetComponent<PlayerMove>().isHiding == false)
        {
            isChasing = true;
        }

        if (isChasing == true)
        {
            isSearching = false;
            isWalking = false;
            JubuChase();
        }
        if (isSearching == true)
        {
            isChasing = false;
            isWalking = false;
            StartCoroutine(JubuSearch());
        }
        if (isWalking == true)
        {
            isChasing = false;
            isSearching = false;
            StartCoroutine(JubuWalk());
        }
    }

    IEnumerator JubuSpawner()
    {
        if (Spawn)
        {
            yield return new WaitForSeconds(JubuSpawnRate);
            AlertPresence();
            yield return new WaitForSeconds(JubuWarningTime);
            Spawn = false;
            JubayerWhole.transform.position = nearestWayPoint.position;
            Jubayer.enabled = true;
            JubayerObj.SetActive(true);
            isChasing = true;
        }
        JubuSpawner();
    }
}

i know its big and long and messy but i’m a noob tho. when i start the game with jubayer(the gameobject where the script is attached to) on it works fine. it spawns in the nearest waypoint to the player but when i turn it off and then start the game and then some milliseconds or seconds if i turn jubayer on it will for some reason not be able to do this line( if (NavMesh.CalculatePath(Player.transform.position, wayPoints[i].position, NavMesh.AllAreas, path))) and be stuck there forever. the waypoints aren’t children to jubayer. help would be appreciated by alot… i’ve been added a debug log before the line if (NavMesh.CalculatePath(Player.transform.position, wayPoints[i].position, NavMesh.AllAreas, path)) it was only stuck on displaying the debug log lol. if you don’t wanna read such long code then it’s okay.

Do you get any errors in the console?

Also… what do you mean by this?

it was only stuck on displaying the debug log lol.

Reading through this code and narrowing in on the problem is sort of hard so getting more information would be helpful.

In unrelated news, I have some suggestions for you.

  1. organize your code into related blocks, you can even use the #region to help with that

  2. you don’t need to test == true for bools, you can just say: if (isWalking) …

  3. you don’t need to define EVERY variable at the class level. For example the variable ‘distance’ is declared at the top of the class but it’s only used in the Update function. This isn’t saving you anything and again only serves to make your code harder to read (class level variables should consist of the variables that reprsent the object’s state)

  4. I’m assuming that isSearching, isWalking, and isChasing are some state machine like functionality where only 1 is true at any given time. You then have this if chain seeing which state you’re in to define what to do. You could instead create an enum like:

enum States
{
    IsSearching,
    IsWalking,
    IsChasing,
}

private States currentState;

Then you can just use a switch rather than the if chain, and be guaranteed you don’t accidentally set 2 of those flags true at any given time.

switch (currentState)
{
    case States.IsSearching:
        //do searching stuff
        break;
    case States.IsWalking:
        //do walking stuff
        break;
    case States.IsChasing:
        //do chasing stuff
        break;
}

Anyways… just a little feedback.

Get back to me with some more info about your problem though if you can, maybe I can help.

2 Likes

thanks alot for your amazing reply. so the thing is that when i keep jubayer(the gameObject where the ai is attached to) turned on in the inspector and then start the game it works absolutely fine. but when i turn off jubayer and start the game and then after a few seconds turn jubayer on again it doesn’t work like it fails. the problem is that NavMesh.calculatePath becomes false for some reason. i will send you a video later.

just to let you know i have solved my problem
by increasing the navMesh height

1 Like

I’m happy you got it resolved!

But doesn’t declaring variables at the class level improve performance?

No it doesn’t improve performance just because you’ve declared them at the class level. I have seen this myth bandied about in many places, I have even heard people claim their professor told them it is so.

It is not so.

What this is usually a conflation of is what is actually saving you performance.

Creating new instances of objects repeatedly when you could have used just 1 is costly. Lets say during your ‘Update’ method (runs repeatedly) you create a ‘new List’ fill it up with stuff and then sort out certain targets, then when you’re done you just toss the list out. You’ve cost your self creating a new list every time Update is called, the GC with throwing it out, and all that. Where as if you declared a class level List that you recycled would save you performance (note - there are threading concerns here, but that is out of the scope of this thread, and your Unity MonoBehaviour is likely not threaded).

Caching of objects that are non-trivial to get references to relative to the frequency of accessing them. Again say you have an Update method where you call ‘this.GetComponent’ to pull a reference to say a Collider attached to the same GameObject your MonoBehaviour is attached. GetComponent is fastish, but Update happens frequently, you might as well cache the reference.

In both these instances it’s not the variable being class level that saved you performance. It’s that the work done which is then stored in said variable is being saved. We’re only creating 1 list/object, or we’re only calling GetComponent once, rather than multiple times. The class level variable is incidental to this because you need somewhere to store the result of that work.

But declaring an int, or a float, or some other value type that gets temporarily used. These don’t cost anything. You’re not allocating space on the heap, you’re not saving some non-trivial task of finding a reference.

public Transform lookAtTarget;
float d;
void Update()
{
    d = Vector3.Distance(this.transform.position, lookAtTarget.position);
    if (d < 10f)  //do a thing
}

vs

public Transform lookAtTarget;
void Update()
{
    float d = Vector3.Distance(this.transform.position, lookAtTarget.position);
    if (d < 10f)  //do a thing
}

You’re not saving anything here. The cost of calculating the float (performing the distance calculation) occurs regardless. You’re just unnecessarily making the code weird to read.

I should point out someone could try to make some argument that they’ve saved having to allocate an extra 4 bytes of space for the stack frame associated with this function call. And it technically is true that you’ve saved that 4 bytes from being allocated… but is that really faster? A stack frame still needs to be allocated is this 4 byte difference in size REALLY that much performance?

If it is… what of the 4 bytes larger the object is on the heap? Our class instance now takes longer to instantiate on the heap, and to be cleaned up on the heap. This happens less frequently so it doesn’t matter?

But is the difference, and we’re talking miniscule differences if they even exist, really matter in the grand scheme? Or does readable code that portrays the intent of your logic matter more?