Side scrolling bike physics help please

I’m new to unity and game engines in general but i’m doing lots of research on the subject trying to pick up what I can.
I want to create a realistic 2D side scroller bike physics control setup but theirs lots of conflicting information about and its very confusing to a new user like myself.
Do I use wheel colliders or should I go with sphere colliders, how do I get the bike frame to react to the torque of the wheels under acceleration (wheelies) and so on.
I think what I want to achieve is quite simple and i’m not looking at taking the game past being a side scroller until I have learnt the basics.
Any help and advice is much appreciated as I feel a little lost with all the car tutorials that seem relevant however so far from what I actually want.
I’ve got my bike modeled pretty good, heres the high polly version it was the first thing I ever modeled in Maya :O)

I’m working on something similar. It’s definitely been a challenge.

You can use wheel colliders. One challenge you’ll get right away is that the bike falls over (doesn’t balance). I just set rotation and transform constraints - it can only move X Z and only rotate on a Y axis. Another challenge is that wheel colliders cast a single ray (down typically). This means that wheelies start to work weird as you get steeper. I fixed that by layering a few wheel colliders and different angles. I would probably end up writing my own collider on my next project because of this.

Like Chris said, it depends a lot on the ground on which you will be moving. If it is flat then go with wheel colliders, if it is relatively flat only with steep slopes, you can try sphere colliders. If you have a lot of “curves” then perhaps a custom solution would be best. I had some success with sphere colliders and joints on straight surfaces (no curves).
Also, there was some motor bike project on the asset store, but it was 3D.
Also, on the asset store there is a spline package that has an example of a 2D car.

How do I go about creating a custom collider ?
I will need something that can handle ramps like quarter pipes etc I find it strange unity has nothing that seems to fit ?

I would use a wheel collider first. It will do 99% of what you need and it will expose things that you probably didn’t even consider. My reason for wanting a custom one is that I wanted more control over the “suspension.” It’s very linear so it’s hard to have it sag realistically and not bottom out super easy. You won’t have much suspension in a BMX game so you don’t need to worry - but you will need a little (tire thickness) or you’ll be dealing with a lot of bouncing. The trick there is that the rider is the suspension in most cases so you almost want to the whole bike to be a single suspenion point under the rider mass. I used to be huge into BMX ramps so I can totally appreciate this project.

Here’s what I’m working on:

MX Test App
Q/A up shift/down shift - up/own accel/brake.

Ok i’ll see how I get on with the wheel collider, need to get my head around scripting though so any tips are well appreciated :slight_smile:
Chris I love where your going with the mx test, I think beeing zoomed in a little more when going slow would help and also it seems hard to push/lean forwards ?

I can’t figure out how to best rig my bike, can anybody point me in the right direction in regards to how to lay out the hierarchy with rigid bodies etc ?

This is how I did it.

Bike (with Rigidbody and it’s own collider)
-Colliders(folder)
–FrontWheelCollider
–RearWheelCollider
-Front Wheel mesh (no collider wheel script)
-Rear Wheel mesh (no collider wheel script)

Wheel script aligns wheel to collider.

Cheers for that fella, i’ve gone about it a different way as I don’t like the wheels not turning from ground friction.
I’ve got the wheels attached via hinge joints to the frame, everything rigid body and sphere colliders on the wheels, seems to work real nice for now.
At the moment i’m applying force the rear wheel just in the inspector so the bike power wheelies off along a plane, its pretty fun but i’m just avoiding learning how to scrip the control inputs I guess lol

Ok if I don’t manage to figure this out sooner, how do write a script linking Input.GetAxis(“Horizontal”) to increasing the power of the moto on the hinge joint ?

Ok so this is what i’ve got :

function Update () {
if
(Input.GetButtonDown(“Pedal”))
hingeJoint.motor.force = 9;
hingeJoint.motor.targetVelocity = -300;
}

Now how to I disengage the power ?

I’ve never used the motor before but I imagine force=0 would work.

Can you use torque instead? You shouldn’t need to apply velocity if you’re applying rotation to the rear wheel.

at the top of your script add:
var vControl : float;
var RearWheel: Rigidbody;
var Power: float = 100;

In the update do this:
vControl = Input.GetAxis(“Vertical”);

in the FixedUpdate do this:
var Torque:float = vControl * Power * Time.deltaTime;
if (Torque<0) {Torque=0;}

for slowing down you’ll need to add brake. I’m not sure if this is the best way but it makes sense to me. You will want to put physics updates into the FixedUpdate though (I learned that the hard way).

Thanks for the reply Chris !

I know what you mean about using torque to turn the rear wheel but when I applied the hinge it had a built in motor so I decided to try and use that.

I’ve uploaded a simplified example scene so you should get a better idea of what i’m doing.
http://wtrns.fr/rUVFrHnc2AqRLZ

If you click play you will see some force i’ve added in the inspector.

Ok i’ve gone back down the route of the wheel colliders as the hinge joint developed far to much gyroscopic effect over jumps etc and also I was having problems with friction and saggy joins under load.
The big problem seems to be how to layer the wheel colliders like you mentioned in your earlier post Chir64. The way I tried it the wheel wouldn’t wheelie past the first collider, any help with this would be amazing as I feel i’m getting closer to a workable solution.

How did this end up turning out for you? I am about to embark on the same project 2D Bike side-scroller and I am experience the same challenges.

Where you able to get it to a place you were happy with? What strategy did you end up going with?

Looking forward to hearing what happened.

Awhile ago I spent some time playing around with motorbike physics for a side scrolling moto game. And I tried so many advanced methods, built my own colliders script and ended up writing a very large script for the bike and doing it that way could never achieve the physics I wanted.

Eventually I realised an easy way to do it, Just using Sphere Colliders, The problem I faced when first using sphere colliders was performing flips and wheelies, doing a wheelie was simple, just add force to the right wheel when grounded, but I couldn’t figure out how to add a flip in the air, I was being stupid, all I needed to do was add torque to the body of the bike and it would flip nicely.

Anyways here’s a very very basic version of my script that should help you get started:

using UnityEngine;
using System.Collections;

public class Bike_Controller : MonoBehaviour {
	
	public Rigidbody bikesRigidbody;
	
	public Transform frontWheel;
	public Transform rearWheel;
	
	public bool onGround;
	
	public float speed;
	public float horsePower;
	public float rotationWeightFactor = 50.0f;
	private float currentSpeed;
	
	// Update is called once per frame
	void Update () {
		if(Input.GetKey(KeyCode.Space)) {
			bikesRigidbody.AddForce(Vector3.up * 100);	
		}
		
		if(Input.GetKey(KeyCode.UpArrow)) {
			frontWheel.transform.gameObject.GetComponent<Rigidbody>().AddTorque(new Vector3(0, 0, -horsePower * 100 * Time.deltaTime), ForceMode.Impulse);
			rearWheel.transform.gameObject.GetComponent<Rigidbody>().AddTorque(new Vector3(0, 0, -horsePower * 100 * Time.deltaTime), ForceMode.Impulse);
		}
		
		if(Input.GetKey(KeyCode.DownArrow)) {
			frontWheel.transform.gameObject.GetComponent<Rigidbody>().AddRelativeTorque(new Vector3(0, 0, horsePower * 100 * Time.deltaTime), ForceMode.Impulse);
			rearWheel.transform.gameObject.GetComponent<Rigidbody>().AddRelativeTorque(new Vector3(0, 0, horsePower * 100 * Time.deltaTime), ForceMode.Impulse);
		}
		
		if(Input.GetKey(KeyCode.LeftArrow)) {
			if(onGround) {
				bikesRigidbody.AddTorque(new Vector3(0, 0, (rotationWeightFactor * 2) * 10 * Time.deltaTime));
				bikesRigidbody.AddForceAtPosition(Vector3.up * 30, new Vector3(frontWheel.transform.position.x, rearWheel.transform.position.y - 0.5f, transform.position.z));
			} else {
				bikesRigidbody.AddTorque(new Vector3(0, 0, rotationWeightFactor * 10 * Time.deltaTime));	
			}
		} else if(Input.GetKey(KeyCode.RightArrow)) {
			if(onGround) {
				bikesRigidbody.AddTorque(new Vector3(0, 0, -(rotationWeightFactor * 2) * 10 * Time.deltaTime));
				bikesRigidbody.AddForceAtPosition(Vector3.up * 30, new Vector3(rearWheel.transform.position.x, rearWheel.transform.position.y - 0.5f, transform.position.z));
			} else {
				bikesRigidbody.AddTorque(new Vector3(0, 0, -rotationWeightFactor * 10 * Time.deltaTime));	
			}
		}
		
		onGround = isGrounded();
	
		currentSpeed = new Vector3(rearWheel.transform.gameObject.GetComponent<Rigidbody>().velocity.x, 0, rearWheel.transform.gameObject.GetComponent<Rigidbody>().velocity.z).magnitude;
	
		LimitSpeed(15.0f);
		
		RaycastHit hit;
		if(Physics.Raycast(rearWheel.transform.position, -Vector3.up, out hit, 100.0f) || Physics.Raycast(frontWheel.transform.position, -Vector3.up, out hit, 100.0f)) {
			if(hit.transform.tag == "Ground" || hit.transform.name == "Floor") {
				float distanceToGround = hit.distance;
				Vector3 pointOfHit = hit.point;
				//Debug.Log("Ray Hit Distance: " + hit.distance + "Hit Points: " + hit.point);
				onGround = true;
			} else {
				onGround = false;
			}
		}
	}
	
	void LimitVelocity (float limit) {
		Vector3 velocity = bikesRigidbody.velocity;
	    if (velocity == Vector3.zero) return;
	
	    float magnitude = velocity.magnitude;
	    if (magnitude > limit) {
	        velocity *= (limit / magnitude);
	        bikesRigidbody.velocity = velocity;
	    }	
	}
	
	void LimitSpeed (float limit) {
		if (currentSpeed > limit) {
      		rearWheel.transform.gameObject.GetComponent<Rigidbody>().velocity = new Vector3(rearWheel.transform.gameObject.GetComponent<Rigidbody>().velocity.x, 0, rearWheel.transform.gameObject.GetComponent<Rigidbody>().velocity.z).normalized * limit + rearWheel.transform.gameObject.GetComponent<Rigidbody>().velocity.y * Vector3.up;
    	}	
	}
	
	public bool isGrounded () {
		return Physics.Raycast(transform.position, -Vector3.up, rearWheel.GetComponent<SphereCollider>().bounds.extents.y + 0.1f);
	}
	
	void OnGUI () {
		GUI.Label(new Rect(20, 10, 300, 100), "Velocity: " + bikesRigidbody.velocity.ToString());
		GUI.Label(new Rect(20, 25, 300, 100), "Speed: " + currentSpeed.ToString());
		GUI.Label(new Rect(20, 40, 300, 100), "On Ground: " + onGround.ToString());
	}	
}
1 Like

Hey smitchell your code looks great. Not st the computer tonight but would definitely like to try this tomorrow. Can you see any problems using joints as a sort of suspension with your code? Did yours actually include any sort of springy suspension? Cheers!