Project Description
GAM 606, Rapid Game Development; Racing Game
This assignment took some interesting twists and turns on its way through development. Originally, the goal was to create something like a 2D version of Forza Horizon’s Drift Zones, where the goal is to rack up as many drift points as possible between two checkpoints, while still crossing the finish point under an allotted time. With this, the DriftMeter at the bottom originally was a Points Multiplier, basically approximating a DriftStreak and giving you inflated rewards the more tricks you performed. However, the concept never really popped — most likely because a mechanic like Forza’s requires constantly pinging and popping rewards and feedback to the player (the casino/slot machine effect). Since this was beyond the purview of a 48-hour gamejam (how I approached the projects in this class), I had to look for alternatives. Because the car’s mass is set fairly high to prevent any strange Unity Physics behavior on collision, it turns out that the enemy AI doesn’t have to be much more complicated than simply steering towards the next point in a series of points — it’s too hard to knock the enemy AI off course!
The project’s base car controller comes from a PrettyFlyGames tutorial available on Youtube. The Youtube tutorial keeps going beyond what’s represented in this project, including multiplayer and touch support, so check that out!
Programming Notes
Originally I planned to let you hold down Spacebar to increase your drift with the e-brake by adding extra velocity to the car’s transform.right, but it turned out the control was much more enjoyable if the e-brake was on all the time! You can test out the original handling still by holding down Spacebar (the IsEbrakeOn logic is reversed).
void ApplyEngineForce()
{
//Apply drag if there is no accelerationInput so the car stops when the player lets go of the accelerator
if (accelerationInput == 0)
carRigidbody2D.drag = Mathf.Lerp(carRigidbody2D.drag, 3.0f, Time.fixedDeltaTime * 3);
else carRigidbody2D.drag = 0;
//Caculate how much "forward" we are going in terms of the direction of our velocity
velocityVsUp = Vector2.Dot(transform.up, carRigidbody2D.velocity);
//Limit so we cannot go faster than the max speed in the "forward" direction
if (velocityVsUp > maxSpeed && accelerationInput > 0)
return;
//Limit so we cannot go faster than the 50% of max speed in the "reverse" direction
if (velocityVsUp < -maxSpeed*0.5f && accelerationInput < 0)
return;
//Limit so we cannot go faster in any direction while accelerating
if (carRigidbody2D.velocity.sqrMagnitude > maxSpeed * maxSpeed && accelerationInput > 0)
return;
//Create a force for the engine
Vector2 engineForceVector = transform.up * (accelerationInput * accelerationFactor);
//Apply force and pushes the car forward
carRigidbody2D.AddForce(engineForceVector, ForceMode2D.Force);
if (eDrift) return;
//Create a force for the edrift
Vector2 eDriftVector = transform.right * (accelerationInput * accelerationFactor);
carRigidbody2D.AddForce(eDriftVector, ForceMode2D.Force);
}
The Enemy AI steers using Transform.InverseTransformPoint to calculate a new steering angle and input vector to reach the AI’s next checkpoint.
Vector3 relativeVector = transform.InverseTransformPoint(points[pointIndex].position);
float newSteer = (relativeVector.x / relativeVector.magnitude) * 1f; // 1f = maxSteeringAngle delta per tick