If you’re fine by just basing it off the position property, you almost have it. Do note, what I’m about to get into defines if the origin (the 0,0,0 point INSIDE the gameobject) is above or below another. If this point is in a weird place relative to some mesh in the gameobject… well it won’t pay attention to said mesh. In this case, you should maybe use bounds and base it off that… or use the collision system which is even more precise. But they’re just basically the same mathematical concept I’m going to cover, just on a more granular level that compensates for the location of any meshes.
Vector3.Angle gives the closest angle between two vectors. It’s based off of the definition of a dot product.
a dot b = ||a||*||b||*cos(theta)
//where theta is the angle between a and b
// ||a|| means ‘magnitude of a’
it can be reversed to be
acos(a dot b / (||a||*||b||)) = theta
//this is essentially what Vector3.Angle does
Anyways, dot products are nice in that they measure a vector projected onto another vector:
The result is a scalar (number) that represents the length of that |------------------|.
If that length is negative it points the opposite direction of the other.
SO, the XZ plane is defined with the plane normal of <0,1,0> (const in unity is Vector3.up).
So we can project onto this normal a vector pointing from A to B. (not the same A and B in that image, A is one transform, B is the other)
If the result is positive, then the vector is pointing near the same direction as up, so it’s pointing upward, which means B is above A.
If the reuslt is negative, then the vector is pointing in the near opposite direction of up, so it’s pointing downward, which means B is below A.
//returns the transform who is above the other
//3rd optional param to define up, in case you're in a world where up changes
public static Transform FindWhoIsAbove(Transform a, Transform b, Vector3 up = Vector3.up)
{
return (Vector3.Dot(b.position - a.position, up) <= 0) ? a : b;
}