Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Building a speed limit and deceleration system, need help fixing/optimising

Discussion in 'Physics' started by Xenon42, Sep 18, 2016.

  1. Xenon42

    Xenon42

    Joined:
    Aug 31, 2016
    Posts:
    15
    I'm trying to make a spaceship dogfighting game, and have been trying to make my ships move like airplanes - always moving forward and cancelling sideways momentum, cancelling angular momentum when none of the pitch, roll or yaw keys are held down, and so on.

    I've got a pair of scripts at the moment that are reasonably functional, one to control my test ship's orientation, and one to control its movement. Currently I want to rewrite the movement code to do a bunch of things:

    • I want the player to be able to increase or decrease the top speed (up to a set limit), whereupon the ship will accelerate to that speed, then stop accelerating and hold to its current forward velocity.

    • If the ship changes direction, I want it to re-accelerate until it's back up to full speed again. If the player reduces the ship's top speed, I want the ship to decelerate to match it.

    • I also want it to be possible to make the ship stop entirely, and go into reverse, with the same rules as going forwards.
    Unfortunately, my current script is a broken mess, and I don't know what's going wrong. The ship will only move forward at a very limited rate, will accelerate backwards indefinitely, and if I alter the ship's orientation while moving it starts accelerating out of control in seemingly random directions.

    I would greatly appreciate it if someone could look over my script and point out any problems they can see, or offer any pointers on how my code could be made more efficient (I'm sure what I've got right now is very messy). I know that's quite a lot to ask, but I'm seriously struggling to untangle this stuff myself.

    Here's my script:

    Code (CSharp):
    1.   using UnityEngine;
    2.   using System.Collections;
    3.  
    4.   public class Thrust : MonoBehaviour
    5.   {
    6.   public float accelRate;
    7.   public float maxSpeed;
    8.   public Rigidbody rb;
    9.   float speed;
    10.   float reverseSpeed;
    11.   int throttle;
    12.   int reverseThrottle;
    13.   int speedKeyState;
    14.  
    15.   void Start ()
    16.   {
    17.   rb = GetComponent<Rigidbody>();
    18.   speed = 0f;
    19.   throttle = 0;
    20.   reverseSpeed = 0f;
    21.   reverseThrottle = 0;
    22.   speedKeyState = 0;
    23.   Vector3 localVel = rb.transform.InverseTransformDirection (GetComponent<Rigidbody> ().velocity);
    24.   localVel.z = maxSpeed;
    25.   }
    26.  
    27.   void FixedUpdate ()
    28.   {
    29.   Vector3 localVel = rb.transform.InverseTransformDirection (GetComponent<Rigidbody> ().velocity);
    30.  
    31.   //This thrust is applied constantly, but limited by the state of the throttle variables
    32.   rb.AddRelativeForce (Vector3.forward * (throttle * (accelRate * Time.deltaTime)));
    33.   rb.AddRelativeForce (Vector3.back * (reverseThrottle * (accelRate * Time.deltaTime)));
    34.  
    35.  
    36.   //These two if blocks are meant to stop the ship from accelerating beyond the max speed, forwards or backwards, and apply a decelerating force in either direction to bring the ship back to the limit
    37.   if (localVel.z >= maxSpeed * speed) {
    38.   if (speed != 0) {
    39.   throttle = 0;
    40.   rb.AddRelativeForce (Vector3.back * ((accelRate * Time.deltaTime) * 0.1f));
    41.   }
    42.   throttle = 0;
    43.   } else {
    44.   throttle = 1;
    45.   }
    46.  
    47.  
    48.   if (localVel.z <= -(maxSpeed * reverseSpeed)) {
    49.   if (reverseSpeed != 0) {
    50.   reverseThrottle = 0;
    51.   rb.AddRelativeForce (Vector3.back * ((accelRate * Time.deltaTime) * 0.1f));
    52.   }
    53.   reverseThrottle = 0;
    54.   } else {
    55.   reverseThrottle = 1;
    56.   }
    57.  
    58.   //These if blocks are intended to cancel out momentum on all axes besides Z, so the ship doesn't end up drifting sideways while flying
    59.   if (localVel.x > 0f)
    60.   {
    61.   rb.AddRelativeForce (Vector3.left * (speed * ((accelRate * 2) * Time.deltaTime)));
    62.   rb.AddRelativeForce (Vector3.left * (reverseSpeed * ((accelRate * 2) * Time.deltaTime)));
    63.   }
    64.   if (localVel.x < 0f)
    65.   {
    66.   rb.AddRelativeForce (Vector3.right * (speed * ((accelRate * 2) * Time.deltaTime)));
    67.   rb.AddRelativeForce (Vector3.right * (reverseSpeed * ((accelRate * 2) * Time.deltaTime)));
    68.   }
    69.   if (localVel.y > 0f)
    70.   {
    71.   rb.AddRelativeForce (Vector3.down * (speed * ((accelRate * 2) * Time.deltaTime)));
    72.   rb.AddRelativeForce (Vector3.down * (reverseSpeed * ((accelRate * 2) * Time.deltaTime)));
    73.   }
    74.   if (localVel.y < 0f)
    75.   {
    76.   rb.AddRelativeForce (Vector3.up * (speed * ((accelRate * 2) * Time.deltaTime)));
    77.   rb.AddRelativeForce (Vector3.up * (reverseSpeed * ((accelRate * 2) * Time.deltaTime)));
    78.   }
    79.  
    80.   //This if block and the switch below are the controls that allow the player to increase or decrease the top speed, or go into reverse
    81.   if (Input.GetKey (KeyCode.UpArrow))
    82.   speedKeyState = 1;
    83.   if (Input.GetKey (KeyCode.DownArrow))
    84.   speedKeyState = 2;
    85.   if (!Input.GetKey (KeyCode.UpArrow) && !Input.GetKey (KeyCode.DownArrow))
    86.   speedKeyState = 0;
    87.  
    88.   switch (speedKeyState)
    89.   {
    90.   case 1:
    91.   speed += 0.01f;
    92.   break;
    93.   case 2:
    94.   if (speed == 0f)
    95.   reverseSpeed += 0.01f;
    96.   else
    97.   speed -= 0.01f;
    98.   break;
    99.   case 0:
    100.   if (reverseSpeed > 0f)
    101.   reverseSpeed -= 0.01f;
    102.   break;
    103.   }
    104.  
    105.   //These if blocks ensure that the speed variables cannot rise or fall below certain thresholds, so that the ship will always be limited to 100% top speed going forwards, and 20% top speed going backwards
    106.   if (speed > 1f)
    107.   speed = 1f;
    108.   if (speed < 0f)
    109.   speed = 0f;
    110.  
    111.   if (reverseSpeed > 0.2f)
    112.   reverseSpeed = 0.2f;
    113.   if (reverseSpeed < 0f)
    114.   reverseSpeed = 0f;
    115.  
    116.   //Some print functions for debug purposes
    117.   print ("speed is: " + speed);
    118.   print ("throttle is: " + throttle);
    119.   print ("localVel is: " + localVel);
    120.   print ("reverseThrottle is: " + reverseThrottle);
    121.   print ("reverseSpeed is: " + reverseSpeed);
    122.   }
    123.   }
    124.  
     
  2. GrischaG

    GrischaG

    Joined:
    Apr 26, 2013
    Posts:
    40
    Hi Xenon,

    you have localVel declared in Start() as well, but that does not affect anything because it is local and never used.

    The second thing i see is, that you are working with speed and reverseSpeed. That can cause some trouble and a lot of if/else statements. Why don´t you use speed (wich can became negative for reverse movement) and a maxSpeed and maxSpeedReverse?

    And i would do the Input checking at the beginning of FixedUpdate. Or do you have a good reason for your implementation?

    Your script isn´t very long but seems to make a lot of trouble. I would suggest to you to do it again and then step by step implement the needed functionality. Start with the forward movement and then backwards. If that works you can take care for changing the direction.

    Grischa
     
  3. Xenon42

    Xenon42

    Joined:
    Aug 31, 2016
    Posts:
    15
    Thanks for the help Grischa. The acceleration functionality is working a lot better now.

    However, I'm currently stuck for how to make the sideways-braking function work. My script right now is functional, but it's not perfect. Currently the script will check if the spaceship's sideways or vertical velocity is greater than 0, and if so apply a counter-force. But the force applied is always larger than necessary, requiring the script to apply a counter-counter-force in the next frame, and so on.

    The result is that the ship's sideways and vertical velocities oscillate to either side of 0, without ever actually settling on it. The discrepancy is small - only single digits either way, and with the forward velocities I'm working with that's no big deal. However it does produce some annoying drift when the ship is moving slowly, and cause the ship to vibrate, which is visible in the wiggling of the exhaust trail that the ship leaves in its wake.

    I would like to eliminate both these problems by ensuring the velocities always settle on 0, but I also don't want the script to cancel out the momentum instantaneously. Currently there's a set rate at which the ship cancels momentum, which gives it a nice sense of weight when turning sharply, and I'd quite like to preserve that feeling if possible. How could I do this?

    The relevant section of my script:

    Code (CSharp):
    1.     if (localVel.x > 0f)
    2.        rb.AddRelativeForce (Vector3.left * (accelRate * Time.deltaTime));
    3.      if (localVel.x < 0f)
    4.        rb.AddRelativeForce (Vector3.right * (accelRate * Time.deltaTime));
    5.      if (localVel.y > 0f)
    6.        rb.AddRelativeForce (Vector3.down * (accelRate * Time.deltaTime));
    7.      if (localVel.y < 0f)
    8.        rb.AddRelativeForce (Vector3.up * (accelRate * Time.deltaTime));
    EDIT:
    Never mind, did some work on my own, and got exactly the functionality I was looking for. I've included my script below, in case anyone else has a similar issue. It's perhaps not the most efficient method, but it works for me:

    Code (CSharp):
    1.     if (localVel.x != 0f)
    2.        rb.AddRelativeForce (Vector3.left * ((localVel.x * brakeRate) * Time.deltaTime));
    3.      if (localVel.y != 0f)
    4.        rb.AddRelativeForce (Vector3.down * ((localVel.y * brakeRate) * Time.deltaTime));
    brakeRate is a public float variable, that can be altered to adjust how quickly the ship corrects itself. The higher the value, the harder the ship brakes, though making the value too high causes the ship to accelerate out of control, so be careful. I'm keeping the value very low, as this gives my ship a nice sense of weight and inertia.
     
    Last edited: Sep 23, 2016