How to calculate (inexpensively) if your object is facing close to global down

I am trying to create a parabolic arc with a projectile that will not wrap around when it goes below the elevation that it was shot at. I don’t know how to quickly calculate if the object is facing a threshold near global down. how do I check to see without other objects if the current object is CLOSE to facing global down?

Simplest (though probably not the fastest) would be something like (bullet.transform.forward.y < -0.9f)

float angle = Quaternion.Angle(transform.rotation, Quaternion.Euler(Vector3.down));

or something like that… can’t check it right now

ill look into that, If I can make an if statement like…

ill be set

if(angle !< 10)
{
transform.Rotate((drop * Time.deltaTime),0,0);	
}

Actually… that’s probably about the quickest you can get. You have no calculations occurring, just a comparison is done.

It’s just not the best for representing proximity. If you wanted to allow for configuring the margin to allow in, say:

…y < margin - 1.0f

where margin is in your example 0.1f

That margin isn’t linear, but instead trigonometric.

It’s also tethered directly to the ‘down’ vector, so if you wanted to allow for other directions, you’d require a completely different algorithm.

So yeah, it’s the fastest… just not the most dynamic.

LeftyRighty has a more open answer that allows for any axis, but using Quaternions has a bunch of extra steps.

You could in turn just use the dot product rule:

dot product is defined as:
A dot B = ||A|| * ||B|| * cos(theta)
where theta is the angle between A and B

note, ||A|| means magnitude of A

if our vectors are unit vectors, well ||A|| and ||B|| both equal 1 so we could reduce this to:

Au dot Bu = cos(theta)

reorganize

acos(Au dot Bu) = theta

here we now have a formula for the angle between two unit vectors:

var a = transform.forward; //get your forward vector, if this isn't it, get it however
var b = Vector3.down; //the axis you're comparing against

var angle = Mathf.Acos(Vector3.Dot(a, b));
if (angle < 0.1f)
{
    //do something
}

note - acos returns in radians, not degrees. So 0.1 is about 5.7 degrees.

Here’s a reusable static function I use for finding angle’s between. It uses magnitude though because it’s not guaranteed the values passed in are actually unit vectors.

        public static float AngleBetween(Vector3 a, Vector3 b)
        {
            return (float)Mathf.Acos(Vector3.Dot(a, b) / (a.magnitude * b.magnitude));
        }

The rest of your post is good, but I believe that’s incorrect. transform.forward has to transform a direction vector by the rotation (it’s a shortcut for “transform.rotation * Vector3.forward”). I can’t think of a way to avoid that transformation, true, but that doesn’t mean it’s free or that someone smarter than I could figure out a faster way.

Oh, and there is a Vector3.Angle function that is probably exactly the same as your custom static function :wink: Except in degrees.

LeftyRighty, your suggestion is wrong on two levels. First of all, that’s not how Quaternion.Euler works - you want to use Quaternion.LookRotation if you’re going to use Vector3.down. (Your code will give a rotation that is rotated 1 degree to the right) Second, you want to compare forward vectors, not rotations, in this case. For example, if I am facing the ground with the top of my head facing north, while you are facing the ground with the top of your head facing south, comparing quaternions will give us 180 degrees difference, even though our noses are pointing the same way and we want 0 degrees in that case. Hence, transform.forward.

I was mostly judging on the comparing the y value. Not how you got the forward. That’s why I say in my example code “however you decide to get your forward”.

And yes, Vector3.Angle appears to do the same thing my static method does. Didn’t know it existed… I ported my util classes over from other frameworks that didn’t have a Vector3.Angle method.

Thank you all for your help I will try these.

It is not perfect, but it works and I am not complaining. I time the drop by the angle to reduce the chance of overshooting true down. Have not had a weapon that overshoots yet, but I would not be surprised if it happened. Glad to be past this problem lol.

angle = Quaternion.Angle(Quaternion.Euler(transform.rotation * Vector3.forward), Quaternion.Euler(Vector3.down));

				if(angle > anglecheck)
				{
					transform.Rotate(((drop * Time.deltaTime) * angle),0,0);	
				}

the way I have this code written, angle goes between 0 and 2. 0 means its looking down, 2 means its looking up. I keep anglecheck around 0.3f. I am shooting thousands of bullets at one time with no slowdown.

That test is not a good test.

Quaternion.Euler creates a rotation from euler rotations (yaw, pitch, roll). Not a rotation in the direction of some vector.

You should not be using that method.

Hmmm… I would think that Vector3.Dot would work better than any of that.

if(Vector3.Dot(Vector3.down, transform.forward) > 0.9f) // We are facing almost down...