There's no getting around it. Sprint Racer has sucked up a huge chunk of my life. And yeah, a lot of that time probably should've been spent focusing on school and professional development. But I'd hesitate to say that it was wasted time. I had a lot of fun making it, and I think it made me a better programmer and designer. And beyond that, it was a nice escape from the stresses of work and university.
Now that we're 5 years out from release (yikes), I think it'd be a good idea to look back at the project and talk about
what my thought process was
what I think worked
and more importantly
what I think could've been done better.
Superfans already know that Sprint Racer was largely based on my map Mario Kart in Minecraft (MKMC) from 2017. But did you also know that the game's code was based on another map I'd worked on called Parkour Grand Prix? Former StickyPiston staff member Mezimo shot me a DM in 2018 looking to make a parkour game with a timer and leaderboard system, and I accepted the gig. I used the project as a chance to learn datapacks and mcfunction which had just been introduced into Minecraft. I ended up liking functions so much that I decided re-do the programming for MKMC, a map which had previously been powered by a hulking mass of command blocks at world spawn. Parkour Grand Prix's game state system, timer, and editor tools were used as the base for this new project.
For a long while, I'd been interested in submitting a map to Minecraft Realms to try and gain some street cred, but I also knew that MKMC would never fly on Realms due to copyright infringement. Thus, the idea was hatched to create a Realms version of MKMC with all copyrighted material trimmed out. The project needed a new name, so I gave it the temporary name "Sprint Racer" with the hope that I'd think of something better. And... I never did. The name stuck.
Mini-games on Minecraft Realms are temporary, repeatable, and don't save any data, so I decided to structure Sprint Racer to accommodate this. Where MKMC resembled a traditional console kart racing game with unlockables and a tiered menu system, Sprint Racer more resembled a coin-op arcade machine. Once players join the server, all they need to do is press the "ready" button and the game basically plays itself.
Of note: MKMC already had an "Arcade Mode" because of a server collaboration with mapmaker NeoMC. When Neo asked me to bring Mario Kart to his server in 2018, he requested that the game be automated, and so Arcade Mode was born. After a few months, a talented builder named MCFilms helped in this process by designing a totally new lobby for Arcade Mode, and this alternate lobby became THE lobby for Sprint Racer. MCFilms and I later co-founded Team Scripted.
Sprint Racer took about 9 months to make, and this included a few months dedicated to testing. So you might think what I'm about to say is kind of ridiculous:
I think that Sprint Racer v1.0 was under-baked.
Sprint Racer v1.0
lacked music (unlike MKMC)
lacked a level editor (unlike MKMC)
lacked alternative game modes (unlike MKMC)
lacked polish
performed poorly
had wack item balance
Hindsight is 20-20 right? Through the process of learning Minecraft as an engine and iterating on a project over time, you come up with lots of ideas that seem obvious in retrospect. But even still, I think v1.0 could've been executed better. My mindset was something along the lines of "this is just a port for Realms, it's not meant to replace MKMC". That was kind of a cop-out.
Maybe I'm being a little harsh. SR v1.0 did bring some cool new stuff to the table, like
AI opponents (although pretty scuffed at first)
item settings
new variants of Item Chests
Regardless, Sprint Racer v1.0 was kind of a shaky start. But with consistent updates, the game improved dramatically and attracted many more players.
In v1.1 and v1.2, everything that was missing from MKMC was added back into Sprint Racer. By this point, I'd embraced the idea of phasing out MKMC.
We once again had
music
Time Attack mode
a custom track editor
user-submitted tracks
We also starting adding some totally new, never-before-seen features, like
elimination modes
teams mode
a custom grand prix editor
The later updates (v1.4-v1.6) did some heavy QOL lifting, by
revamping the lobby to look insanely good
making battle mode faster and encouraging offensive play
re-working the in-game HUD (no more position display in the middle of the screen...)
helping visualize where the checkpoints are via particles
fixing an avalanche of bugs
I think Sprint Racer is in far better condition now compared to launch. It's sort of the opposite of a death by a thousand cuts. Life by a thousand... kisses? Sure, let's go with that.
For future projects, though, I should really take more time to polish and improve them before shoving v1.0 out the door. Sprint Racer's been downloaded and played a lot by Minecraft: Java Edition standards, and it bothers me that half of those plays were before SR v1.4.
As a developer or artist, it's just really hard to look back on your old work. Like, I can't even look at MKMC anymore.
Probably one of the most important changes happened in v1.3: an item balance re-work. This was invisible to players, but it had a huge impact on the core gameplay of Sprint Racer. Under the new system:
there was a 0% chance of getting speed items in 1st
offensive items were concentrated in mid-to-front positions
Speed Boost items were given more generously near the back.
The items given in the back turned out to be especially important.
One big issue with the old system is that players toward the back of the pack were extremely likely to receive the Elytra and Ender Pearl items. Elytras and Ender Pearls have massive comeback potential... but only if you know the mechanics and track layout. This was a deliberate design choice, as I wanted huge comebacks to be possible with high effort/skill. But this approach ignored the reality that players way in the back were generally inexperienced or sensory overwhelmed, and they were frustrated that the items they kept receiving were "useless". So to combat this, we now give a lot more triplet Speed Boost items to these players. Elytra and Ender pearl still happen, but the odds are a lot lower. This change successfully prevented blowout games where some players would be 40+ seconds behind 1st at the end.
Speaking of time differences: another huge change in v1.3 was that the players' item luck switched to being based on time behind 1st place rather than placement. Naively, you'd assume that the player's number position is all you'd need to know, but players are often distributed unevenly along the course. For example: imagine 1st place is 20 seconds ahead of 2nd. How is 2nd place supposed to close the gap with weak items? Another example: imagine all the players are clumped together. Last place is only 2 seconds behind 1st, but they get served super powerful items that let them slingshot ahead. Determining item luck via distance to 1st solves these problems.
Most racing games use distance calculations to determine how far ahead or behind racers are, but unfortunately this is too expensive to do in mcfunction. So to get a similar effect, we opted to take timestamps at every checkpoint and evaluate the time difference between 1st place and a given player. This also had an added benefit of making it harder to farm powerful items at the start of a race, since the timestamp system doesn't kick in until you hit your first checkpoint.
Overall, these changes to item balance had a very positive impact on Sprint Racer. Races became more interesting and competitive without being frustrating or cheese-able.
Below you can see a (slightly outdated) sheet detailing the madness that is Sprint Racer's item randomization.
Here's a Design Delve video talking about the balance of Mario Kart. I think it does a great job explaining the challenges of designing a combat racing game with random items.
While the system Sprint Racer ended up with isn't perfect, I think we did a decently good job avoiding the "mug-zone" problem and enabling players in the back to reach the front again with some effort.
Okay, we're done with history and updates. Let's look back on the finished product.
We'll start with the bad and end with the good.
I dove head-first into developing Parkour Grand Prix and Sprint Racer without fully doing my homework on how Minecraft commands had changed in MC 1.13. This resulted in a lot of silly mistakes that hurt performance.
Selectors. The selector @e[name=w] was used in many places to refer to Sprint Racer's global variable holder entity. When you use this selector, Minecraft searches through every loaded entity in the world and checks if they have the specified name. I was under the impression that this was cached in some way, but this wasn't the case. This gets especially nasty when there's a lot of entities loaded in the world. These selectors could've been made much faster by making them type limited and chunk limited. Example: @e[name=w,type=armor_stand,x=0,y=0,z=0,distance=..1]. This second selector is a lot better because it only looks for entities of one type in a single chunk.
Fake players. One feature I missed when moving to Minecraft 1.13 commands was that the new /execute command allowed you to evaluate scores on players of any name, even if they weren't loaded in the world. This lets you store global variables basically anywhere. I could've avoided most of slowdown from mistake #1 if I'd just used fake players to store global variables instead of using entities.
Execution context. I was still new to the idea of execution context with the new /execute command, so I missed a lot of opportunities to outline clusters of commands that were all executed by the same entity. By outlining stuff done by one entity in its own function, you avoid having to do an @e selector for each line of code.
Desperate measures. When v1.0 was wrapping up, I noticed that the game had noticeable slowdown in some situations. Instead of going in and fixing the underlying performance problems, I made a silly 10Hz mode that kicks in when things get too intense. This meant that parts of the code had to be designed with both 10Hz and 20Hz execution in mind. This was dumb, and it caused a lot of bugs and inconsistencies over the course of Sprint Racer's life.
Localization. I didn't know that you could use translation keys in text components along with a resource pack's language files to support localization. This means that earlier versions of Sprint Racer had hard-coded text. I did place all functions relevant to displaying text into their own directory (which made localization support easier down the road), but that didn't really help anyone else who wanted to localize Sprint Racer at the time.
I've fixed a lot of these problems since v1.0, but the issue of not using fake players is so ingrained in the code that I can't reasonably fix it without doing everything over again. Modern Sprint Racer performs a lot better than it did at launch, but it's still not as efficient as it could be.
Sprint Racer doesn't have a strong visual identity. It's a little easier to recognize from a glance now that we have the new HUD, but it still looks pretty much like vanilla Minecraft. I think making a unique set of block textures would've gone a long way in making the map look nicer. Strong art direction is also massive for attracting players to an independently made game. I think this is something we improved on significantly in later projects.
Many of Sprint Racer's tracks don't play well with the items or checkpoint system. Some lack useful places to use items like Jump Boost or Elytra, some lack interesting checkpoint shortcuts, and some have checkpoint exploits that are so powerful they ruin all competition. This is largely a consequence of how Sprint Racer was made. Almost half the tracks were built between 2013 and 2015, long before the game mechanics and items were decided on. Ideally you'd design your game mechanics first, then design levels that complement those mechanics.
This is kind of a funny one. If you look through the item handbook from the lobby, you'll see that the items are divided into 5 categories with exactly 4 items in them. Was there a good reason for there to be exactly 4 items in each category? Not really. Ideally you'd design as many items as you feel are necessary and beneficial to the game, but instead I designed based on a weird quota system. Some items (like invisibility or totem of undying) felt kind of superfluous.
The superficial item problem gets amplified when you consider that every item has to have an enchanted variant. This gets you items like the Super Jump Boost, which I don't think anyone has ever intentionally used to gain a real advantage in a race.
Also questionable is the fact that Race and Battle mode share the exact same item set. I do think it's impressive that the items work relatively well in both game-modes without any modifications, but it still would've been better to tailor things better to each mode. I sort of mitigated this issue in Battle mode by skewing random item drops towards offensive items and away from speed items.
Designing Sprint Racer to be an arcade-machine-like experience had a knock-on effect: there's no progression or unlockables. This is the one area where I feel that MKMC still has a leg up on Sprint Racer. Unlocking all the tracks in MKMC was fun and felt rewarding, and that feeling is largely missing from Sprint Racer.
I do still think that not having unlockables and advancements was the right call for Sprint Racer since it was primarily designed to be a Realms minigame. Not having satisfying progression is sort of a limitation of the platform. Sure, we could've made a map for the Adventure category instead, but it's common knowledge among Realms mapmakers that these maps don't get as many plays. I feel like the Minigame tab was a better fit for Sprint Racer, anyway.
Sprint Racer does still have hidden cheat codes, though. And while inputting them is slow and clumsy, I still think it's a good solution to the problem of not having persistent saves. Once players find cheat codes, they stick in memory and can be used again in later play sessions. A lot of other Realms mapmakers have used this system, too, so I think that's a testament to how effective this is.
Some players expressed a desire for a dedicated grand prix mode like MKMC had. I sort of brushed this idea off for a while because I didn't see the value in just repackaging the game's content in a new format like that. But now, after playing some old racing games and adding a grand prix to Good Phantom, I finally see the value in giving players a more curated way of experiencing the content of a game. I can't really put it into words, but it just feels right, and sometimes routine can be comforting.
Now that I think of it, it may have been a good idea to split Sprint Racer into an "Arcade" mode and "Traditional" mode. This was the case in MKMC, although I never saw anyone try switching it outside of NeoGames. Making two different systems that run your game can be difficult, though, and it's possible that having two Sprint Racers could confuse players who try to share their experiences with each other on the internet.
Battle mode, while fun, is mostly just chaos. It's four minutes of noise with almost no signal. Players don't strategize much aside from trying to enchant certain items; they just grab items and then attack whoever they happen to bump into. I think some more decision making would improve this mode. I have a few ideas on how this could be achieved:
My first idea is to bring back the power-ups from MKMC. Every few minutes, a power-up will spawn at a predetermined spot on the map. This power-up
grants a buff like strength or regeneration,
drops when the holding player is KO'd,
lasts for a limited amount of time, and
is always visible to all players.
When a power-up spawns, all players have to make a choice whether they want to risk going for a power-up that is highly sought after if they want to greatly increase their KO potential. This also brings players together and creates a memorable central conflict.
Second idea: mix in some predetermined item locations with the usual random item boxes. With a few item boxes (capsules) having a permanent predetermined item they grant, players would naturally memorize the spawn locations of the items they like. This would add some more decision-making to Battle mode and would also result in players competing over certain resources. This was the central idea behind the mode "Resource Control", but nobody ever played that due to how difficult it was to memorize 30+ item locations for each track.
Idea #3: add a minimap that's actually useful. Knowing where other players are reduces aimless wandering. Knowing where specific items/power-ups are makes changes #1 and #2 meaningful. This is technically possible in Minecraft, but would be very hard to implement. It would be smart to do this in a different game engine.
Allowing players to hold multiple items at once opens up great potential for strategy in the item system, but Sprint Racer doesn't really take advantage of this. Having items interact in a sort of rock-paper-scissors fashion would make player interactions a lot more intimate and engaging. Although designing sets of items to work this way while remaining intuitive to use would be a challenge.
There's a ton of tracks in Sprint Racer now, and every individual track has a leaderboard for both "Items" and "Itemless" play. The sheer number of leaderboards in Sprint Racer means that many tracks get little attention. Not many players have the time or willingness to grind every single level. I don't really want to get rid of either of the categories, since I think they both have value. Kind of a tough situation, here.
If we were able to integrate track rankings into the game directly, this could make fragmentation less of an issue. Although automatic in-game rankings are also very susceptible to hacking (see Super Meat Boy).
Whether you're aware of it or not, all racing games have some sort of checkpoint system. You need to be able to track players' progress through a race, ideally in a way that doesn't open the door for game-ruining exploits (see Grumble Volcano from Mario Kart Wii). In MKMC and Sprint Racer, these checkpoints are embraced as a real and visible part of the game. The checkpoints prevent capital C cheating, but they also encourage players to cheat as much as possible within the constraints of the system.
Players, when aware of the checkpoint system, form a deeper relationship to the levels they're racing on. Whereas a lot of racing games are easy to auto-pilot through as you just point your car in the direction of the road, Sprint Racer's checkpoints encourage paying closer attention by rewarding creative routing between checkpoints. This is further complemented by the items, many of which open up new movement possibilities.
I suppose this system also removes the frustration you find when trying to speed-run other racing games. The opaqueness of the typical lap/checkpoint system results in a lot of trial and error as you probe levels for unintended shortcuts.
...and by AI, I mean bots. We called them "AIs" when Sprint Racer was released in 2019. This was before generative AI became a really hot topic. Sprint Racer's bots are not a machine-learning-neural-network-black-box kind of thing-- their behavior is governed by deliberately designed algorithms and in-level waypoints placed by track creators.
The obvious benefit of bots is that they make the game livelier and re-playable for solo players or tiny groups. A lot of Minecraft games are designed with larger groups of players in mind, sort of like the massively popular multiplayer PVP games that everyone plays on Hypixel or in the broader video game sphere. There's nothing necessarily wrong with designing games for big groups, but the reality in Minecraft map-making is that these large player groups almost never happen. There's no centralized server browser for an individual Minecraft map-- everyone who installs your map has their own bubble of players. The people playing your maps will almost certainly be individuals or small groups of friends, so it's a good idea to factor that into your design. Sprint Racer plays well with 4-12 players, so we use bots to help keep the player count in that range.
The other major benefit of bots in Sprint Racer is that they teach players how to think differently. Bots are programmed to sometimes take shortcuts and use items in some "creative" (pre-defined) ways. New players that witness a bot perform a creative shortcut will experience an "aha!" moment where they realize that they can get saucy and take some creative routes to reach each checkpoint instead of mindlessly following the road. And aside from giving free improv lessons, bots are generally good practice dummies to help players get better at your game.
Something to note about the bots in Sprint Racer is that, by default, they have adaptive difficulty. This means that, under the default game settings, the difficulty of the bots will rise if you beat them and fall if you lose to them. Some people might raise an eyebrow at this feature since it can come across as unfair or confusing, but I stand by my decision. My reasoning is that
there's no concrete award for winning a set in Sprint Racer, so unexpected upsets aren't a big deal
most players won't interact with the AI difficulty settings before playing
young children (or generally inexperienced players) play Sprint Racer frequently, and I think the bots should relax for them instead of completely annihilating them
adaptive difficulty pushes players to improve. Winning is always within reach for a player at any skill level, but it takes just a little extra effort to remain in 1st overall.
Anyways, while the bots may not be the smartest, I think they do their job well and are impressive for a Minecraft project. Playing against bots will never be as fun as a lobby filled with real people of course, but having them in Sprint Racer definitely improves the game.
What really convinced me to start putting bots in my games was this Super Bunnyhop video. I highly recommend giving it a watch:
I'll admit, putting player health (and KOs) in a racing game was a gutsy move. It worked out pretty well in Sprint Racer, though.
The lower a player's health is, the greater the risk is they get KO'd and sent back to the last checkpoint (yeah no dip, sherlock). Players can sneak and heal themselves at any time, but doing so slows you down significantly. This leaves players with a choice: do you slow down to heal, or do you gun it to the next checkpoint under the risk of getting sent back? This results in some tense moments where you're closing in on a checkpoint with low health and opponents behind you. This also results in players paying closer attention to each other as they watch for opportunities to get a KO on a low-health player.
Hitting a checkpoint restores 4 HP, and getting a player KO also restores 4 HP. I think those small details really compliment this system.
Races clock in at 2-4 minutes, lobby interludes are 30-40 seconds, and battles are 4 minutes. Under the default settings, a 5-round set takes around 20 minutes. This is a reasonable pace to get through 5 unique levels, but it's not so long that any level overstays its welcome. The length of rounds also means spectators never have to wait more than 4 minutes to play. I personally think Sprint Racer's pacing is spot-on for the kind of game it is.
The lobby interludes could be argued to be unnecessary. You can skip them with certain game settings, but I see them as a good thing. It gives players a needed break between rounds, and also allows players to talk to each other. It's common for players to talk about what happened in the previous round or discuss what map they want to vote for during lobby interludes. The quality of conversation in-between games is naturally better, too, since players aren't preoccupied by a chaotic race/battle.
Opening up the custom track editor to players and giving it an easy-to-use frontend turned out to be great for Sprint Racer. Having a level editor in a Minecraft map really keeps with the spirit of Minecraft, and that's great because Sprint Racer by itself doesn't do much to justify why it's using Minecraft as a game engine.
I was very pleasantly surprised with how many custom tracks people made with the track editor. Honestly, I expected less than 5 would ever be made, but people have made dozens at this point. Most of the bonus tracks added after v1.0 were initially made by other people in the custom track editor, too. I can't express how happy I am that people cared enough about this game to make new levels for it 😊
The most impressive example of user-created custom tracks is ReflectedMantis' Mario Kart Track Pack, which boasts 20+ new tracks, all with support for bots.
Maybe a somewhat controversial take, but I think The Gauntlet was a great inclusion. It's the only (non-hidden) track with the "Master" difficulty rating, and it only appears after you've played around 15 rounds in a single session. A lot of players moan when this track comes up in voting, but usually in a lighthearted "oh boy, here we go" kind of way. And somehow, this track always seems to win the vote.
This track is very unlike any other race track. It's long, obstacle-ridden, has no items, and has a 10-minute time limit. Many players have obsessed over clearing this track, getting the best time, and finding tricks (unanticipated by me) to cut times down even further. If you want evidence of how successful this individual track was, I'd recommend searching "Sprint Racer The Gauntlet" on YouTube and seeing how many results come back. This almost makes me think that gauntlet levels should be their own game-mode-- Food for thought if I do another game like this again.
Here's the current Gauntlet world record, held by Eel_Ken.
The hubbub around The Gauntlet also helped the Time Attack mode and its leaderboard take off. There's been some hefty competition for top spot in most of the tracks, and it makes me very happy as a developer seeing a bunch of players who have an encyclopedic knowledge of all the levels in my game.
Sadly, The Gauntlet has been losing relevance as more tracks get added. With more tracks in the random track pool, it's less likely that this one pops up in a regular play session. That's unfortunate.
Tracks have ratings from "Novice" to "Expert". Higher difficulty tracks don't enter the random track pool until you're a couple rounds in. Having new players start out on easier levels is a common practice in single-player games, but Sprint Racer is among the few that do it in multiplayer. This system was a great success in my view, as it greatly improved first impressions for new players. Difficulty ramp-up worked well for a multiplayer game with randomized level selection.
Experienced players might not necessarily need to start on the Novice tracks, but it's still good to have a warm-up when starting a new game session.
I think track difficulty ratings also inspired some restraint in custom track making. When you give players a level editor, a common impulse people have is to just make really hard levels. That's still supported, but track makers have to make a choice about how to label their map. Someone labeling their track as "Novice" will naturally chill out a bit when designing the track. A track marked as "Expert" or "Master" will give players an idea of what they're getting into when they play someone else's custom track.
A really common issue in multiplayer Minecraft games is that the start mechanic is grief-able. Usually what you'll see in a central button that both starts a game (begins a countdown) and cancels it if pressed again. All it takes is one rogue player to ruin the game with this system. Sprint Racer's handheld-streetlight-ready-up-button-thing™ feeds into a majority vote to start/stop the game, and this system successfully prevents griefing while being simple enough for players to understand it. There is a bit of a language barrier that slows a few people down when starting a game, but I haven't seen anyone not figure it out.
We also prevent griefing in some other ways, like
having an optional "Admin mode" that protects game settings in the lobby
having races time out 80 seconds after the first finish, which prevents stalling.
The lobby is a great playground for players to run around in and get familiar with the movement mechanics and items before they're thrown into a race or battle.
Super-casual players will probably just start up the game right away without exploring and get the straight-forward experience they were looking for. Curious players, however, will find that there's a lot more to the game when they find all the options and hidden menus in the lobby. In a way, Sprint Racer sort of mirrors the level of interest that the player has.
...and, obviously, the lobby is drop-dead gorgeous. Like... wow. Shout-out to MCFilms and Jayjo, they did a fantastic job with it.
Thanks for reading this hilariously long developer commentary. I've had a lot of these thoughts on my mind for years. It feels good to let them out.
And it's probably a good idea to jot all of this down so any ideas I've got don't get lost when I inevitably decide to make another racing game ;)