🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Dir of Travel on 3d Planet. I need math help here.

Started by
11 comments, last by TheCyberFlash 5 years, 7 months ago

I'm attempting to rework some code that I wrote early on and I'm not the best at math, so what I ended up doing to avoid figuring out a math problem that puzzled me for several days was as follows:

Problem: figure out how to get the correct angle of the players direction of travel relative to the "north pole" on my 3d planet... for generating a stinking simple compass display.

Honestly, I think I spent almost a full week trying to get this to work..  I looked up nearly every math heavy google result I could to try and make it work but I just couldn't wrap my head around enough of it to get it going without some trickery.  So, even though I've learned a LOT since then, I really don't want to spend hours and hours trying to look up the same problem that I really wasn't effective at solving on my own the first time.. haha

My Trickery:

1) I was having trouble figuring out how to implement the correct math for half of the problem(maybe the only real math part...).  So, instead, I created a child object of the player character and gave it this code:

(the code was not actually doing anything at all, so I've removed it..., see below example where the gymbal object rotation is set, the code here was doing similarly)..

My goal, and the effect, was to define a constant source of angle information so I didn't have to do the math. ;)  It works, quite effectively..  I just feel like there's a much much much easier(more efficient) way to do it that I can't sort out (mathematically IN c#)...

2) the second part of the trickery is using the data from the "gymbal" object instead of calculating the math directly.


//Code to determine player direction of travel NSEW, on a spheroid planet.

Vector3 point2origin = transform.position - planet.transform.position;  //origin is center of planet
Vector3 point2closestPointOnLine = point2origin - Vector3.Dot(point2origin, planet.transform.up) * planet.transform.up;

//(playerGymbal) == Generic game object parented to player character object(keeps it moving with the pc).
playerGymbal.transform.rotation = Quaternion.FromToRotation(-transform.up, point2closestPointOnLine) * transform.rotation;

//Here's the number I actually need.
float angle = SignedAngle(playerGymbal.transform.forward, planet.transform.up, playerGymbal.transform.position);

String nsew = "";

if (angle < -5 && angle > -85)
{nsew = "SE";}
if (angle <= 5 && angle >= -5)
{nsew = "S";}
if (angle > 5 && angle < 85)
{nsew = "SW";}
if (angle >= 85 && angle <= 95)
{nsew = "W";}
if (angle > 95 && angle < 175)
{nsew = "NW";}
if (angle >= 175 || angle <= -175)
{nsew = "N";}
if (angle > -175 && angle < -95)
{nsew = "NE";}
if (angle >= -95 && angle <= -85)
{nsew = "E";}
Dir.text = nsew;

So, ignoring the pile of ifs and I'm sure a few other nasty code smells, does anybody see my solution clearly?  I just want to simplify this process and get rid of the extra gymbal object and its Update loop. 

If I wasn't worried about optimization, or could use it as a helpful visual aid, I would just keep it because I think I clevered my way out of that problem pretty creatively..  But nope, it's just an invisible object using resources..

Anyhow, thanks for checking it out, any help would be greatly appreciated!

 

To summarize, I've got:

Planet's position in 3d space.

Player's position/rotation in 3d space.

North Pole's position in 3d space.

How do I calculate the resulting angles as above(or similar), simply, without all the extra trickery?

An important point:

The player character's "gymbal" object, always points towards the line running from the north pole to the south pole, and it rotates relative to the player's rotation, so that's how the extra object saved me from figuring out the math...

I failed to mention that anywhere above.. sorry.

Advertisement

Haha, so I guess the real question here is:

How do I get a ".forward" direction from this quaternion:

Quaternion.FromToRotation(-transform.up, point2closestPointOnLine) * transform.rotation;

I have a feeling there are a lot of ways of doing this, but here's an idea off top of my head:

  • If you have the player translate and rotate, then get the player transform, find the inverse (to get the player at the origin pointing in default direction)
  • Transform the north pole (or a higher up point) by this inverse transform to get it relative to the player
  • Use atan2 to get the yaw of the north pole relative to the players direction (in 2d)

There's probably also some cunning ways with cross products or something.

5 hours ago, lawnjelly said:

I have a feeling there are a lot of ways of doing this, but here's an idea off top of my head:

  • If you have the player translate and rotate, then get the player transform, find the inverse (to get the player at the origin pointing in default direction)
  • Transform the north pole (or a higher up point) by this inverse transform to get it relative to the player
  • Use atan2 to get the yaw of the north pole relative to the players direction (in 2d) 

There's probably also some cunning ways with cross products or something.

When you say "find the inverse"  you mean?  Because the only "inverse" I currently understand is an inverse transform relative to another object as below.

I'm already gathering the latitude and longitude with these 3 lines (directly preceding the above the code post). 


//Convert player's WORLD coordinates to be relative to the planet game object.
Vector3 pos = planet.transform.InverseTransformPoint(playerOwner.transform.position).normalized;

Lat = Math.Round(Math.Asin(pos.y), 3);
Lon = Math.Round(Math.Atan2(pos.x, pos.z), 3);

So, that gives me the pitch and yaw of the players Position, relative to the north pole..

I'm beginning to wonder if I actually need to fix this anymore..  I've removed the contents of the script that was attached to the gymbal object, so it's no longer costing me an extra update loop.

So, presently to get the angle I need, instead of performing the math directly, I'm rotating an empty game object so that its up direction is always pointing towards the closest point on the planet's NS axis line, it's forward angle tracks the player's forward angle simply by being a child of the PC, so then I check the signed angle between it's forward direction and this NS axis(planet.transform.up).

I guess it's still costing me extra compute cycles for the rotations and translations, so if I can figure out the math only solution I'll use it.

 

GuyOnWorld.jpg.8a98cc0ad7e3a6e4c6295a0c181fe45b.jpg

Try this:

Let A be a unit vector pointing from the center to the north pole (i.e. your world axis)

Let B be a unit vector from the center of your world to your guy. You can just take his coordinates, subtract the center of your world and then normalize to get B

Now......

C =  Normalize( CrossProduct(A,B) )   

D = CrossProduct( B, C )

D now should point north from your guy.

Now you need a unit vector that points the way the guy is facing. Let's call it F

So now you want to find a signed angel from North (D) to F (call it AN). I think it's something like:

AN = atan2( DotProduct( B, CrossProduct( D, F) ), DotProduct( D, F) )

Hopefully I didn't mess up anywhere.

 

Thanks @Gnollrunner!  Now that's spelling it out.. (assuming you didn't mess up anywhere, haha)

I will test it out straight away and let you know what I find!

7 minutes ago, Septopus said:

@Gnollrunner(assuming you didn't mess up anywhere, haha)

 

If I messed up, it's in the atan2 part. I'm pretty sure the the rest is right.  Keep in mind that you'll need to have a special case at the poles because the cross product wont work, but it kind of makes sense. For instance which way is north at the north pole? There is no north. And which way is north at the south pole? All directions are north. So it's a special case in real life too.

Yeah, I'm sure this will probably get me there, it vibes with the stuff I was googling when I was trying to get it working initially, but I just couldn't sort it all out.  Thanks!  Let you know in a few.

Okay, well here's my quick and dirty c# translation.  NICE MATHING @Gnollrunner!!! :D


Vector3 playerVec = player.transform.position - planet.transform.position;
Vector3 northVec = planet.transform.up;
Vector3 c = Vector3.Cross(playerVec, northVec);
Vector3 d = Vector3.Cross(playerVec, c);
Vector3 f = player.transform.forward;
double rads = Math.Atan2(Vector3.Dot(playerVec, Vector3.Cross(d, f)), Vector3.Dot(d, f));
double deg = (180 / Math.PI) * rads;

Debug.Log(deg.ToString());

All I had to do was realize it was returning radians and not degrees.  Perfect!

I'm sure Gnollrunner's approach sounds good too, that was the kind of thing I was hoping for with cross product but didn't have the brains to workout lol. :)

Sorry I didn't properly read your code as I couldn't work out easily what was going on, it's the kind of thing that diagrams are good...

But the approach I was suggesting works like this:

  • If you imagine holding a compass, you might hold it relative to your body, and the needle is pulled towards wherever north is. So everything is relative to your body.
  • If you know your player position and rotation (in world space), presumably you can create or have a transform matrix that will transform your player from model space (centred on the origin pointing along your default axis) to world space. You know the position of north in world space. So in order to get the position of north RELATIVE to your player, you have to simply back transform the north point from player world space to player model space.
  • So you simply take your player transform matrix, invert it (this is standard matrix operation), multiply your north point by this, to get the north point in 'player relative' space. You can then take the x and y coords in player relative space and use atan2 to get a yaw (relative to the player heading), as you would for finding a yaw in any 2d game.

This is working pretty much exactly how a compass works in real life I think. There will also be points right around north pole and south pole where there is no 'north', just as in Gnollrunner's solution.

 

This topic is closed to new replies.

Advertisement