Using navMesh stoppingDistance

In the prior challenge I used the navMesh’s stoppingDistance to figure out when to stop (as opposed to manually computing a distance). It turns out I was too clever by half as it turns out to be a liability here. Couldn’t find any great way to figure out if navMesh is actively trying to navigate without majorly changing other parts of the code.

At this point I realized some major refactoring is probably coming down the line soon so I did it the way Sam the instructor did it.

My current/temporary conclusion is that it’s probably better to calculate stopping distances yourself rather than use the built in stoppingDistance. This course definitely has me intrigued. I am glad I am taking it.

1 Like

In my case, I have done a simple “raycast” check between the character and the target (if any target are selected/targeted).

Since I know that I got quite a few bit of code here and there that might need some sort of converter or checker or something else, I usually setup a specific script called “Convert.cs” which includes any kinds of coding tools that I might need multiple time.

For the distance, I have written this bit of code:

    public static bool CheckForObstatcles(GameObject Source, GameObject Target, float Range, RaycastHit HitRayCache)
    {
        if (Physics.Raycast(Source.transform.position, (Target.transform.position - Source.transform.position), out HitRayCache, Range))
        {
            if (HitRayCache.transform == Target.transform)
            {
                return true;
            }
        }
        return false;
    }

On my Combat.cs script (well, mine is actually called ActionHandler.cs because it handle both combat and interactions with the stuff), I’m using this kind of function to check if the character can attack;

        [SerializeField]
        private float AttackRange = 1f;
        [SerializeField]
        private float AttackSpeed = 1f;

        private RaycastHit HitRay;
        private GameObject CurrentTarget;
        public int Attack(GameObject AttackTarget, float AttackDuration, bool ForceAttack)
        {
            CurrentTarget = AttackTarget;
            if (Vector3.Distance(CurrentTarget.transform.position, gameObject.transform.position) > AttackRange)
            {
                Debug.Log(CurrentTarget.name + " is outside of the attack range.");
                return 0;
            }

            if (ForceAttack || Convert.CheckForObstatcles(gameObject, CurrentTarget, AttackRange, HitRay))
            {
                Debug.Log(gameObject.name + " is attacking " + CurrentTarget.name);
                //Attack animation here.
                AddWaitTimeToNextAction(AttackDuration / AttackSpeed);
                return 2;
            }
            else
            {
                Debug.Log(CurrentTarget.name + " is hidden from " + gameObject.name + " attack");
                return 1;
            }
        }

This simple bit of a function allows me to know if the attack is within range or if the target is within range or hidden or if the attack animation can be played and so on. The AddWaitTimeToNextAction(float) is a cached (and singled) coroutine that allows me to define if the character is doing an action or not so that I don’t have conflict such as if the character is activating a level, I wouldn’t want it to attack at the same time. (Yeah, this requires me to basically check and set a time values for all actions, but it also avoid me having to use Animation Events as those can be pretty inaccurate when overlapping or in transitions.)

The “ForceAttack” boolean allows me to overlook any obstacles (without even having to do raycast because of the order in the if statement). For example, it could be used for a character cast a spell like a lightning strike coming from above to an enemy behind a barricade or in situation where you want the action to be caste regardless of the obstacle because, maybe, the action results in an AOE effect or you want to allow the player to shoot a projectile even if the target is hidden behind a cover.

If you wonder why I have a RaycastHit variable called HitRay in the Combat script being “send” (a.k.a. feed) to the Convert.CheckForObstatcles() function, that’s something you do when you want to avoid generating multiple instance/reference of a Raycast (or any other parameters) when calling a static function.

If you add something like RaycastHit RayHit; directly in the static function, you generate a new ID-ed single-use Raycast block in the memory allocation each time you call the function. A RaycastHit contain all of the following values in its cached data which is generated every time you cast it once. If you cast it once per frame and run the game at 60 FPS, you get 60x that amount of data in the cache per sec which has to be digested by the Garbage Collector (the thing that clear up the unused bytes from the memory allocated blocks)

By allocating the parameter “outside” of the function in a script, you basically tell the memory allocation system to only overwrite that existing ID and never create a new one so even if you call a function that update that value 60 times per sec, it wouldn’t record more than 1 block of the data in the memory and just keep reusing that same block again and again until it’s not used anymore (such as if the scene changes).

Privacy & Terms