This month I've been tackling two fairly difficult tasks...
Where are we?
It's a simple question and one that's fundamental to a racing game. You can't have a racing game unless you can work out which cars are in which position and split times between those cars. It seems like an easy problem to solve but in circuit racing there are lots of things to consider (such as lapped cars). In other games they rely on keeping a linear representation of the track in code and, based on track position, move the cars along that linear representation. In my case it's hard to generate that linear representation without adding something that would allow me to determine exactly what point a car is on the track itself (probably by tracking the outer and inner edges of the circuit and using that to determine the car position). This sounds like quite a lot of overhead and, as I'm targeting mobiles, I felt it was overkill.
The track is already divided into three sectors and I record sector times but this isn't accurate enough to allow me to work out race position. I can tell if two cars are within the same sector but working out which car is ahead in that sector is not possible when just recording current sector and current lap.
To solve the issue I decided to track three pieces of information:
- Current lap
- Current sector - incrementing each time a car enters a new sector and not resetting each lap
- Current sector entry time
I could then turn these three pieces of information into a numerical representation of each car's race position:-
car_progress = (current_lap * 100000000) + (current_sector * 100000) + (100000 - (sector_entry_time * 10))
(The numbers are held in variables obviously.)
Using current lap and current sectors multiplied by modifiers is fairly clear to understand but to determine position within a sector I add the value for an additional sector and then subtract the sector entry time. By subtracting the time we make sure that a car that enters a sector later than another (i.e. with a greater sector entry time) ends up with a smaller number than a car that entered earlier.
Once we have this figure for each car then ordering them is easy and from that we can determine the race order. It's important to use an appropriate data type as, if using floats, you may find they get rounded.
The multipliers are used to stop the data from each piece of information overlapping in a reasonable length of time; we basically separate the individual bits of information by several orders of magnitude. The size of the multipliers depends on the game, if the lap was longer then you'd need to adjust the modifiers accordingly. Your mileage may vary 😉
I also worked on calculating split times for the player. This can be summarised as the time elapsed between the player and, say, the car that's ahead reaching the same point on the circuit. I could calculate only when the player crosses the start/finish line but I think it's more fun to be able to calculate this more frequently. To do this I tracked sector entry times for each car and then it's a case of looking up the entry time for the relevant sector and relevant car. Knowing the race order allows me to work out the "relevant car" and we already know what sector the player car is in. We then determine the difference between the times to determine the split.
Where is everyone else?
The other task I've started work is helping the rival cars become more situationally aware. I've started adding "sensors" (ray casts) to the cars so they can detect other cars that are near them. This will allow me to control how the cars behave to defend or attack a position. It will also allow me to implement changes to allow cars to get a "tow" (get into another car's slipstream) for other cars.
I'll be carrying on working on car sensors and behaviour. If there's time I'll work on code to allow AI cars to recover from spins as I didn't manage to work on that this month.