Custom setup using the YACC library
This page will guide you through the steps required to setup a custom car controller using the YACC library.
Let's start by defining the components that we will need to setup our car controller, with an engine. We will also need a gear box, wheels from YACC, remember that all classes are exposed with the [Serializable]
attribute so all public fields will be visible in the inspector.
// Access to the YACC namespace, otherwise use
// YACC.Engine, YACC.GearBox, YACC.Wheels below.
using YACC;
public class CustomCar : MonoBehaviour
{
public Engine engine = new();
public GearBox gearBox = new(
ratios: new float[] { 3.5f, 2.5f, 2.0f, 1.5f, 1f, 0.8f },
finalRatio: 4f,
reverseRatio: 3.7f,
changeGearTime: 0.5f
);
public Wheels wheels = new();
}
As a following step you should remember to Dispatch the Awake method to the wheels manager, this will initialize the wheels and setup the wheel colliders for you if you don't want to manually setup the wheel colliders on your prefabs.
Remember also to dispatch the OnValidate method to all the components, this will ensure that all the values are properly setup and validated, in this example we are also calculating the optimal gear shifts for the car using the YACC utility method Utils.CalculateOptimalGearShifts
.
private void Awake()
{
wheels.Awake();
}
private void OnValidate()
{
wheels.OnValidate();
gearBox.OnValidate();
engine.OnValidate();
Utils.CalculateOptimalGearShifts(engine, gearBox, wheels);
}
In your Start()
method you might need to initialise your input system controls and run some checks, for example we recommend to check for a rigidbody to be attached to the gameobject as well as setting up the center of mass of the object, this will help the gameobject to avoid overturning at high speeds.
private void Start()
{
carRigidBody = GetComponent<Rigidbody>();
if (carRigidBody.centerOfMass == Vector3.zero)
{
Transform centerOfMass = gameObject.transform.Find("CenterOfMass");
carRigidBody.centerOfMass = centerOfMass.localPosition;
}
}
For the update methods it is recommended to setup your input reads on the update method and then dispatch the fixed update methods to the physics components of the car, this will ensure that the physics are properly updated and that the car will behave as expected, lets grab the inputs and then dispatch the fixed update methods to the components.
Let's apply throttle to the engine, and brake and steer to the wheels.
private float ACCEL_INPUT = 0f;
private float BRAKE_INPUT = 0f;
private float STEER_INPUT = 0f;
private void Update()
{
// Read inputs here
// Example if using the old input system:
ACCEL_INPUT = Input.GetAxis("Vertical");
BRAKE_INPUT = Input.GetAxis("Vertical") * -1f;
STEER_INPUT = Input.GetAxis("Horizontal");
}
private void FixedUpdate()
{
engine.throttle = ACCEL_INPUT;
wheels.brake = BRAKE_INPUT;
wheels.steer = STEER_INPUT;
}
Now that we are feeding the inputs to the engine and wheels we also need to:
- Attach the engine to the wheels.
- Attach the engine clutch to the gear box clutch.
- Apply torque to the wheels so we can push the car forward.
The gearBox exposes a nice method GetGearRatio()
that will return the current gear ratio of the car, this will be used to calculate the torque that we need to apply to the wheels.
private void Start() {
// Connect the engine to the wheels and gear box (if using YACC's components).
engine.ConnectParts(wheels: wheels, gearBox: gearBox);
}
private void FixedUpdate()
{
engine.throttle = ACCEL_INPUT;
wheels.brake = BRAKE_INPUT;
wheels.steer = STEER_INPUT;
wheels.torque = engine.engineTorque * gearBox.GetGearRatio() * this.gearBox.clutch;
}
Once we have this set up we have an engine that runs and applies forces to the wheels, however we still need to shift up and down since our car is on neutral gear, on our updates we can bind some controls to switch on the engine and set the first gear:
private void Update()
{
ACCEL_INPUT = Input.GetAxis("Vertical");
BRAKE_INPUT = Input.GetAxis("Vertical") * -1f;
STEER_INPUT = Input.GetAxis("Horizontal");
// Let's bind to S the start of the engine and the first gear
if (Input.GetKeyDown(KeyCode.S))
{
StartCoroutine(engine.StartEngine());
StartCoroutine(gearBox.ChangeGear(1));
}
}
Great, stuff now if we have automatic shifting (on by default) on our gear box we're set, if we want to disable the automatic shifting we need to manage Shift up and Shift down appropriately. The gearbox already runs checks for you, you don't need to clamp the gear index and run a bunch of ifs
private void Update()
{
ACCEL_INPUT = Input.GetAxis("Vertical");
BRAKE_INPUT = Input.GetAxis("Vertical") * -1f;
STEER_INPUT = Input.GetAxis("Horizontal");
if (Input.GetKeyDown(KeyCode.S))
{
StartCoroutine(engine.StartEngine());
StartCoroutine(gearBox.ChangeGear(1));
}
// Let's shift up on up arrow
if (Input.GetKeyDown(KeyCode.UpArrow))
{
StartCoroutine(gearBox.ShiftUp());
}
// Let's shift down on down arrow
if (Input.GetKeyDown(KeyCode.DownArrow))
{
StartCoroutine(gearBox.ShiftUp());
}
}
Now we have a working engine and wheels, but we are missing a speedometer, let's add a speedometer to the car, we will use the YACC.Speedometer
component, this component will display the speed of the car in km/h, the current gear and the RPM of the engine.
public Speedometer speedometer;
private void FixedUpdate()
{
engine.throttle = ACCEL_INPUT;
wheels.brake = BRAKE_INPUT;
wheels.steer = STEER_INPUT;
wheels.torque = engine.engineTorque * gearBox.GetGearRatio() * this.gearBox.clutch;
// Read the speed of the car, this will be used to update the speedometer
// and display the speed of the car in km/h.
// We are the rigidbody velocity and then we convert it to km/h, but we can also use
// the wheels rpm to calculate the speed of the car to make a realistic odometer if we want to,
// let's stick to this more accurate method.
speed = carRigidBody.velocity.magnitude * 3.6f;
kmh = Mathf.Lerp(kmh, speed, Time.deltaTime * 4f);
speedometer.SetInfo(kmh: kmh, gear: gearBox.GetCurrentGear(), RPM: engine.RPM);
}
On your UI canvas or UI Document now you can add the YACC.Speedometer
component and link it to the speedometer
field of your car controller, this will display the speedometer on your UI.
Congratulations, your custom car is done! 🎉