Search Unity

Balancing Motorcycle?

Discussion in 'Physics' started by Bimmy02, Jan 11, 2019.

  1. Bimmy02

    Bimmy02

    Joined:
    Jan 11, 2019
    Posts:
    7
    I've been trying to make a script for a motorcycle game but i don't know how to make the balancing part. I've tried to use the rigidbody constrainers but it doesn't work, it still falls after some time. I have also been trying to use AddTorque and AddForce. I don't know how to make this work so I hope someone can help me with this problem i have. Thanks!
     
  2. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,512
    I'm really interested on this as well.
     
    Bimmy02 likes this.
  3. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    How about a PID controller to self balance?
    Think of it like the rider shifting it's weight side-to-side.
     
    Bimmy02 likes this.
  4. Bimmy02

    Bimmy02

    Joined:
    Jan 11, 2019
    Posts:
    7
    That seems like a really good idea. I'll try it as soon as i've learned it
     
  5. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,512
    Let us know how it goes! :)
     
  6. Marcos-Schultz

    Marcos-Schultz

    Joined:
    Feb 24, 2014
    Posts:
    381
  7. Marcos-Schultz

    Marcos-Schultz

    Joined:
    Feb 24, 2014
    Posts:
    381
    Motorcycle physics is a little complex. You can use the inverted pendulum equations to try to stabilize things, or you can do everything in the most realistic way possible: https://en.wikipedia.org/wiki/Bicycle_and_motorcycle_dynamics

    Personally, I believe that using a few tricks controlling "ribidbody.anglarVelocity" directly is simpler and more effective. Unless you are trying to create a realistic simulator.
     
    Bimmy02 likes this.
  8. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,512
    I had tried by setting the center of mass below the surface but then it behaves like a pendulum, oscillating from side to side. Also the bike tilts in the opposite way when accelerating and braking.
     
  9. Marcos-Schultz

    Marcos-Schultz

    Joined:
    Feb 24, 2014
    Posts:
    381
    I did some testing here. Just play the code below on an empty object and press Play.

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. [RequireComponent(typeof(Rigidbody))][RequireComponent(typeof(BoxCollider))]
    7. public class Motorcycle : MonoBehaviour { //MS Motorcycle test - Marcos Schultz (www.schultzgames.com)
    8.  
    9.     WheelCollider frontWheel;
    10.     WheelCollider rearWheel;
    11.     GameObject meshFront;
    12.     GameObject meshRear;
    13.     Rigidbody ms_Rigidbody;
    14.  
    15.     float rbVelocityMagnitude;
    16.     float horizontalInput;
    17.     float verticalInput;
    18.     float medRPM;
    19.  
    20.     void Awake () {
    21.         transform.rotation = Quaternion.identity;
    22.         ms_Rigidbody = GetComponent<Rigidbody> ();
    23.         ms_Rigidbody.mass = 400;
    24.         ms_Rigidbody.interpolation = RigidbodyInterpolation.Extrapolate;
    25.         ms_Rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
    26.  
    27.         //centerOfMass
    28.         GameObject centerOfmassOBJ = new GameObject ("centerOfmass");
    29.         centerOfmassOBJ.transform.parent = transform;
    30.         centerOfmassOBJ.transform.localPosition = new Vector3 (0.0f, -0.3f, 0.0f);
    31.         ms_Rigidbody.centerOfMass = transform.InverseTransformPoint(centerOfmassOBJ.transform.position);
    32.         //
    33.         BoxCollider collider = GetComponent<BoxCollider>();
    34.         collider.size = new Vector3 (0.5f, 1.0f, 3.0f);
    35.         //
    36.         GenerateWheels();
    37.     }
    38.  
    39.     void OnEnable(){
    40.         WheelCollider WheelColliders = GetComponentInChildren<WheelCollider>();
    41.         WheelColliders.ConfigureVehicleSubsteps(1000, 30, 30);
    42.     }
    43.  
    44.     void FixedUpdate () {
    45.         horizontalInput = Input.GetAxis ("Horizontal");
    46.         verticalInput = Input.GetAxis ("Vertical");
    47.         medRPM = (frontWheel.rpm + rearWheel.rpm) / 2;
    48.         rbVelocityMagnitude = ms_Rigidbody.velocity.magnitude;
    49.  
    50.         //motorTorque
    51.         if (medRPM > 0) {
    52.             rearWheel.motorTorque = verticalInput * ms_Rigidbody.mass * 4.0f;
    53.         } else {
    54.             rearWheel.motorTorque = verticalInput * ms_Rigidbody.mass * 1.5f;
    55.         }
    56.  
    57.         //steerAngle
    58.         float nextAngle = horizontalInput * 35.0f;
    59.         frontWheel.steerAngle = Mathf.Lerp (frontWheel.steerAngle, nextAngle, 0.125f);
    60.  
    61.  
    62.         if(Mathf.Abs(rearWheel.rpm) > 10000){
    63.             rearWheel.motorTorque = 0.0f;
    64.             rearWheel.brakeTorque = ms_Rigidbody.mass * 5;
    65.         }
    66.         //
    67.         if (rbVelocityMagnitude < 1.0f && Mathf.Abs (verticalInput) < 0.1f) {
    68.             rearWheel.brakeTorque = frontWheel.brakeTorque = ms_Rigidbody.mass * 2.0f;
    69.         } else {
    70.             rearWheel.brakeTorque = frontWheel.brakeTorque = 0.0f;
    71.         }
    72.         //
    73.         Stabilizer();
    74.     }
    75.  
    76.     void Update(){
    77.         //update wheel meshes
    78.         Vector3 temporaryVector;
    79.         Quaternion temporaryQuaternion;
    80.         //
    81.         frontWheel.GetWorldPose(out temporaryVector, out temporaryQuaternion);
    82.         meshFront.transform.position = temporaryVector;
    83.         meshFront.transform.rotation = temporaryQuaternion;
    84.         //
    85.         rearWheel.GetWorldPose(out temporaryVector, out temporaryQuaternion);
    86.         meshRear.transform.position = temporaryVector;
    87.         meshRear.transform.rotation = temporaryQuaternion;
    88.     }
    89.  
    90.     void Stabilizer(){
    91.         Vector3 axisFromRotate = Vector3.Cross (transform.up, Vector3.up);
    92.         Vector3 torqueForce = axisFromRotate.normalized * axisFromRotate.magnitude * 50;
    93.         torqueForce.x = torqueForce.x * 0.4f;
    94.         torqueForce -= ms_Rigidbody.angularVelocity;
    95.         ms_Rigidbody.AddTorque (torqueForce * ms_Rigidbody.mass * 0.02f, ForceMode.Impulse);
    96.  
    97.         float rpmSign = Mathf.Sign (medRPM) * 0.02f;
    98.         if (rbVelocityMagnitude > 1.0f && frontWheel.isGrounded && rearWheel.isGrounded) {
    99.             ms_Rigidbody.angularVelocity += new Vector3 (0, horizontalInput * rpmSign, 0);
    100.         }
    101.     }
    102.  
    103.     void GenerateWheels(){
    104.         GameObject frontWeelObject = new GameObject ("frontWheel");
    105.         frontWeelObject.transform.parent = transform;
    106.         frontWeelObject.transform.localPosition = new Vector3 (0, -0.5f, 1.0f);
    107.         frontWheel = frontWeelObject.gameObject.AddComponent<WheelCollider> () as WheelCollider;
    108.         //
    109.         GameObject rearWheelObject = new GameObject ("rearWheel");
    110.         rearWheelObject.transform.parent = transform;
    111.         rearWheelObject.transform.localPosition = new Vector3 (0, -0.5f, -1.0f);
    112.         rearWheel = rearWheelObject.gameObject.AddComponent<WheelCollider> () as WheelCollider;
    113.  
    114.         //settings
    115.         frontWheel.mass = rearWheel.mass = 40;
    116.         frontWheel.radius = rearWheel.radius = 0.5f;
    117.         frontWheel.wheelDampingRate = rearWheel.wheelDampingRate = 0.75f;
    118.         frontWheel.suspensionDistance = rearWheel.suspensionDistance = 0.35f;
    119.         frontWheel.forceAppPointDistance = rearWheel.forceAppPointDistance = 0;
    120.  
    121.         //spring
    122.         JointSpring suspensionSpringg = new JointSpring ();
    123.         suspensionSpringg.spring = 15000;        
    124.         suspensionSpringg.damper = 4000;        
    125.         suspensionSpringg.targetPosition = 0.5f;
    126.         frontWheel.suspensionSpring = rearWheel.suspensionSpring = suspensionSpringg;
    127.  
    128.         //Friction
    129.         WheelFrictionCurve wheelFrictionCurveFW = new WheelFrictionCurve(); //friction FW
    130.         wheelFrictionCurveFW.extremumSlip = 2.0f;
    131.         wheelFrictionCurveFW.extremumValue = 4.0f;
    132.         wheelFrictionCurveFW.asymptoteSlip = 4.0f;
    133.         wheelFrictionCurveFW.asymptoteValue = 2.0f;
    134.         wheelFrictionCurveFW.stiffness = 2.0f;
    135.         frontWheel.forwardFriction = rearWheel.forwardFriction = wheelFrictionCurveFW;
    136.  
    137.         WheelFrictionCurve wheelFrictionCurveSW = new WheelFrictionCurve(); //friction SW
    138.         wheelFrictionCurveSW.extremumSlip = 0.2f;
    139.         wheelFrictionCurveSW.extremumValue = 1.0f;
    140.         wheelFrictionCurveSW.asymptoteSlip = 0.5f;
    141.         wheelFrictionCurveSW.asymptoteValue = 0.75f;
    142.         wheelFrictionCurveSW.stiffness = 2.0f;
    143.         frontWheel.sidewaysFriction = rearWheel.sidewaysFriction = wheelFrictionCurveSW;
    144.  
    145.  
    146.         //generateMeshes
    147.         GameObject wheelFrontMesh = new GameObject ("wheelFrontMesh");
    148.         GameObject meshFrontTemp = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
    149.         Destroy (meshFrontTemp.GetComponent<CapsuleCollider> ());
    150.         meshFrontTemp.transform.parent = wheelFrontMesh.transform;
    151.         meshFrontTemp.transform.localPosition = new Vector3 (0.0f, 0.0f, 0.0f);
    152.         meshFrontTemp.transform.localEulerAngles = new Vector3 (0.0f, 0.0f, 90.0f);
    153.         meshFrontTemp.transform.localScale = new Vector3 (1.0f, 0.1f, 1.0f);
    154.         meshFront = wheelFrontMesh;
    155.         meshFront.transform.parent = transform;
    156.         meshFront.transform.localPosition = new Vector3 (0, -0.5f, 1.0f);
    157.         //
    158.         GameObject wheelRearMesh = new GameObject ("wheelRearMesh");
    159.         GameObject meshRearTemp = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
    160.         Destroy (meshRearTemp.GetComponent<CapsuleCollider> ());
    161.         meshRearTemp.transform.parent = wheelRearMesh.transform;
    162.         meshRearTemp.transform.localPosition = new Vector3 (0.0f, 0.0f, 0.0f);
    163.         meshRearTemp.transform.localEulerAngles = new Vector3 (0.0f, 0.0f, 90.0f);
    164.         meshRearTemp.transform.localScale = new Vector3 (1.0f, 0.1f, 1.0f);
    165.         meshRear = wheelRearMesh;
    166.         meshRear.transform.parent = transform;
    167.         meshRear.transform.localPosition = new Vector3 (0, -0.5f, -1.0f);
    168.         //
    169.         GameObject meshCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
    170.         Destroy (meshCube.GetComponent<BoxCollider> ());
    171.         meshCube.transform.parent = transform;
    172.         meshCube.transform.localPosition = new Vector3 (0.0f, 0.0f, 0.0f);
    173.         meshCube.transform.localScale = new Vector3 (0.5f, 1.0f, 3.0f);
    174.     }
    175. }
    176.  
    177.  
    It has several problems, but it is already a principle to achieve a little stability.
     
    Last edited: Jan 16, 2019
  10. Bimmy02

    Bimmy02

    Joined:
    Jan 11, 2019
    Posts:
    7
    Thanks! That code is actually really good. Do anyone have any idea how to make the motorcycle lean when i press A/D
     
  11. alexeu

    alexeu

    Joined:
    Jan 24, 2016
    Posts:
    257
    motorcycle lean...
    You mean like this ?
     
  12. Bimmy02

    Bimmy02

    Joined:
    Jan 11, 2019
    Posts:
    7
    Yes! Exactly like that.
     
  13. alexeu

    alexeu

    Joined:
    Jan 24, 2016
    Posts:
    257
    That depends on your own built/hierarchy... you also have to deal with wheels colliders settings etc...etc...
    But basicaly you just rotate the Cube Mesh(basicaly meshs and Renderers) that represents the body of your vehicle.
    Lets say we declare this vehicle body as
    Code (CSharp):
    1. public Transform bikeBody;
    2.  
    In Update() you can rotate it like this
    Code (CSharp):
    1. Quaternion bodyRotation = transform.rotation * Quaternion.Euler(0f, 0f, -frontWheel.steerAngle * verticalInput);
    2. bikeBody.rotation = Quaternion.Lerp(bikeBody.rotation, bodyRotation, 0.1f);
     
    riveranb and Bimmy02 like this.
  14. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    9,448
    Bimmy02 likes this.
  15. SpookyCat

    SpookyCat

    Joined:
    Jan 25, 2010
    Posts:
    3,768
    I had a quick play with this using. I had a Gyro balancing script I did for a Helicopter controller which I changed a bit, works along similiar lines to the code above but instead of just balancing to upright it can balance to any lean angle. Needs a bit of work to handle crashing nicely but the code above should get most people what they need from a bike balancing system. Below is a quick video showing the bike driving around, excuse the modelling I made most of out of Unity primitives but doesn't look too shabby.
     
    Edy, riveranb, RealSoftGames and 4 others like this.
  16. MikeUpchat

    MikeUpchat

    Joined:
    Sep 24, 2010
    Posts:
    1,056
    I tried doing bikes a while ago and gave up, couldn't get anything that was in any way driveable but looking at the posts here and the helper script I think will try again, if I can get anything close to SpookyCat's video I will be very happy.
     
    RealSoftGames likes this.
  17. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    Great stuff folks. First question which also has bearing on the second question. If the front forks are at an angle as in custom choppers, for example 45 degrees to perpendicular, do you rotate the wheelCollider so the y axis aligns with the front forks?

    Assuming you have an upper suspension assembly (handlebar steering pivot and upper forks) and a lower suspension assembly (lower forks, wheel hub/brakes, wheel mesh). How do you get the lower suspension assembly which would be coupled to the wheel collider to work with the upper suspension which is attached to the main bike frame?

    @SpookyCat The suspension on your motorcycle has the suspensions working well, front and back. Can you post the Hierarchy so I can set up my chopper. It is a fully customizable bike with swap out parts for everything so I will probably have to consolidate parts under various parent objects to get it to work as the current hierarchy does not seem to have a logical set-up for suspension simulation. If I can get a look at how that is done and perhaps the values you used on the colliders for the springs and other related values. I have built a PID controller with leaning system for a MonoCycle which will stabilize the rigidbody on this chopper. The suspension system is a bit confusing to me as I only started writing scripts for wheel colliders and vehicle controllers very recently.
     
  18. Bimmy02

    Bimmy02

    Joined:
    Jan 11, 2019
    Posts:
    7
    Hi! It's been a while, anyways here is an update on the game, haven't worked on it a lot but here is a video of how far i've come. It can be improved for sure, for example grabbing multiple objects at once can be annoying and don't ask why the throttlehandle is rotated incorrectly. Enjoy and feedback is appreciated, positive and negative.
    https://drive.google.com/file/d/1Xhaj8RDThZ5QdzZtsGbjbty8-iRbXYxV/view?usp=sharing
     
    pauloaguiar likes this.