Steffan's Game Development Portfolio

See Projects Go to git repositories
RaceGame
May 2016 - July 2016

The Race Game was my assignment for the last semester of the first year during my education as Game Developer at ROC Friese poort. The assignment was to make a racegame with the wheelcollider physics of Unity. To add some extra challenge for myself I decided to add an AI that uses bezier curves to follow the road

External Links
Racegame

CarAI
The car ai class is a child class of the CarController class. It uses a bezier spline as orientation for the location of the road. Using the bezier spline it can determine wether it should go full gas or use its breaks when a turn is coming up ahead. To prevent oversteering around the spline it uses an PID.

CarAI2.cs
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4.  
  5. public class CarAI2 : CarController {
  6.     private BezierSpline bezier = null;
  7.  
  8.     public float PathLengthPerKmh = 0.5F;
  9.     public int waypointAhead = 10;
  10.     public int stepsPerCurve = 25;
  11.     public float velocityBreakFactor = 0.15F;
  12.     private int totalSteps = 0;
  13.     private float progressPerStep = 0;
  14.     private int currentWaypoint = 0;
  15.  
  16.     private float steering = 0;
  17.     private float gas = 0;
  18.     private float brake = 0;
  19.  
  20.     PID pid;
  21.  
  22.     protected override void Start()
  23.     {
  24.         base.Start();
  25.         bezier = GameController.Instance.track;
  26.         totalSteps = bezier.CurveCount * stepsPerCurve;
  27.         progressPerStep = 1 / (float) totalSteps;
  28.         pid = new PID(1, 0.2F, 0.001F, 0.001F);
  29.         currentWaypoint = SelectClosestWayPoint();
  30.     }
  31.  
  32.     protected override void FixedUpdate()
  33.     {
  34.         MoveAI();
  35.         //print("Current waypoint: " + currentWaypoint + " Total Waypoints: " + totalSteps + " Velocity: " + GetAverageVelocity() + " Gas: " + gas + " Brake: " + brake + " Speed: " + rb.velocity.magnitude * 3.6F + " Desired Speed:" + GetAverageVelocity() * velocityBreakFactor);
  36.         move(steering, gas, 0, brake);
  37.     }
  38.  
  39.     void MoveAI()
  40.     {
  41.         //maxWayPoint = GetMaxPointToCalculate();
  42.         currentWaypoint = SelectWayPoint();
  43.         if (rb.velocity.magnitude * 3.6F < GetAverageVelocity() * velocityBreakFactor)
  44.         {
  45.             gas = 1;
  46.             brake = 0;
  47.         }
  48.         else {
  49.             gas = 0;
  50.             brake = 1;
  51.         }
  52.         Vector3 toWayPoint = bezier.GetPoint(currentWaypoint * progressPerStep) - transform.position;
  53.         float dotProduct = Vector3.Dot(transform.right, toWayPoint);
  54.         steering = pid.calcPID(steering, dotProduct);
  55.     }
  56.  
  57.     int GetMaxPointToCalculate() {
  58.         float totalDistance = Vector3.Distance(transform.position, bezier.GetPoint(currentWaypoint));
  59.         float desiredDistance = (rb.velocity.magnitude * 3.6F) * PathLengthPerKmh;
  60.         int wp = currentWaypoint;
  61.         while (totalDistance < desiredDistance) {
  62.             wp++;
  63.             totalDistance += Vector3.Distance(bezier.GetPoint((wp - 1) * progressPerStep), bezier.GetPoint(wp * progressPerStep));
  64.         }
  65.         return wp;
  66.     }
  67.  
  68.     float GetAverageVelocity() {
  69.         float totalVelo = 0;
  70.         for (int i = 0; i <= waypointAhead; i++) {
  71.             if (currentWaypoint + i >= totalSteps)
  72.             {
  73.                 totalVelo += bezier.GetVelocity((0 + i - (totalSteps - currentWaypoint)) * progressPerStep).magnitude;
  74.             }
  75.             else {
  76.                 totalVelo += bezier.GetVelocity((currentWaypoint + i) * progressPerStep).magnitude;
  77.             }
  78.         }
  79.         return (totalVelo / waypointAhead);
  80.     }
  81.  
  82.     int SelectWayPoint() {
  83.         Vector3 positionCurrentWaypoint = bezier.GetPoint(progressPerStep * currentWaypoint);
  84.         Vector3 toWayPoint = positionCurrentWaypoint - transform.position;
  85.         float dotProduct = Vector3.Dot(transform.forward, toWayPoint);
  86.         if (dotProduct < 5F)
  87.         {
  88.             if (currentWaypoint >= totalSteps)
  89.             {
  90.                 return 0;
  91.             }
  92.             else {
  93.                 return currentWaypoint + 1;
  94.             }
  95.         }
  96.         else {
  97.             return currentWaypoint;
  98.         }
  99.     }
  100.  
  101.     int SelectClosestWayPoint()
  102.     {
  103.         int i = 0;
  104.         int closestPoint = 0;
  105.         float distanceClosest = Vector3.Distance(transform.position, bezier.GetPoint(0));
  106.         List<Vector3> pathPoints = new List<Vector3>();
  107.         for (int x = 0; x <= totalSteps; x++)
  108.         {
  109.             pathPoints.Add(bezier.GetPoint(x * progressPerStep));
  110.         }
  111.         foreach (Vector3 pathP in pathPoints)
  112.         {
  113.             float distance = Vector3.Distance(transform.position, pathP);
  114.             if (distance < distanceClosest)
  115.             {
  116.                 closestPoint = i;
  117.                 distanceClosest = distance;
  118.             }
  119.             i++;
  120.         }
  121.         Vector3 toWayPoint = bezier.GetPoint(closestPoint * progressPerStep) - transform.position;
  122.         float dotProduct = Vector3.Dot(transform.forward, toWayPoint);
  123.         if (dotProduct < 0)
  124.         {
  125.             closestPoint++;
  126.         }
  127.         currentWaypoint = closestPoint;
  128.         return closestPoint;
  129.     }
  130. }
CarAI2.cs