Search Unity

Look At object while aligned to surface

Discussion in 'Scripting' started by B2_Squared, Feb 4, 2018.

  1. B2_Squared

    B2_Squared

    Joined:
    Mar 13, 2013
    Posts:
    14
    I've been trying to solve this for a while now and am at the end of my wits. It's about time I ask the community for help on this one.

    I'm making a side-scrolling game that takes the player through 3d spaces. That is to say, there is only left and right movement, but the player will be following a path. Think of it like a car that has a go-pro mounted to a self stick taped to the side of it.

    I've hit a bit of a snag with my way-point system. I want my controller to be robust and able to handle all sorts of orientations and positions. The player himself will be aligned to the ground normal, so on inclines, the player's up axis will not be pointing along the world's up axis.

    Way points on flat surfaces are not an issue. The controller works perfectly on those. It's when the player is on an incline that the waypoint orientation begins to fail. I've attached a picture to showcase the problem.



    The controller only rotates the object during one frame, when the player has passed the current Nav Point. My process has been to get the objects up direction by raycasting onto a plane that is a child object of the player/object (this way point code I plan to use on multiple objects). From there, I use LookAt to rotate the object to look at the next nav point, then I use FromToRotation to align the player/object's up transform to the normal I had stored before.

    It comes close.... but it's not precise enough! At longer distances, the failure begins to show big time. The player could be several meters away from the next nav point. Here's a sloppy example. When I move to the Nav Point, the rotation happens on the previously shown incline. The forward axis of the player is not lined up with the next nav point.



    I'm trying to find a way to rotate the player/object along its up transform and point at the next nav point, and it all needs to happen in a single frame. I'm sure there is some math that can accomplish this, and I've been trying to figure out a way to make it happen. I'm just at a total loss now and quite frustrated as this is the last bit of code I need to finish my gameplay controller.

    Any help would be appreciated! I can find a million threads and questions about looking at an object or locking the y rotation of an object with LookAt, but all of those fail if the objects isn't aligned with the world axis's!

    EDIT:

    The nav points will all be located at 0 on the y axis. I can't just align the nav points to where the player will be facing, because the world will have various platforms to jump on/the character can jump. They aren't locked to the ground.
     
  2. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    So you want to look in the general direction of the next target position.

    BUT, that next target direction has no vertical value of significance (it's always 0). So really it's like you're looking at a pole at the position of the next node... you'd look at whatever position at that pole is most appropriate for the player's current elevation. Your world is kind of like a winding sheet of paper through 3-space oriented vertical that you're stuck to, and the next node is a line vertically on that paper that you look in the direction of.

    And you need to make sure that the player still orients to the ground surface.

    If I have this correct...

    Code (csharp):
    1.  
    2. //get these however you want. Raycast for the up, node position is trivial. Whatever.
    3. Vector3 up = *current up at the position, must be unit vector (normalized)*;
    4. Vector3 nodePos = *position of the next node*;
    5. Transform player = *player transform*;
    6.  
    7. //vector from player to node
    8. Vector3 v = nodePos - player.position;
    9. //remove the y value, treating it as a general direction
    10. v.y = 0f;
    11.  
    12. //remove the up amount from the vector
    13. float d = Vector3.Dot(v, up);
    14. v -= d * up;
    15. if(v.sqrMagnitude < 0.00001f)
    16. {
    17.    //if you're standing over the node, v would have been parallel to up
    18.    //in this case there's no general direction to facebook
    19.    //don't do anything
    20.    return;
    21. }
    22.  
    23. //normalize vector
    24. v.Normalize();
    25.  
    26. player.rotation = Quaternion.LookRotation(v, up);
    27.  
    Basically we get the direction from player to node.

    We redact the y value, this would ignore the height of the node (treat it like a pole).

    At this point, we could just look in this direction if we were on flat ground. This is because by setting y to 0 we removed all vertical rotation. But since we're on an incline, we now have to remove any vertical rotation relative to that incline.

    This is where the dot product comes in. We project v onto up, this gives us how much of v is in the direction of up. And then subtracting that, we remove the parts of v that are in the direction of up. This gives us a general direction of the node, but it is parallel to the surface (we removed any value of the vector in the direction of the normal. It's now 90 degrees to the normal, meaning it's in the direction of the tangent... otherwise known as parallel to the surface)

    Note that on flat ground this would be:
    v dot <0,1,0> = v.y

    So this is why setting y to 0 would work on flat ground only.

    Note it's at this point that we potentially have a zero vector on our hands. If you happened to be standing over the node, your directional vector would be pointing in the direction of up at it. (note, the v.y = 0 removes potential times where if you were at an angle you could also get a 0 vector). How do you look at something below you while also looking forward. You can't! So we exit here if this situation arises.

    Finally, normalize the vector, and look in its direction. Note I pass the 'up' vector to the overload of Quaternion.LookRotation. By default Quaternion.LookRotation tries to orient to y is up. We have an arbitrary up axis, so we pass that along.

    Here's a vertical view of it:
     
    Last edited: Feb 4, 2018
    KiddUniverse likes this.
  4. B2_Squared

    B2_Squared

    Joined:
    Mar 13, 2013
    Posts:
    14
    This is EXACTLY what I was looking for! Your description of what I wanted to do was also spot on and much better than what I came up with, haha!

    Thank you so much! I'm going to give this a go when I get to my computer.
     
  5. niuage

    niuage

    Joined:
    Nov 17, 2019
    Posts:
    123
    lordofduct, I want to thank you as well, as your answer was extremely helpful to me <3

    I had an issue kinda like that: https://imgur.com/a/yjp1K7Y and your answer fixed it.
     
  6. anat712

    anat712

    Joined:
    May 5, 2020
    Posts:
    7

    That helped me a lot! I could use a little bit more help. for some reason when I use this code the movement looks a little bit jittery, like the normal is really different between near points. I don't really get why, the slope isn't that large and my model moves at a slow speed. I took the up vector from raycasting on the terrain. If anyone knows how to fix this it would be great!
    Anyway, thanks a lot for posting this solution and explaining the math!