1
\$\begingroup\$

I have a space fighter AI. I need to determine how to pitch, yaw and roll the craft to aim at the target.


My current solution:

To obtain the Yaw angle to position the target above the fighter, here are my steps:

  1. Project the target vector onto the local XZ plane of the fighter.
  2. Vector3.Angle(transform.forward, projectedVector)

The problem is that Vector3.Angle always return a positive angle

^ T        T ^
|/          \|
F            F

(F - Fighter, ^ - forward, T - Target)

both of these return the same angle

My current solution is to

  1. Cross product transform.forward and targetYawVector
  2. Dot the result against transform.up
  3. if < 0, its one direction, where > 0 its the other. (switchable)

Same is done with roll and pitch

Issue:

This current solution does too much computation. Doing a stress test with ~300 units slows the simulation down to sub 30 FPS on a 3 year old computer. (I am assuming that players will have older computers)


Question:

Is there a better way to obtain the direction of an angle without needing to do all these?

\$\endgroup\$
  • \$\begingroup\$ Are you just trying to decide whether to turn left or right? Why not just Vector3.Dot(target.position - transform.position, transform.right) > 0 ? TurnRight : TurnLeft - no need for angular trig calculations. \$\endgroup\$ – DMGregory Oct 4 '16 at 16:09
1
\$\begingroup\$

For a direction on an angle in 3D space you need to pick what positive and negative mean in your context.

One simple method I've used in the past is to simply multiply the results of Vector3.Angle by the sign of dotting the target vector to the direction I want to be positive. For example if we assume the situation you described is top down:

^ T          T ^
|/            \|
F->            F->

(F - Fighter, ^ - forward, T - Target, > - right)

And your vectors are cast to the XZ plane as your first step describes then you can simply use

signedAngle = Vector3.Angle(transform.Forward, targetVector) *
              Mathf.Sign(Vector3.Dot(transform.Right, targetVector))

Mathf.Sign returns 1 if the value is >= 0 and -1 if it is < 0 so when multiplied with the result of Vector3.Angle this should give you an value from (-180, 180] which sounds like what you want.

Another nice thing is since we only care about whether the dot is positive or negative and the angle is likewise unaffected (IIRC) we can get away without using normalized vectors, which I mainly call out as you mentioned performance issues.

Many times when I've seen performance issues on code using a lot of vectors, too much unnecessary normalization has been to blame or a good first suspect as it involves a number of square roots. While I do not know what your code looks like you should try to only normalize a vector once in a block of code, assuming you need it. A gotcha to pay attention to is that the vector.normalized property will re-normalize the vector every time it is called, so if you need the normalized value frequently and are seeing performance problems it can help to cache the result and use that instead.

\$\endgroup\$
0
\$\begingroup\$

I can guarantee you that one extra vector projection per frame for 300 units will not slow down your game. You could probably run thousands and thousands of these without noticing a performance dip. Your issue is elsewhere. To find out where you will need to do some profiling. Thankfully this is really easy since Unity 5.0 as you now have access to the profiler even as a free user.

Go to Window -> Profiler to open the profiler window.Make sure "Record" is pressed and then play your game. You'll now be able to see what method is hogging your resources which may not be processor time, maybe you're running out of memory or have some other bottleneck.

\$\endgroup\$
  • \$\begingroup\$ The profiler indicate a 25 ms total calculation time (60% total) under the Update function. The only thing that is happening there is 6-7 of these calculation, depending on the state of the AI, and one physics overlap sphere. The physics overlap is unavoidable due to collision avoidance. \$\endgroup\$ – DarkDestry Jun 15 '16 at 21:06
  • \$\begingroup\$ Have you stepped into the methods to see which methods called by Update are actually eating up the time? \$\endgroup\$ – Mikael Högström Jun 16 '16 at 7:59
  • \$\begingroup\$ The only thing listed under there is physics calculation taking up 4-5 ms of the 25 ms calculation time \$\endgroup\$ – DarkDestry Jun 16 '16 at 8:33
  • \$\begingroup\$ Try commenting out the code you think is causing the issue and run again. Does this have significant effect on the calculation time? \$\endgroup\$ – Mikael Högström Jun 17 '16 at 8:47
  • \$\begingroup\$ Snipping out code only to obtain angle direction resulted in a drop from average 19 ms to average 14 ms. (I did some optimization to the obstacle avoidance code and thus the drop from 25 ms to 19 ms) \$\endgroup\$ – DarkDestry Jun 17 '16 at 23:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.