How can I optimize my range tests?

My enemies could find me from anywhere so I figured I should only do it in a certain range. I get a lot of lag with this way so I am asking is their a better way? (Less lag or If I am just putting the code in the wrong place)

if ( Vector3.Distance(objPlayer.transform.position,transform.position) < Enemyrange)
{
    inputMovement = objPlayer.transform.position - transform.position;

    // face the direction we are moving, towards the player
    inputRotation = inputMovement; 
    if ( Vector3.Distance(objPlayer.transform.position,transform.position) < 1.2f )
    {
        meleeAttackState = true;
        inputMovement = Vector3.zero;
        if (currentAnimation != animationMelee) 
        { 
            frameNumber = meleeAnimationMin + 1; 
        }
    }
}

The Vector3.distance is to do with enemyrange the other is for if they are in range to attack (the one for attack worked with no lag before I added the finding player range.

At the top of the script I have

public float Enemyrange = 0.0f;

In which I set an amount depending on the enemys sight.

Cheers,

  1. Profile your code to find what's sucking up most CPU.
  2. Remove redundancy by caching results to expensive calls.
  3. Use sqrMagnitude rather than Magnitude or Distance if you don't need the exact magnitude.
  4. Consider using a multithreaded manager to utilize all CPU cores if you have lots of objects.
  5. Consider using spatial culling if appropriate to your project.

Be very sure about what is causing the lag. Use the profiler (Pro only) if you haven't already.

Otherwise you might end up trying to fix the wrong thing.


First off you're calculating distance twice and you probably don't need to do that. You can calculate it once and save the results:

// Calculate distance once!
var distance = Vector3.Distance(objPlayer.transform.position,transform.position);

if ( distance < Enemyrange)
{
    inputMovement = objPlayer.transform.position - transform.position;
    inputRotation = inputMovement; 

    if ( distance < 1.2f )
    {
        meleeAttackState = true;
        inputMovement = Vector3.zero;
        if (currentAnimation != animationMelee) 
        { 
            frameNumber = meleeAnimationMin + 1; 
        }
    }
}

Another point to mark out is that you don't really need the distance information to do this test. It would actually be faster to work with square distances instead. And by doing so we can also reduce another calculation in the go that is required anyways:

// Get the delta (difference) vector between player and this object.
var delta = objPlayer.transform.position - transform.position;

// sqrMagnitude is cheaper to use than Magnitude since it doesn't have
// to find the root of the square magnitude.
var sqrDistance = delta.sqrMagnitude;

// Note: These two variables probably should be 
// cached in Start and doesn't need to be calculated
// on the fly...
var sqrEnemyRange = Enemyrange * Enemyrange; 
var sqrMeleeRange = 1.2f * 1.2f;

if ( sqrDistance < sqrEnemyRange)
{
    // We no longer need to calculate inputMovement since we already did that 
    // for the range check!
    inputMovement = delta;
    inputRotation = delta; 

    if ( sqrDistance < sqrMeleeRange )
    {
        meleeAttackState = true;
        inputMovement = Vector3.zero;
        if (currentAnimation != animationMelee) 
        { 
            frameNumber = meleeAnimationMin + 1; 
        }
    }
}

If you have a ton of objects and you aren't utilizing all processor cores, it might be wise giving that a shot since it can significantly boost performance on 2+ core processors. In essence you could create as many background worker threads as there cores and give each worker an equal portion workload to crunch through. Note that there is some overhead using threads so it's not suitable for small workloads. Note that threading in itself is error prone if you don't watch what you are doing. Do not attempt multi-threading until you have solid knowledge about it and feel confident it won't break the rest of the project.

Then there are better ways to perform range tests. The first topic you should learn about if you're having a lot (thousands or even more) of these checks is "Spatial Culling". Spatial Culling comes in many forms and I think you could benefit from a Quadtree or Octree for these range checks. Basically what this does is place each of your items into subdividable buckets. A bucket would be a 2d area or 3d volume. Subdividable in this context means that you can split one bucket into several smaller buckets.

Once all your items are in these buckets, you can perform neighborhood tests. Basically you check the buckets in range first. Does they contain anything? If not, then you don't need to perform any further checks because if nothing are in the immediate buckets then you certainly don't need to perform any distance checks.

Quadtree visualisation

Quadtree visualisation. Image from Wikipedia.

It is not immediately clear if this would benefit your solution. It might actually add more overhead. Are you sure that these checks take up most time? Have you profiled the code? Could it be that you're using GameObject.Find("Player") for each call or something trivial like that? Because that just adds extra overhead.


Profiling without Pro

You are probably better off finding a third party profiling tool (such as this on the wiki), or write one yourself rather than manually testing your performance! It's not that hard. Basically you need to sample the current time before and after your algorithm, with very high precision timers (if you google you shall find).

If you lack pro and don't have access to profiler, try and rule out exactly what is consuming most time by selectively disabling some pieces of code (and possibly replacing it with bogus constant values). That is one "fast" way to deduce what calls are slowing your game down. For example you could try to remove the calls to Distance all together and pass in a single constant number. Of course this won't work for a real game but it'll help check if optimizing Distance would make an impact at all. If you see no difference, then your Distance checking code isn't the problem. Do this for all your suspicious lines of code to narrow down on the time consumer. This manual labor is error prone and it's hard to deduce if you made any improvements at all just looking at it run.