A desktop car racing game in Go

Go Faster!

© Lead Image © alphaspirit, 123RF.com

© Lead Image © alphaspirit, 123RF.com

Article from Issue 274/2023
Author(s):

The fastest way through a curve on a racetrack is along the racing line. Instead of heading for Indianapolis, Mike Schilli trains his reflexes with a desktop application written in Go, just to be on the safe side.

A few years ago, I got to test the physical limits of my Honda Fit during a safety training session. A short time later, I discovered I was interested in car racing. It's also a more popular hobby than you might think among Silicon Valley employees, who let their tuned private cars off the leash on racetracks like Laguna Seca in California – maybe because of the strict speed limit that typically applies on freeways in the US.

While studying the topic, I was surprised to learn that it's by no means just a matter of keeping your foot on the gas. If you want to break track records, you have to take the turns exactly in line with physical formulas and always find the ideal line in order to knock those vital seconds off your time in each lap. The physical principles of racing are explained in the reference work Going Faster by Carl Lopez [1]. The book describes exactly how quickly you can enter a turn without the car starting to skid and tells you the angle and time at which the driver needs to turn the steering wheel to lose as little time as possible while cornering.

Learning How to Race

The ideal line through a turn is never going to be the shortest path, which runs along the inside. Instead, the aim is to drive through the curve on a trajectory with as large a radius as possible (Figure 1). Before the 90-degree right-hand bend shown in Figure 1, a world-class driver like Jos Verstappen will initially steer to the left-hand edge of the road and then pull sharply to the right towards the apex. This means that the race car just barely scrapes past the inside of the curve, only to run over to the left side of the road again shortly afterwards on the straight that follows the turn. This means that the radius followed by the car is far larger than that of the turn, and that the car can negotiate the turn at a far faster speed without the tires losing traction or the vehicle skidding.

Figure 1: The fastest path through the turn uses the largest possible radius.

World of Geometry

Figure 2 shows the simulation of a 90-degree turn as a desktop game written in Go with racing animation. The race car, depicted as a green square, speeds upwards towards the curve. The player has to steer the vehicle to the left and right with the H and L keys so that it doesn't hit the side of the road at this breakneck speed, but safely reaches the end of the turn at the top right of the game window. The stopwatch next to the two buttons runs during the animation and displays the elapsed lap time in seconds with an accuracy of two decimal figures.

Figure 2: The car racing game in action on the desktop.

With a little prior knowledge of geometry and video game technology, a simple 2D game like this can be quickly put together using Go and the Fyne framework [2]. To do this, the program runs through a number of frames per second, in each of which it computes the current position of the game figures, which it then refreshes in the graphics. At the same time, it fields user input such as keystrokes or mouse clicks and incorporates these events into the calculations, for example, by adjusting the steering.

In the Beginning Was the Circle

But how do you write a game like this in Go? First of all, you need to draw the "world" for the game. This is best done in such a way that the program can later compute the game's status at lightning speed for each video frame that it is running through. First and foremost, it needs to give feedback about whether the race car is still driving on the road or has already left the contours of the turn and is lying somewhere in the bushes.

The program draws the racetrack's right turn as an overlap of two concentric circles (Figure 3) with the radii r2 (outside) and r1 (inside). But only the upper left quadrant of this shape is of interest for the curve; this is why I used some cleverly placed rectangles in Figure 4 to mask the irrelevant parts of the circles. The blue, gray, and orange areas later disappear in the display, and two additional salmon-colored rectangles define the road at the entry to the turn and its exit. Listing 1 implements this "world," as gamers call it, using the Circle() and Rectangle() functions on a canvas object provided by the Fyne framework.

Listing 1

world.go

01 package main
02
03 import (
04   "fyne.io/fyne/v2"
05   "fyne.io/fyne/v2/canvas"
06   "fyne.io/fyne/v2/container"
07   col "golang.org/x/image/colornames"
08   "image/color"
09 )
10
11 func drawWorld(r1, r2 float32) (fyne.CanvasObject, Car) {
12   bg := drawRectangle(col.Grey, 0, 0, 2*r2, 2*r2)
13   co := drawCircle(col.Lightsalmon, r2, r2, r2)
14   ci := drawCircle(col.Grey, r2, r2, r1)
15   mb := drawRectangle(col.Grey, 0, r2, 2*r2, r2)
16   mr := drawRectangle(col.Grey, r2, 0, r2, r2)
17   in := drawRectangle(col.Lightsalmon, 0, r2, r2-r1, r2)
18   out := drawRectangle(col.Lightsalmon, r2, 0, r2, r2-r1)
19
20   car := Car{Ava: canvas.NewRectangle(col.Green),
21     StartPos: fyne.NewPos(10, r2+r2-1),
22   }
23
24   car.Ava.Resize(fyne.NewSize(10, 10))
25   car.Ava.Move(car.StartPos)
26   objects := []fyne.CanvasObject{bg, co, ci, mb, mr, in, out, car.Ava}
27   play := container.NewWithoutLayout(objects...)
28
29   return play, car
30 }
31
32 func drawCircle(co color.RGBA, x, y, r float32) *canvas.Circle {
33   c := canvas.NewCircle(co)
34   pos := fyne.NewPos(x-r, y-r)
35   c.Move(pos)
36   size := fyne.NewSize(2*r, 2*r)
37   c.Resize(size)
38   return c
39 }
40
41 func drawRectangle(co color.RGBA, x, y, w, h float32) *canvas.Rectangle {
42   r := canvas.NewRectangle(co)
43   r.Move(fyne.NewPos(x, y))
44   r.Resize(fyne.NewSize(w, h))
45   return r
46 }
Figure 3: Two concentric circles define the 90-degree road turn …
Figure 4: … and thanks to a couple of rectangles as masks, I now have a racetrack.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Straight to the Point

    With the Fyne framework, Go offers an easy-to-use graphical interface for all popular platforms. As a sample application, Mike uses an algorithm to draw arrows onto images.

  • Chip Shot

    We all know that the Fyne framework for Go can be used to create GUIs for the desktop, but you can also write games with it. Mike Schilli takes on a classic from the soccer field.

  • Wheat and Chaff

    If you want to keep only the good photos from your digital collection, you have to find and delete the fails. Mike Schilli writes a graphical application with Go and the Fyne framework to help you cull your photo library.

  • GUI Apps with Fyne

    The Fyne toolkit offers a simple way to build native apps that work across multiple platforms. We show you how to build a to-do list app to demonstrate Fyne's power.

  • Magic Cargo

    To be able to power up and shut down his NAS and check the current status without getting out of his chair, Mike Schilli programs a graphical interface that sends a Magic Packet in this month's column.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News