Rug Racers: Ultimate
C++ Raylib
Rug Racers: Ultimate is an arcade PvP racing game where you control an actual carpet. Built during 10 weeks with a team of 6 people, I was the lead programmer and worked on gameplay, rendering, input, physics and tools.
Physical setup and some competitive racing!
In-game footage. In this video, two young kids are playing the game for the first time.
About
Rug Racers: Ultimate began as an idea to take the punchy retro-arcade feel of old racing games such as OutRun and to put it on an actual carpet. Our first step was to study how to implement pseudo-3D rendering similar to OutRun. Instead of using an engine like Unity, we chose to hand-craft the engine on top of raylib for achieving the retro rendering feel.
There was one question: how would we design levels without an editor? I proposed using an image editing program as the level editor. By having a single image per entity-kind, we could put them on different layers and place entities by painting pixels into named layers. Each image would be a top-down map of all entities of a kind in the level. The brightness of a pixel controls the height of an entity, which allows for placing around clouds or birds or stacking buildings. I implemented level loading using stb_image with hotreloading to allow for easy iteration, as well as a hotreloading config-file system using cJSON which let us programmers expose tweakable variables for anyone on the team to edit. We knew that not using an engine like Unity was a risk and could put too much dependency on us programmers, so implementing these systems was crucial for the project's success.
Rendering
To render the world efficiently, a Z-sorted array of each sprite entity is built during level loading and a slice of that array is rendered each frame depending on the player's Z-coordinate. The entities all have 3D world coordinates that get perspective-projected into 2D and drawn back-to-front using raylib's 2D sprite drawing functions. Everything is drawn into a 320x360 rendertarget which is draw to the screen to emulate a lower-resolution screen. The curves in the level were a special effect done by translating the sprites horizontally according to a 1D "level curvature" texture. During playtesting, we got feedback that the sprites felt flickery. I extended raylib's sprite shader by adding pixel art antialiasing, which helped a lot.
Physics
The player physics are simple in essence, using just a 2D position and a velocity which gets continuously added into the position. I implemented pixel-perfect collision detection against sprites: if an artist drew a hole in a sprite, then a player could just fly through that hole without extra work. This is implemented by checking all sprites in the Z-sorted list that the player might cross through in a frame, doing an AABB check, and looping through the rectangular area of pixels that the player would collide with. If most of the pixels are opaque, the player dies and respawns. If some of the pixels are opaque, then an average "bounce" vector is found, and is applied to the player velocity along with a speed reduction, sound effect, dust particles and joycon rumble. If all pixels are transparent, we do nothing.
When a player is respawned after they die, they should spawn in an open / safe position and not in the middle of a stream of buildings. To do this, I keep track of the player's position history and spawn them where they were a few seconds ago. This player history turned out to be useful in other places too. I used it to implement the slipstream feature, which is a wind tunnel that a player leaves behind, giving the other player an easier time to catch up. I also used the history to record a flythrough of the map into a file, and made the main menu camera fly through the world using the recording. This made the main menu feel more energetic and inviting. We had Gotland Game Conference in mind when designing the game and wanted to attract a lot of attention from bystanders - just like a real arcade game would.
An unfortunate playtester hitting all the obstacles on the right
Player Input
When the game idea was pitched and greenlit, we hadn't yet discussed how to actually make a controllable carpet. As long as we had up/down/left/right inputs, we'd be happy. There were various ideas: distance-measuring strings attached to the carpet, joysticks underneath, taped wii controllers... We settled on Nintendo switch controllers and velcro tape thanks to their light and compact form. It was my task to figure out the rest.
A joycon has both an accelerometer and a gyroscope sensor. The accelerometer tracks acceleration in each axis, while the gyroscope tracks the angular momentum about each axis. The goal was to extract the absolute position of the controller (holding the carpet up should steer up, down to down, etc...). The proper way to find it would be to integrate the accelerometer data to get the velocity, and to integrate the velocity to get the position. However, small errors would make the position and velocity drift away from their true values quickly. The same issue applies to integrating absolute orientation using the gyroscope input.
After some experimentation with drift-reduction attempts, I realized that I could utilize the earth's gravity that shows up in the accelerometer data as a constant source of acceleration. It changes depending on how the controller is rotated / which axis the gravity is facing towards. This can be used for finding the absolute rotation! My solution was to take this estimate, apply smoothing to it to reduce noise from small accelerations, and add back some gyroscope input to get back some sharpness. I bet this kind of tilt input is a bog-standard technique, but I learned about it the hard way.
Playtesting in action. In this version, we didn't have carpets yet and had two joycons per player. Only near the end, I scrapped this in favor of one joycon in the middle of a carpet which resulted in easier, more predictable controls.Other things
There were lots of little fun polish features I worked on. For example, I made the particle system sprite player, the respawn screen transitions, camels and birds that bounce away when you hit them, and the interactive main menu where you lift the carpet up for "ready". But my favorite is the scoreboard and the carpet-controlled text editor for entering your name. You can see it in the first video at the top of the page at 2:29. This was the true challenge of the game to many players.
You can play the game on PC if you have two Nintendo Switch joycons and two fashionable carpets. A keyboard works too. Download the game from itch.io now!
A normal day at the officeRug racing at the Gotland Game Conference