This one's a retro 3D racer, the kind you'd find in an 80s arcade. You'll get a curvy road, a car to drive, trees flying past, other cars to pass, and lap times to beat.
It uses the ThreeDee Module, which draws flat shapes with perspective so far away things look smaller. Same deal as the other tutorials: play the demo, mess with the code, hit Run. If you're new to MDog Engine, start with the platformer tutorial.
MDog Engine is already imported in these demos, and the screen is 256 by 192. For your own game, grab the import line from Getting Started.
In 3D, positions get a third number, z, for how far away something is. The camera looks forward along z, so bigger z means farther ahead. Driving is just adding to the camera's z every tick. That's it.
The road is a bunch of stripes, each 4 long, in two alternating colors. The stripes are the whole reason it looks like you're moving. Every tick we build a new ThreeDeeScene with only the stripes you can see, from 3 in front of the camera out to 100. Each stripe is a ThreeDeeFloor of grass across the screen, with the road on top made of ThreeDeeShapes (flat shapes made from 3D corners).
A scene draws things in the order you add them. So we add the farthest stripes first, and the closer ones get drawn on top. This "far stuff first" idea comes up again later.
The 6s are how wide the road is, if you want a highway.
Now you get a car. Its position is a Vector3, and it moves forward by its speed every tick. Up speeds you up, letting go slowly coasts, and down brakes. Going off onto the grass slows you down too, so stay on the road.
The camera sits 5 behind the car, lined up with it, so the car always stays in the middle of the screen and the road moves around it instead. The car itself is a few ThreeDeeRectangles (rectangles that always face the camera): wheels, body, window, and tail lights, each added after whatever it covers.
Steering moves you sideways more the faster you go, so you can't slide sideways while you're stopped. Real cars can't either.
A straight road isn't much of a race. roadCenter() now curves the road back and forth by adding two Math.sin() waves together, so the curves don't all look the same. Every stripe asks roadCenter() where it should go, so the whole road follows along. Now you actually have to steer.
Trees line the road every 10, switching sides, which really helps you feel the speed. This is where "far stuff first" gets trickier: trees and the car can be in front of or behind each other, so they all go in one list, get sorted from farthest to closest, and get added in that order. The road is flat, so it all goes in first, under everything.
The 14 controls how sharp the curves are. Try making it bigger if you want to suffer.
A race isn't really a race without other cars. Six of them drive along the road, each in its own lane and at its own speed, a little slower than your top speed. A car's lane is just how far it is from the middle of the road, so they follow the curves too.
Here's a fun cheat: there are only six cars, but it feels like a lot more. When one falls way behind you, it gets moved way ahead, past where you can see, and comes back like it's a new car. If you're the slow one, cars that get too far ahead get moved back behind you.
Rear-end someone and you get put right behind them and slowed down. Everything's on the ground, so cars only need to be close in x and z to crash.
The road now loops, and a lap is 600 long. The trick is that roadCenter() uses waves that repeat exactly every 600, so every lap has the same curves. There's a checkered finish line and a banner over it to mark the start of each lap.
Your lap is the car's z divided by the lap length, rounded down, plus one. When that number goes up, you just finished a lap, so the game saves how long it took and starts timing the next one. Times are in ticks, and there are 160 ticks in a second, so divide by 160 to get seconds.
You can make lapLength shorter for quicker laps. The waves are built from it, so the track still loops.
Finally, the race gets a start and a finish. The title screen shows the road slowly rolling by. Press space and everyone lines up, then a 3, 2, 1, GO countdown holds the cars still until it's done. After 3 laps you get your total time and best lap, and your car coasts to a stop.
Same as the other tutorials, each screen is its own function and setActiveFunction() switches between them. drive() now takes whether the keys work, so the title and results screens can use it too, with nobody driving.
Some ideas if you want to keep going:
groundY along the road, the same way roadCenter() changes x.localStorage (see Saving Progress), and show it on the title screen.For the last and biggest one, there's the adventure game tutorial.