Search Unity

Go Kart Style Racing AI

Discussion in 'Scripting' started by tookydo, Nov 4, 2014.

  1. tookydo

    tookydo

    Joined:
    Jul 15, 2014
    Posts:
    75
    Has anybody found or made a Go-Kart style racing AI they have found cheap or have made them selves and are willing to give to me. Or even found a series on YouTube that explains how to make a go-kart style AI in unity? If you decide to give it to me, I would add you in the description of my game if I release it.
     
  2. cl9-2

    cl9-2

    Joined:
    May 31, 2013
    Posts:
    417
    The Gotow Car Tutorial Project is a good start. It's not actual AI, but waypoint following, meaning that cars will crash into each other.

    I've made a Github repository here. And also I've converted it to C# with the help of an online converter.

    The zip file of the C# project can be downloaded here
     
  3. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
  4. tookydo

    tookydo

    Joined:
    Jul 15, 2014
    Posts:
    75
    Thank you! I will be sure to try that out and see how it works with my game and Ill tell you how it goes!
     
    cl9-2 likes this.
  5. tookydo

    tookydo

    Joined:
    Jul 15, 2014
    Posts:
    75
    If you could help me that it would be great! Ive made a very very simple AI before but I vaguely understand how it works so if you could help me that would be awesome!
     
  6. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
    Well, today's QCS testing for me, so I can only write a short explanation of how I did it. will hopefully write something better after I get home. :D

    So here goes:

    Basically, you have to make a line of GameObjects, all named something like "Waypoint0," "Waypoint1," etc. I have them all under one container, tagged "waypoints"
    When you have done this, then, in the script, you get all the waypoints and store them in an array, then set the current one to 0.
    By programming the movement carefully, use a steering vector, generated by using InverseTransformPoint (I think it was that... :p) and then, to make it better, modify the vector a little each frame. that way, the cars follow the full path, and have steering variation too, to make it more exciting. (My car AIs do this, and often, they will merge into the lane next to them unintentionally. It is also possible for a car to wrongly guess it's steering vector, and drive off the edge... :D)

    So that's a summary of how I did it. I will post a full report, and possibly some code to help explain, once I get home. :)
     
  7. idurvesh

    idurvesh

    Joined:
    Jun 9, 2014
    Posts:
    495
    11 days still not get home....need to report CIA?? :p
     
  8. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
    I sort of forgot what day that was... :D I will post it! :)
    the first part is obviously this:
    Code (csharp):
    1. cars.Sort(cars, new UpdateRanks_Laps());
    And the second part is the IComparer module:
    Code (csharp):
    1.  
    2. class UpdateRanks_Laps implements IComparer
    3. {
    4.     function Compare(a : System.Object, b : System.Object) : int {
    5.         if ( !(a instanceof GameObject) || !(b instanceof GameObject)) return;
    6.         var CarA : GameObject = a;
    7.         var CarB : GameObject = b;
    8.      
    9.         if (CarB.GetComponent(Rank_Sys).lap.CompareTo(CarA.GetComponent(Rank_Sys).lap)){
    10.             return CarB.GetComponent(Rank_Sys).lap.CompareTo(CarA.GetComponent(Rank_Sys).lap);
    11.         }
    12.         if (CarB.GetComponent(Rank_Sys).currentWaypoint.CompareTo(CarA.GetComponent(Rank_Sys).currentWaypoint)){
    13.             return CarB.GetComponent(Rank_Sys).currentWaypoint.CompareTo(CarA.GetComponent(Rank_Sys).currentWaypoint);
    14.         }
    15.         else {
    16.             return CarA.GetComponent(Rank_Sys).dist.CompareTo(CarB.GetComponent(Rank_Sys).dist);
    17.         }
    18.     }
    19. }
    this third script is what you attach to ALL of your cars:
    Code (csharp):
    1.  
    2. var waypointContainer : GameObject;
    3. var waypoints : Component[];
    4. var waypointRadius : float = 1.5;
    5. var currentRank : int = 0;
    6.  
    7. //vars for comparison. distance is to next waypoint, current is waypoint, lap is current laps made round track.
    8. var dist : float = 0;
    9. var currentWaypoint : int = 0;
    10. var lap : int = 1;
    11.  
    12. //if finished, change to true. is false at race start.
    13. var finished : boolean = false;
    14.  
    15. function Start () {
    16.     lap = 1;
    17.     waypointContainer = GameObject.FindGameObjectWithTag("Waypoints");
    18.     waypoints = waypointContainer.GetComponentsInChildren(Transform);
    19. }
    20.  
    21. function Update () {
    22.     dist = Vector3.Distance(waypoints[currentWaypoint].transform.position, transform.position);
    23.     if(dist <= waypointRadius)
    24.     {
    25.         currentWaypoint++;
    26.         if(currentWaypoint>=waypoints.Length)
    27.         {
    28.             currentWaypoint = 0;
    29.             lap++;
    30.             if (lap > GUI_Racer.laps){
    31.                 finished = true;
    32.             }
    33.         }
    34.     }
    35. }
    Ok, some notes on this:
    1. this is JS code. If you need it ported to C#, I can do this, so just let me know if you need anything!
    2. This code is surprisingly (or not... :D) expensive on a low-end mobile device! So if you're producing a simple racing game for ALL devices, I don't suggest you use this code. (I thought my project was dragged down by physics... I tracked it down to actually be this code!)
    3. For most applications otherwise, this should work.
    4. You will need to lay down some waypoints first before you try setting up anything, as this approach uses them for tracking the objects.
    5. For anything to be picked up by the function, make sure your car objects are in the corresponding array, and all of them MUST have the third script attached.
    Good luck! If any of you need help with this, I can help you set up your scene, as I am currently using this method for my own mobile racer. Just search for "Splash Dash" (Yet to be renamed... I have a new name for it! :D) and my WIP thread should come up.

    I do plan on switching methods myself though, for the reasons stated above: for more than two cars, this is surprisingly expensive on mobile!

    EDIT: IF you want easier integration, rename the script with the third snippet in it "Rank_Sys" Otherwise, you will have to rewrite part of the IComparer so that it knows what type to look for!
     
    cl9-2 likes this.
  9. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
    Oh shoot... wrong thread!
    Here's the AI code. (the stuff above is actually for ranking! :D)
    Code (csharp):
    1. var waypointContainer : GameObject;
    2. var waypoints : Component[];
    3. var waypointRadius : float = 1.5;
    4. var damping : float = 0.1;
    5. var speed : float = 2.0;
    6. var death : float;
    7. var turnSpeed : float;
    8. var playerName : String;
    9.  
    10. private var targetHeading : Vector3;
    11. private var currentHeading : Vector3;
    12. private var targetwaypoint : int;
    13. private var timer : int;
    14.  
    15. var particles : ParticleSystem;
    16. var isHit : boolean = false;
    17. var isBump : boolean = false;
    18. var hitW : Vector3;
    19. var engineNoise : AudioSource;
    20.  
    21. //cache rigidbody, for speedup!
    22. var PhysX : Rigidbody;
    23. // Use this for initialization
    24. function Start() {
    25.     turnMod = true;
    26.     PhysX = GetComponent(Rigidbody);
    27.     waypointContainer = GameObject.FindGameObjectWithTag("Waypoints");
    28.     multiplier = 0;
    29.     PhysX.centerOfMass.y = -0.5;
    30.     prev = transform.rotation.eulerAngles.y;
    31.     waypoints = waypointContainer.GetComponentsInChildren(Transform);
    32.     currentHeading = transform.forward;
    33.     while(true){
    34.         timer -= 1;
    35.         if (timer < 0){
    36.         timer = 0;
    37.         }
    38.         yield WaitForSeconds(1);
    39.     }
    40. }
    41.  
    42.  
    43. // calculates a new heading
    44. private var prev : float = 0;
    45. var multiplier : float = 1;
    46. var isGrounded : boolean = false;
    47. var rayDist : float;
    48. var time : float;
    49. var turn : float;
    50. var frontAxle : GameObject;
    51. var backAxle : GameObject;
    52.  
    53. var upperLimit : float;
    54. var isBoosting : boolean = false;
    55. var turnMod : boolean = false;
    56.  
    57. var splash : GameObject;
    58. var deathBool : boolean;
    59.  
    60. function OnLevelExit () {
    61.     Destroy(gameObject);
    62. }
    63.  
    64. var ptW : Quaternion;
    65. var pt : Quaternion;
    66. var lerpTimer : float;
    67. var variant : float;
    68. var started : boolean;
    69.  
    70. function FixedUpdate() {
    71.     if (GUI_Racer.start){
    72.         if (multiplier > 2.0 && !started) {
    73.             isHit = true;
    74.             started = true;
    75.         }
    76.         else if (multiplier < 1.5 && !started) {
    77.             multiplier = 0;
    78.             started = true;
    79.         }
    80.         else if (!started) {
    81.             isBoosting = true;
    82.             time = 0;
    83.             particles.Play();
    84.             started = true;
    85.         }
    86.         var rot : Vector3 = transform.InverseTransformPoint(Vector3(waypoints[targetwaypoint].transform.position.x,transform.position.y,waypoints[targetwaypoint].transform.position.z));
    87.         var vector = (turnSpeed * multiplier) * (rot.x / rot.magnitude);
    88.        
    89.         if (PhysX.velocity.magnitude >= 0.01 && isGrounded){
    90.             frontAxle.transform.Rotate(Vector3(0,-PhysX.velocity.magnitude*1.3,0), Space.Self);
    91.             backAxle.transform.Rotate(Vector3(0,-PhysX.velocity.magnitude*1.3,0), Space.Self);
    92.         }
    93.         if (Physics.Raycast(transform.position, -transform.up, rayDist)){
    94.             isGrounded = true;
    95.         }
    96.         else {
    97.             isGrounded = false;
    98.         }
    99.        
    100.         //if the car was hit by an item, carry out this action, unless the car is already flying in the air.
    101.         if (isHit) {
    102.                 if (isGrounded){
    103.                     multiplier = 0;
    104.                     PhysX.velocity.y = 10;
    105.                     PhysX.AddTorque(Vector3(0,500,0), ForceMode.VelocityChange);
    106.                     isHit = false;
    107.                 }
    108.                 else {
    109.                     isHit = false;
    110.                 }
    111.         }
    112.         if (isGrounded && !isBoosting && !isHit) {
    113.             multiplier += 0.01;
    114.             if (multiplier > upperLimit){
    115.                 multiplier -= 0.05;
    116.             }
    117.         }
    118.         else {
    119.             multiplier -= 0.02;
    120.             if (multiplier < 0){
    121.                 multiplier = 0;
    122.             }
    123.         }
    124.         //take vectors, and move car if not hit.
    125.         if (!isHit && !isBump) {
    126.         var move : Vector3 = transform.forward * speed * multiplier;
    127.         move.y = PhysX.velocity.y;
    128.         PhysX.velocity = move;
    129.         }
    130.        
    131.         if (isBump) {
    132.             PhysX.AddForce(hitW * 12, ForceMode.Force);
    133.             rigidbody.velocity.y = 0;
    134.             isBump = false;
    135.         }
    136.        
    137.         if(rot.magnitude <= waypointRadius)
    138.         {
    139.             targetwaypoint++;
    140.             if(targetwaypoint>=waypoints.Length)
    141.             {
    142.             targetwaypoint = 0;
    143.             }
    144.         }
    145.        
    146.         time += 0.25;
    147.         //turn the car a little more to either direction, to add variation to movement
    148.         if (time >= 5){
    149.             turn = Random.Range(1.2,3.5);
    150.             variant = Random.Range(-10,10);
    151.             time = 0;
    152.             turnMod = false;
    153.             deathBool = false;
    154.         }
    155.         if (time > 2.4 && time < 2.6) {
    156.             turnMod = true;
    157.         }
    158.        
    159.         if (timer <= 0){
    160.             isBoosting = false;
    161.         }
    162.        
    163.         if (transform.position.y <= death) {
    164.             PhysX.velocity = Vector3(0,0,0);
    165.             pt = transform.rotation;
    166.             transform.position = waypoints[targetwaypoint].transform.position + Vector3(0,2,0);
    167.             transform.rotation = waypoints[targetwaypoint].transform.rotation;
    168.             multiplier = 0;
    169.             deathBool = true;
    170.         }
    171.        
    172.         if (!isGrounded) {
    173.             lerpTimer += Time.deltaTime*2.5;
    174.             if (lerpTimer > 1) {
    175.             lerpTimer = 1;
    176.             }
    177.             ptW = Quaternion.Euler(0,prev,0);
    178.             transform.rotation = Quaternion.Lerp(pt,ptW,lerpTimer);
    179.             isBoosting = false;
    180.         }
    181.        
    182.         if (isGrounded && !deathBool) {
    183. //            transform.Rotate(Vector3(0,Mathf.Lerp(0,vector,0.5),0));
    184.             vector += variant;
    185.             transform.rotation.eulerAngles.y = Mathf.Lerp(prev,prev + vector,Mathf.Clamp01(turn*Time.deltaTime));
    186.             prev = transform.rotation.eulerAngles.y;
    187.             ptW.eulerAngles = Vector3(0,transform.rotation.eulerAngles.y,0);
    188.             lerpTimer = 0;
    189.         }
    190.     }
    191.     else {
    192.         backAxle.transform.Rotate(Vector3(0,-multiplier*10,0), Space.Self);
    193.         time = Random.Range(0,10);
    194.         started = false;
    195.         if (time < 5) {
    196.             multiplier += 0.08;
    197.         }
    198.         else {
    199.             multiplier -= 0.03;
    200.             if (multiplier < 0){
    201.                 multiplier = 0;
    202.             }
    203.         }
    204.     }
    205.     engineNoise.pitch = multiplier*2.5 + 1.0;
    206.     pt = transform.rotation;
    207. }
    208.  
    209. function OnTriggerEnter (info : Collider) {
    210.     if (info.gameObject.CompareTag("Dash")){
    211.         multiplier = 2;
    212.         PhysX.AddForce(transform.forward * 6, ForceMode.VelocityChange);
    213.         isBoosting = true;
    214.         timer = 1;
    215.         particles.Play();
    216.     }
    217.     if (info.gameObject.CompareTag("Water")){
    218.         Instantiate(splash, transform.position, splash.transform.rotation);
    219.     }
    220.     if (info.gameObject.CompareTag("DeathTrigger")){
    221.         deathBool = true;
    222.         PhysX.velocity = Vector3(0,0,0);
    223.         pt = transform.rotation;
    224.         transform.position = waypoints[targetwaypoint].transform.position + Vector3(0,2,0);
    225.         transform.rotation = waypoints[targetwaypoint].transform.rotation;
    226.         multiplier = 0;
    227.     }
    228. }
    229.  
    230. function OnCollisionEnter(info : Collision) {
    231.     if (info.gameObject.CompareTag("Car") && PhysX.velocity.magnitude <= info.rigidbody.velocity.magnitude) {
    232.         hitW = Vector3.Normalize(transform.InverseTransformPoint(info.transform.position));
    233.         hitW.y = 0;
    234.         isBump = true;
    235.     }
    236.     if (info.gameObject.CompareTag("Missile") && info.gameObject.GetComponent(Missile).sender != this.gameObject && isGrounded) {
    237.         isHit = true;
    238.         Destroy(info.gameObject);
    239.     }
    240. }
    241.  
    242. function IsValid (quaternion : Quaternion) : boolean {
    243.     var isNaN : boolean = float.IsNaN(quaternion.x + quaternion.y + quaternion.z + quaternion.w);
    244.     var isZero : boolean = quaternion.x == 0 && quaternion.y == 0 && quaternion.z == 0 && quaternion.w == 0;
    245.     return !(isNaN || isZero);
    246. }
    note though that I have just pulled this straight out of my own game, so it's probably not going to work first-off. Let me know what errors you get, and I will help integrate the code with your project!
     
  10. idurvesh

    idurvesh

    Joined:
    Jun 9, 2014
    Posts:
    495
    Thanks alot for it Fuzzy

    The above line make me Heart break kid o_O;):(

    Although thanks for this stuff really appreciated.....It will surely help alot of people (including me) :)
    :)
     
  11. idurvesh

    idurvesh

    Joined:
    Jun 9, 2014
    Posts:
    495
    Yeah searched for splash dash many games appeared not quite sure which is yours? please provide the link, I am very much excited to see it :)
     
  12. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
  13. idurvesh

    idurvesh

    Joined:
    Jun 9, 2014
    Posts:
    495
    hi, can you post how you implemented camera on it? I am stuck at corners, camera and player do not rotate properly..
    o_O

    by the way neoDas seems unique game name...

    Good luck for it keep us updating :) waiting for final version :)
     
  14. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
    I actually used smoothfollow, of which I then assign the car to the lookAt position variable. (SmoothFollow is a script that comes with Unity: simply import that from the CharacterController package)

    If you're using a custom script for your camera, just pop it here, and I will help fix it up! :)
     
  15. idurvesh

    idurvesh

    Joined:
    Jun 9, 2014
    Posts:
    495
    Yeah I got it work using Quaternion.LookRotation() and rotateTowards...

    Btw do you know how to change from one waypoint system to another at run time? Like suppose I have implemented shortcut way in my track then how to change waypoint system for it ?
     
  16. subzer0

    subzer0

    Joined:
    Dec 10, 2010
    Posts:
    94
    Hello FuzzyQuills,

    I am making a kart race game and I tried to use the above AI script. But I am getting 2 errors:

    1. (73,13): BCE0005: Unknown identifier: 'GUI_Racer'.
    Code (JavaScript):
    1. function FixedUpdate() {
    2.         if (GUI_Racer.start){
    3.             if (multiplier > 2.0 && !started) {
    4.                 isHit = true;
    5.                 started = true;
    6.             }
    7.             else if (multiplier < 1.5 && !started) {
    8.                 multiplier = 0;
    9.                 started = true;
    10.             }
    2. (238,83): BCE0005: Unknown identifier: 'Missile'.
    Code (JavaScript):
    1. function OnCollisionEnter(info : Collision) {
    2.         if (info.gameObject.CompareTag("Car") && PhysX.velocity.magnitude <= info.rigidbody.velocity.magnitude) {
    3.             hitW = Vector3.Normalize(transform.InverseTransformPoint(info.transform.position));
    4.             hitW.y = 0;
    5.             isBump = true;
    6.         }
    7.         if (info.gameObject.CompareTag("Missile") && info.gameObject.GetComponent(Missile).sender != this.gameObject && isGrounded) {
    8.             isHit = true;
    9.             Destroy(info.gameObject);
    10.         }
    11.     }
    How can I solve this?
     
  17. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
    Oh... :D GUI_Racer's my main script in the game I got that from! just substitute GUI_Racer.start with whatever your racing script uses to indicate when the race is starting! :)

    The second bit is actually for a missile power-up, so you can probably delete that. ;)

    Other than that, good luck! If you need help writing a core script, I can definitely help! :)
     
  18. Arthur2996

    Arthur2996

    Joined:
    Oct 25, 2019
    Posts:
    1

    Hi man!!
    I would like to make a motocross video game, I search a physic and IA scripting... I have read your post and I would like to ask you, can you ported your script in C# for unity?

    Thanks in advance