Balancing Motorcycle?

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!

I’m really interested on this as well.

1 Like

How about a PID controller to self balance?
Think of it like the rider shifting it’s weight side-to-side.

1 Like

That seems like a really good idea. I’ll try it as soon as i’ve learned it

Let us know how it goes!

1 Like

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: Bicycle and motorcycle dynamics - Wikipedia

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.

1 Like

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.

I did some testing here. Just play the code below on an empty object and press Play.

``````using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))][RequireComponent(typeof(BoxCollider))]
public class Motorcycle : MonoBehaviour { //MS Motorcycle test - Marcos Schultz (www.schultzgames.com)

WheelCollider frontWheel;
WheelCollider rearWheel;
GameObject meshFront;
GameObject meshRear;
Rigidbody ms_Rigidbody;

float rbVelocityMagnitude;
float horizontalInput;
float verticalInput;
float medRPM;

void Awake () {
transform.rotation = Quaternion.identity;
ms_Rigidbody = GetComponent<Rigidbody> ();
ms_Rigidbody.mass = 400;
ms_Rigidbody.interpolation = RigidbodyInterpolation.Extrapolate;
ms_Rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;

//centerOfMass
GameObject centerOfmassOBJ = new GameObject ("centerOfmass");
centerOfmassOBJ.transform.parent = transform;
centerOfmassOBJ.transform.localPosition = new Vector3 (0.0f, -0.3f, 0.0f);
ms_Rigidbody.centerOfMass = transform.InverseTransformPoint(centerOfmassOBJ.transform.position);
//
BoxCollider collider = GetComponent<BoxCollider>();
collider.size = new Vector3 (0.5f, 1.0f, 3.0f);
//
GenerateWheels();
}

void OnEnable(){
WheelCollider WheelColliders = GetComponentInChildren<WheelCollider>();
WheelColliders.ConfigureVehicleSubsteps(1000, 30, 30);
}

void FixedUpdate () {
horizontalInput = Input.GetAxis ("Horizontal");
verticalInput = Input.GetAxis ("Vertical");
medRPM = (frontWheel.rpm + rearWheel.rpm) / 2;
rbVelocityMagnitude = ms_Rigidbody.velocity.magnitude;

//motorTorque
if (medRPM > 0) {
rearWheel.motorTorque = verticalInput * ms_Rigidbody.mass * 4.0f;
} else {
rearWheel.motorTorque = verticalInput * ms_Rigidbody.mass * 1.5f;
}

//steerAngle
float nextAngle = horizontalInput * 35.0f;
frontWheel.steerAngle = Mathf.Lerp (frontWheel.steerAngle, nextAngle, 0.125f);

if(Mathf.Abs(rearWheel.rpm) > 10000){
rearWheel.motorTorque = 0.0f;
rearWheel.brakeTorque = ms_Rigidbody.mass * 5;
}
//
if (rbVelocityMagnitude < 1.0f && Mathf.Abs (verticalInput) < 0.1f) {
rearWheel.brakeTorque = frontWheel.brakeTorque = ms_Rigidbody.mass * 2.0f;
} else {
rearWheel.brakeTorque = frontWheel.brakeTorque = 0.0f;
}
//
Stabilizer();
}

void Update(){
//update wheel meshes
Vector3 temporaryVector;
Quaternion temporaryQuaternion;
//
frontWheel.GetWorldPose(out temporaryVector, out temporaryQuaternion);
meshFront.transform.position = temporaryVector;
meshFront.transform.rotation = temporaryQuaternion;
//
rearWheel.GetWorldPose(out temporaryVector, out temporaryQuaternion);
meshRear.transform.position = temporaryVector;
meshRear.transform.rotation = temporaryQuaternion;
}

void Stabilizer(){
Vector3 axisFromRotate = Vector3.Cross (transform.up, Vector3.up);
Vector3 torqueForce = axisFromRotate.normalized * axisFromRotate.magnitude * 50;
torqueForce.x = torqueForce.x * 0.4f;
torqueForce -= ms_Rigidbody.angularVelocity;
ms_Rigidbody.AddTorque (torqueForce * ms_Rigidbody.mass * 0.02f, ForceMode.Impulse);

float rpmSign = Mathf.Sign (medRPM) * 0.02f;
if (rbVelocityMagnitude > 1.0f && frontWheel.isGrounded && rearWheel.isGrounded) {
ms_Rigidbody.angularVelocity += new Vector3 (0, horizontalInput * rpmSign, 0);
}
}

void GenerateWheels(){
GameObject frontWeelObject = new GameObject ("frontWheel");
frontWeelObject.transform.parent = transform;
frontWeelObject.transform.localPosition = new Vector3 (0, -0.5f, 1.0f);
frontWheel = frontWeelObject.gameObject.AddComponent<WheelCollider> () as WheelCollider;
//
GameObject rearWheelObject = new GameObject ("rearWheel");
rearWheelObject.transform.parent = transform;
rearWheelObject.transform.localPosition = new Vector3 (0, -0.5f, -1.0f);
rearWheel = rearWheelObject.gameObject.AddComponent<WheelCollider> () as WheelCollider;

//settings
frontWheel.mass = rearWheel.mass = 40;
frontWheel.wheelDampingRate = rearWheel.wheelDampingRate = 0.75f;
frontWheel.suspensionDistance = rearWheel.suspensionDistance = 0.35f;
frontWheel.forceAppPointDistance = rearWheel.forceAppPointDistance = 0;

//spring
JointSpring suspensionSpringg = new JointSpring ();
suspensionSpringg.spring = 15000;
suspensionSpringg.damper = 4000;
suspensionSpringg.targetPosition = 0.5f;
frontWheel.suspensionSpring = rearWheel.suspensionSpring = suspensionSpringg;

//Friction
WheelFrictionCurve wheelFrictionCurveFW = new WheelFrictionCurve(); //friction FW
wheelFrictionCurveFW.extremumSlip = 2.0f;
wheelFrictionCurveFW.extremumValue = 4.0f;
wheelFrictionCurveFW.asymptoteSlip = 4.0f;
wheelFrictionCurveFW.asymptoteValue = 2.0f;
wheelFrictionCurveFW.stiffness = 2.0f;
frontWheel.forwardFriction = rearWheel.forwardFriction = wheelFrictionCurveFW;

WheelFrictionCurve wheelFrictionCurveSW = new WheelFrictionCurve(); //friction SW
wheelFrictionCurveSW.extremumSlip = 0.2f;
wheelFrictionCurveSW.extremumValue = 1.0f;
wheelFrictionCurveSW.asymptoteSlip = 0.5f;
wheelFrictionCurveSW.asymptoteValue = 0.75f;
wheelFrictionCurveSW.stiffness = 2.0f;
frontWheel.sidewaysFriction = rearWheel.sidewaysFriction = wheelFrictionCurveSW;

//generateMeshes
GameObject wheelFrontMesh = new GameObject ("wheelFrontMesh");
GameObject meshFrontTemp = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
Destroy (meshFrontTemp.GetComponent<CapsuleCollider> ());
meshFrontTemp.transform.parent = wheelFrontMesh.transform;
meshFrontTemp.transform.localPosition = new Vector3 (0.0f, 0.0f, 0.0f);
meshFrontTemp.transform.localEulerAngles = new Vector3 (0.0f, 0.0f, 90.0f);
meshFrontTemp.transform.localScale = new Vector3 (1.0f, 0.1f, 1.0f);
meshFront = wheelFrontMesh;
meshFront.transform.parent = transform;
meshFront.transform.localPosition = new Vector3 (0, -0.5f, 1.0f);
//
GameObject wheelRearMesh = new GameObject ("wheelRearMesh");
GameObject meshRearTemp = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
Destroy (meshRearTemp.GetComponent<CapsuleCollider> ());
meshRearTemp.transform.parent = wheelRearMesh.transform;
meshRearTemp.transform.localPosition = new Vector3 (0.0f, 0.0f, 0.0f);
meshRearTemp.transform.localEulerAngles = new Vector3 (0.0f, 0.0f, 90.0f);
meshRearTemp.transform.localScale = new Vector3 (1.0f, 0.1f, 1.0f);
meshRear = wheelRearMesh;
meshRear.transform.parent = transform;
meshRear.transform.localPosition = new Vector3 (0, -0.5f, -1.0f);
//
GameObject meshCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Destroy (meshCube.GetComponent<BoxCollider> ());
meshCube.transform.parent = transform;
meshCube.transform.localPosition = new Vector3 (0.0f, 0.0f, 0.0f);
meshCube.transform.localScale = new Vector3 (0.5f, 1.0f, 3.0f);
}
}
``````

It has several problems, but it is already a principle to achieve a little stability.

5 Likes

Thanks! That code is actually really good. Do anyone have any idea how to make the motorcycle lean when i press A/D

motorcycle lean…
You mean like this ?

Yes! Exactly like that.

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

``````public Transform bikeBody;
``````

In Update() you can rotate it like this

``````Quaternion bodyRotation = transform.rotation * Quaternion.Euler(0f, 0f, -frontWheel.steerAngle * verticalInput);
bikeBody.rotation = Quaternion.Lerp(bikeBody.rotation, bodyRotation, 0.1f);
``````
2 Likes

1 Like

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.

7 Likes

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.

1 Like

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.

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.

1 Like