Physics Racing Game – Bots (Part 3)

Hey there! This tutorial is Part 3 of my Physics Racing Game series. Make sure you’ve followed the previous parts before reading this one!


For creating the bots, we’ll use our car object’s code (oCar). So, I’ll duplicate that object, and name it oCarAI. I’ll also give it a separate sprite.


Now the first thing we’ll do is open its Create event, and remove the camera code, since only the player needs the camera:


In the Step event, you’ll see two parts: the first one which we covered in Part 1, that moves the car; and the second one which we covered in Part 2, that counts the laps.


For this part, we’ll need the blue part (laps) to be at the top, and the red part (car movement) to be below it. In other words, their places will need to be swapped:


We are doing this because we will need to use variables from the blue part in the red part.

Now, let’s get to coding the bot’s movement. We’ll be changing the input code, the part marked in yellow:4.PNG

Remove that, and in its place, add this:

//Choose point to follow
var fDist = pathWidth*random(0.5);
var fDir = choose(pathDir+90, pathDir-90);
var fX = nextX + lengthdir_x(fDist, fDir);
var fY = nextY + lengthdir_y(fDist, fDir);

//Set movement
var dir = point_direction(x, y, fX, fY);
var diff = angle_difference(dir, -phy_rotation);
var hor = -sign(diff);

var ver = -1;

if (abs(diff) > 90) ver = 1;

In the code that we made in Part 2, we set variables for the position of the next point on the track where the car had to go (nextX, nextY). We’ll set a random point on the line of that point, and set it so that the car moves towards that point. We could just make it follow the center point, but that wouldn’t be as realistic.

fDist will get a random distance from the center point, up to half of the path’s width. fDir will get the direction to set the point in (left or right), since the base point is in the center of the line.

So, fX and fY will get that point on the line using fDist and fDir.

Now all we need to do is set the movement of the car to steer towards that point. So, it’ll get the direction towards that point in dir. Then it will get the difference between the current angle of the car and the target angle, in diff, and get its sign. I’ll make the sign negative to make it work in accordance to our other code. This will be applied to hor to simulate input for the horizontal axis.

ver will be set to -1 to simulate W/Up being pressed, so that it moves forward. If the absolute difference between the current angle and the target angle is greater than 90 (I’m making it absolute (positive) to take into account values smaller than -90 as well), then ver will be set to 1, to simulate S/Down being pressed, so that it moves backwards. This will help in case the car is turned around and pressed against a wall. (In case your bots don’t turn away after being pressed against a wall, try reducing the 90 value)

Spawning Cars

Now we’ll make it so that it spawns a set number of cars on the track.

If you open oMain and go to its Create event, you’ll see this line that we added in Part 1, to spawn the player car at the start of the path:


Remove it. In its place, we’ll add some new code. But not literally in its place. The new code that we add will go inside the loop that builds the track, at its end:


There, I’ll add this:

//Place Cars
if (i==0){
    var cars = 3;

    for(var c=0; c<cars; c++){
        //Object to place
        var obj = oCar;
        if (c>0) obj = oCarAI;

        var xx = px1 + lengthdir_x(32 + pathWidth*(c/cars), pdir+90);
        var yy = py1 + lengthdir_y(32 + pathWidth*(c/cars), pdir+90);[c] = instance_create_layer(xx, yy, "Instances", obj);[c].phy_rotation = -pdir;

First you’ll notice that the whole code is wrapped around a condition that checks whether i is equal to 0. That is because we only want this code to run when the loop is at the first point; that’s the only place where we want to place the cars.

I’m setting the local variable cars to 3, which is the total number of cars I want to place. Then a for loop will run (with c as its loop variable, since we’re already in another loop that uses i) that will place each car.

First it’ll set the object to place (obj). I’m setting it to oCar so that it places the player car. If c is greater than 0, which means that it’s not the first car, then it’ll use oCarAI, to place bots. This way, the first car placed will be the player.

Then it’ll set the position to place the car at. It’ll start at (px1, py1), which is one end of the line at that point. On that line it will add 32 (so that it doesn’t place a car at the boundary) and the path width multiplied by the loop’s progress.

Then it will create the object at that position. It will also create an entry in an array called that will store the id of each car created in this loop. Then it’ll set that car’s rotation to the direction of the path at that point.

Keep in mind that your path will have to be wide enough to spawn the number of cars you want. Also, in the previous part we added some code in the Draw and Draw GUI events of the car to draw the next line and the lap info. You can remove all that code since it was just for testing purposes and will not be needed.

So now if you run the game, you can see that the bots work:



Now we’ll implement a countdown in the beginning of the race. Open oMain, and in its Create event, initialize global.countdown at 4.

global.countdown = 4;

Now, in its Step event, add this:

if (global.countdown >= 0) global.countdown -= 1/room_speed;

If the countdown variable is equal to or more than 0, it will be reduced by 1/room_speed. Since room_speed is one second, the countdown variable will go down by 1 each second.

Add this in its Draw GUI event:

var guiW = display_get_gui_width();
var guiH = display_get_gui_height();

if (global.countdown >= 0){

    var text;
    if (global.countdown>=1) text = floor(global.countdown);
    else text = "Go!";
    draw_text(guiW/2, guiH/2, text);


guiW and guiH will get the width & height of the GUI layer.

The next block will run only if the countdown variable is at or above 0. It will set the text alignment to draw in the center, both horizontally and vertically.

We want to draw the countdown (3..2..1), but when it goes to 0, we want to draw “Go!” instead of the number.

text will be the variable that will be drawn. If the countdown is at or above 1, it will be set to the countdown, floored (so that 2.4 becomes 2). Else, it will be set to “Go!”; so if the countdown is below 1, it will draw that string instead of the number.

The text variable will be drawn at the center of the screen. Then the alignment values will be reset back to top-left.

Now that we’re done with the countdown system, we need to stop the cars until the countdown reaches 0. So, I’ll open oCar & oCarAI’s Step events, and at the top of both events, I’ll add this:

if (global.countdown>=1) exit;

So if the countdown is at or above 1, it will exit that event, resulting in the event code not being executed at all. So the cars won’t work until the countdown reaches 0:



You can download the project here (GMS2)

I regularly update my site with new tutorials. Make sure you follow now (at the bottom of the page) to stay updated!


If you need free GML help or want to provide GML help to those who need it, join our Discord server! We have a fun little community there (and a fun bot); it’ll be good to have you too!

See ya later, and till then, happy dev’ing!

Thumbnail icon credit:


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s