Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Tightening up turning radius with AddRelativeForce

Discussion in 'Physics' started by pjlx911, Jan 27, 2015.

  1. pjlx911


    Dec 19, 2013
    Hi, how is everyone?

    I have been working on this game project for a school studio project (and possibly it might end up being more if I could get this one thing working correctly). The idea is to be a arcade space racing game, subsequently, I want to have physics that are quick (like the ability to turn yaw-wise in a quick fashion).

    The issue I keep running into is how to pull this off? I have the script working, I have barrel rolls, strafing, but I am using rigidbody.AddRelativeForce as the means of propulsion/movement. This due to the fact that I couldn't find anyway outside of using Unity's physics system for this. I tried transform.Translate, but that would only propel the ship in one direction, along the z-axis, and not the direction the ship is pointing in. So I am also open to figuring out a non-Unity physics-based method to get this working (if anyone knows, otherwise I'll stick to using rigidbody physics) as the physics system seems laggy/lazy to me turning-wise.

    So can anyone help me? Here is my script for just the ship's basic controls.

    Thanks for any help.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    4. public class PlayerShip_Easy : MonoBehaviour
    5. {
    6.         private float shipSpd = 300.0f;
    7.         private float turnSpd = 150.0f;
    8.         private float rotSpd = 50.0f;
    9.         private float strafeSpd = 50.0f;
    11.     float rollRate = 0.0f;
    12.     float pitchRate = 0.0f;
    13.     float yawRate = 0.0f;
    15.         float invert = -1.0f;
    17. void Update ()
    18.     {
    19.         float roll = Input.GetAxis("Roll");
    20.         float pitch = Input.GetAxis("Pitch");
    21.         float yaw = Input.GetAxis("Yaw");
    22.         float strafe = Input.GetAxis("Strafe");
    24.         if (Input.GetAxis("Pitch") > 0)
    25.         {
    26.             pitchRate -= pitch * invert * turnSpd * Time.deltaTime;
    27.             pitchRate = Mathf.Clamp(pitchRate, -45.0f, 45.0f);
    28.             transform.rotation = Quaternion.Euler(pitchRate, yawRate , 0);
    29.         }
    30.         else if (Input.GetAxis("Pitch") < 0)
    31.         {
    32.             pitchRate += pitch * turnSpd * Time.deltaTime;
    33.             pitchRate = Mathf.Clamp(pitchRate, -45.0f, 45.0f);
    34.             transform.rotation = Quaternion.Euler(pitchRate, yawRate , 0);
    35.         }
    37.         if (Input.GetAxis("Yaw") > 0)
    38.         {
    39.             yawRate += yaw * turnSpd  * Time.deltaTime;
    40.             yawRate = Mathf.Clamp(yawRate, -60.0f, 60.0f);
    41.             transform.rotation = Quaternion.Euler(pitchRate, yawRate, 0);
    42.         }
    43.         else if (Input.GetAxis("Yaw") < 0)
    44.         {
    45.             yawRate += yaw * turnSpd  * Time.deltaTime;
    46.             yawRate = Mathf.Clamp(yawRate, -60.0f, 60.0f);
    47.             transform.rotation = Quaternion.Euler(pitchRate, yawRate, 0);
    48.         }
    50.     rigidbody.AddRelativeForce(pitch, yaw, shipSpd);
    51. }
  2. pjlx911


    Dec 19, 2013
    Does anyone have any ideas? Or was the information given out too insufficient?

    I would really like to get the controls locked in, bu I'm a little at an impasse on how to do it.

    Thanks again for any help.
  3. andeeeee


    Jul 19, 2005
    Is the problem that the ship keeps drifting on in the same direction after a turn and only gradually starts moving in the new direction? If so then the reason is that the rigidbody's momentum tends to keep it moving in a straight line after it has been accelerated (so the the overall effect feels like the slow, drifty turning of a hovercraft rather than the quick turning of an aircraft, say). The reason that a real aircraft doesn't usually behave like this is that a force is being applied to stop it from moving in a direction other than the direction of travel. This is why a plane banks when it turns - the wings' underside has high drag and so tends to stop the plane drifting on in its original direction.

    You are probably more interested in how to fix the problem, though :) You can introduce some sideways drag on the ship by measuring its speed in the X axis (which I assume is left-right in your game) and applying an opposite force in proportion to that speed in the Update function:

    Code (csharp):
    2. var relVelocity = transform.InverseTransformDirection(rigidbody.velocity);
    3. rigidbody.AddRelativeForce(-relVelocity.x * dragScaleValue * Vector3.right);
    The dragScaleValue is just a float value that lets you tweak how much sideways drag you want. A value of zero turns the drag off altogether and a high value gives high drag and therefore very little drifting.
    Last edited: Jan 29, 2015
    265lutab and pjlx911 like this.
  4. pjlx911


    Dec 19, 2013
    Hi, and thanks for the reply.

    I'm going to try to figure out how to get this to work within the confines of my code. I've noticed the rolling effect when an aircraft makes sharp turns (to help it steer), but every time I try it, it seems to send the ship for a literal loop. What would be a good way to implement a small amount of roll during a hard turn?
  5. pjlx911


    Dec 19, 2013

    Question, do I need to follow this code for all user inputs? Or is this a one-time deal?

    Here's what I've done, and I've noticed that if I put the same amount of drag for the right movements that I do for the left, it breaks the physics in-game. Do I have to balance out the values given between the two sides? What about Up/Down? Would I need to balance it then by four controls?

    Thanks again for the help. It seems to work pretty well! Just need some clarification on it.

    Code (CSharp):
    3.  if (Input.GetAxis("Yaw") > 0)
    4.         {
    5.             yawRate += yaw * turnSpd  * Time.deltaTime;
    6.             yawRate = Mathf.Clamp(yawRate, -90.0f, 90.0f);
    7.             transform.rotation = Quaternion.Euler(pitchRate, yawRate, 0);
    9.             var relV = transform.InverseTransformDirection(rigidbody.velocity);
    10.             rigidbody.AddRelativeForce(-relV.y * 100f * Vector3.up);
    11.         }
    12.         else if (Input.GetAxis("Yaw") < 0)
    13.         {
    14.             yawRate += yaw * turnSpd  * Time.deltaTime;
    15.             yawRate = Mathf.Clamp(yawRate, -90.0f, 90.0f);
    16.             transform.rotation = Quaternion.Euler(pitchRate, yawRate, 0);
    18.             var relV = transform.InverseTransformDirection(rigidbody.velocity);
    19.             rigidbody.AddRelativeForce(-relV.y * 0.2f * Vector3.down);
    20.         }
  6. MatthewW


    Nov 30, 2006
    I'd approach this sort of thing by looking at your idealized velocity and your current velocity, and then breaking it apart from there.

    So if you want to be going completely straight, your ideal velocity is basically:

    Vector3 idealVelocity = transform.forward * rigidbody.velocity.magnitude;

    Where your real velocity is just:


    There's a couple different ways to break this down. You could project your rigidbody velocity onto your ideal velocity, and then figure out what % of a match you are. Apply some severe correction at weak matches, and less at higher matches.

    I have this floating around my libraries:

    Code (csharp):
    2. public static Vector3 Proj(this Vector3 vector, Vector3 baseVector)
    3. {
    4.     var direction = baseVector.normalized;
    5.     var magnitude = Vector2.Dot(vector, direction);
    7.     return direction * magnitude;
    8. }
    A super simplified approach would be to simply manipulate your velocity directly. A prefect follow of your orientation would be this:

    rigidbody.velocity = transform.forward * rigidbody.velocity.magnitude;

    (This is going to feel terrible)

    You could nuance it with:

    Vector3 idealVelocity = transform.forward * rigidbody.velocity.magnitude;
    rigidbody.velocity = Vector3.Lerp(rigidbody.velocity, idealVelocity, 0.5f * Time.deltaTime);

    This is probably going to be weird even still.

    Personally, I like applying multiple forces in games based around physics tuning, because it's also easy to visualize with Debug.DrawRay and fiddle with all the numbers separately. Big single-line formulas aren't as easy to manipulate.

    I'd recommend constructing a braking force, probably pushing against your current velocity relative to how out-of-alignment you are, and then applying it via AddForce. Potentially you could construct a force in the direction of your forward vector with a same-or-smaller push, to redirect that energy into the new direction.

    This would basically be approximating anisotropic/directional friction, which is actually in the Unity physics materials too.

    Hope this helps!