Search Unity

Calculating the rigidbody velocity direction using the normal of the terrain

Discussion in 'Scripting' started by Armetron, Aug 30, 2018.

  1. Armetron


    Oct 7, 2016
    Hello everyone, I'm currently stuck on a problem that I cant seem to find the solution to for the past few hours.

    I am making an fps shooter that has a gravity manipulation theme, in it the players will be able to orient themselves through multiple ways to change their "down" gravity (Inspiration) however due to the complexity of the movement mechanics I am unable to use the standard character controller since the collider is unable to be reoriented.

    The way I'm currently coding the controller is to directly modify the velocity of the player using Input.GetAxis * speed (using Unity's auto input learping for the speed up and slow down)

    The problem is when I get to a slope in reference to the player, by directly modifying the velocity I am forcing the player into the slope and having the physics engine push the player up, I do not want this, instead I want to transform the input direction to be perpendicular to the normal direction received from the slope the player is on. If I can get it working this way then I can use it on downward slopes and have the player stick to the floor instead of flying off

    Sorry If I'm unable to explain it properly, I've been at it for 6 hours, also Is there a better way of moving a rigidbody other than velocity? I dont want to use force since then I need to deal with friction and I feel that velocity would help me keep the player stuck on the downhill so long as its within a certain angle

    Code (CSharp):
    1. private void OnCollisionEnter(Collision other) {
    2.         colliderNormal = other.contacts[0].normal; //new "up" of input
    3.     }
    5. void Update() {
    6.         moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); //Get Input
    7.         moveDirection = transform.TransformDirection(moveDirection); //Convert from Local to World
    8.         rotationDirection = new Vector3(rotationAngle, 0.0f, 0.0f); //This will be used later to change the orientation of the player and to set the "down" of the player
    10.         //I need moveDirection to be pointing along the slope its touching
    11.         Debug.DrawRay(rb.transform.position, colliderNormal);
    Again sorry if my explanation is a jumbled mess I'm not good with describing whats going through my head.
  2. Antistone


    Feb 22, 2014
    First: I don't know the details of your game, but if the player changing gravity results in new gravity for EVERYTHING in the world (including other people and objects), then you might want to consider using standard physics and just rotating the entire world instead.


    From your diagrams, it looks like your desired direction is simply the input direction projected onto the plane of the slope.

    Unity has a function specifically for that: ProjectOnPlane

    After that, you probably want to normalize the resulting vector, and then maybe also multiply by the magnitude of the original input vector (if you want to preserve the original magnitude and not just the direction).

    Note that your output vector may be zero if the "slope" is actually a vertical wall.

    I'm not totally sure that's actually what you want to do, though. Your code snippet makes it look like you're already rotating your input from the player's local reference frame into worldspace, and that you intend to reorient the player based on the surface they're touching; if you're already doing those two things, then aren't the player's axes going to be aligned with the slope already?
    petey and Armetron like this.
  3. hippocoder


    Digital Ape Moderator

    Apr 11, 2010
    Code (csharp):
    2.     public Vector3 Align(Vector3 vector, Vector3 normal)
    3.     {
    4.         //typically used to rotate a movement vector by a surface normal
    5.         Vector3 tangent = Vector3.Cross(normal, vector);
    6.         Vector3 newVector = -Vector3.Cross(normal, tangent);
    7.         vector = newVector.normalized * vector.magnitude;
    8.         return vector;
    9.     }
    That should do it.
    jojue and Armetron like this.
  4. Armetron


    Oct 7, 2016
    Oh my GOD, that function is Precisely what I was trying to calculate and I somehow didn't see it. Thank you

    I'm planning to orient the player not based on the surface that they stand on but on special powers/abilities that they use to change gravity. I can't rotate the entire map because its going to be a multiplayer map so it would affect everyone in it
  5. Armetron


    Oct 7, 2016
    Yes that works as well, I completely forgot about cross product
    Thank you