Hitboxes that Feel Good: Reinventing the Goomba Stomp

If you’re in a hurry, skip to the approach I used.


We’re developing a game that utilizes an age old mechanic – the Goomba Stomp.

Ready Set Goat gameplay clips

There’s a common classical approach to this mechanic. A single, monolithic hitbox representing the physical space of the character. And that space is used for everything – stomping, getting hurt, and bumping into walls. (If you can’t tell, I’m foreshadowing here.)

Here’s what the Goomba Stomp looks like in Mario, with an overlay of the hitboxes next to it:

Mario stomping Mario stomping with hitboxes

By the way, there’s a great video on YouTube that overlays the hitboxes for an entire speedrun. This is just fun to watch; check it out:

Courtesy user qvidz on Youtube

The first thing you’ll notice is, you suck at Mario.

Just kidding, this is an AI playing the game. At least I think it is…

The second thing you might notice is, the hitboxes are tight. They are typically smaller than the character, which gives the player some breathing room. You can overlap a few pixels with an enemy – maybe a hair on a mustache, a cheek, or a toe – and not be damaged.

There’s some fudge factor here, and that makes the game slightly more forgiving. This is generally considered a good approach and should help produce a good Game Feel. Mario’s movement and feel were largely responsible for its success back in the 80s.

Tight hitboxes are a tried and true method for implementing this mechanic. So why devote a whole blog post to them?

In the course of implementing the Goomba Stomp in Ready Set Goat, I learned that just because it worked in one game, doesn’t mean it would work in mine. And after weeks of playtesting, we decided that something needed to be changed.

1. Tight Hitboxes

With tight hitboxes, Mario can stand next to a Goomba and not get punished for being a few pixels off. This is great. It’s forgiving to the player; they probably won’t even notice when it happens. They’d just notice if it wasn’t there.

But here’s the problem:

Mario visually stomps an enemy, but gets killed

See that? That extra breathing room works both ways. Now it benefits the Goomba.

In this scenario, Mario appears to stomp some of the enemy’s pixels, yet the player receives no reward. What’s worse is that the Goomba continues moving, eventually slamming into Mario, causing the plumber’s untimely demise.

In Ready Set Goat, the action is much more fast paced than Mario; there’s less time for the player to react to a misstep. So these weird situations can happen way more often. Let’s look at some other hitbox approaches and see if they’re any better.

2. Loose Hitboxes

Loose hitboxes work the opposite way. They give the player a huge footprint, allowing them to stomp from greater distances. In fact, the player might earn a stomp without any pixels touching. Right out of thin air.

Mario doesn’t quite earn a stomp yet, but is awarded one

There are a few downsides to this approach. The first is that it might be too easy to stomp. You might think that that makes the game less challenging. But it actually makes it less predictable. In some cases, the player might not want to stomp because the implications could lead them to their death. So by trying to make things easier, we’ve just made the game more challenging.

In Ready Set Goat, choosing when to stomp is just as important as choosing when not to stomp.

Stomping an enemy at the wrong time could accidentally bounce the goat off into a different enemy.

The worst problem though, is that the player might time a jump really close to the enemy, and clip them on the way up, resulting in the player’s death.

Mario should safely jump over the Goomba with this trajectory. But the hitboxes are too big and he gets owned.

3. Pixel Perfect

Pixel Perfect hitboxes are an opinionated response to the question “How much breathing room should I give the player?” Its answer is exactly none. The player’s stomps will connect predictably, but the player gets no leeway if they make a mistake.

Unforgiving pixel-perfect collisions

While the community over at /r/hitboxporn might disagree, I believe this approach leads to an unforgiving game. While testing it out, I found myself frustrated with the exactness of Pixel Perfection.

Of course, there are simple ways to correct this. For example, you could only count a collision if more than a certain percentage of pixel overlap. But that still suffers from the problem of being unpredictable.

4. Composite Hitboxes

The last common approach I can think of is Composite Hitboxes. However this approach doesn’t answer the question “How much breathing room should I give the player?” It gives you a more accurate representation of the player’s volume (just like Pixel Perfect), but the developer is still left to decide whether that space should be Tight or Loose.

Example of composite hitboxes

The approach by itself doesn’t solve anything for us. But what about the idea in general? Multiple hitboxes… maybe we’re on to something here.

The Elephant… Er…. Goat in the Room

After considering these four approaches, finally, it hit me like a ton of brick blocks. The hitboxes in Mario (and games like it) are having an identity crisis.

The space where Mario attacks from is the same space in which he’s vulnerable. To complicate things further, it’s also his physical space in the world; platforms and walls use this hitbox to determine if Mario bumped into them.

To some, this sort of ambiguity might be an obvious problem. To me, it was an epiphany.

A New Player Has Entered the Game

To solve the problem, I looked outside the platformer genre for inspiration. A common pattern from a different genre gave me some ideas.

Fighting games use a different concept; a hurtbox and a hitbox. A hurtbox is the space where the player is vulnerable; where they get hurt. A hitbox is where a player’s hits land.

Fighter games break out character space into two types: Hitboxes and Hurtboxes

A quick note on terminology: I come from a First Person Shooter background. In FPS’s, we define a hitbox as the area where a character can be hit. If you shoot a bullet at their hitbox, you hit them.

But fighting games would call that a hurtbox.

Regardless, in Mario, there is no differentiation. Your hurtbox is your hitbox, and vice versa.

The swapped terminology is bound to cause confusion, possibly anger. I’m sticking to the FPS-friendly terminology for the rest of the article, because that’s what I know best. But the point of this article is less about terminology, and more about the concept. Call these boxes whatever works best for you in your own projects. AttackBox and DefenseBox are one suggestion.

The different identification of these boxes led me to my final approach: multiple boxes, each defining a different spatial representation of the player.

Hitboxes and Hurtboxes and Bounding Boxes, Oh My!

I categorized and defined the three different spatial representations of the characters:

  1. Hitbox
    The area where the character can get hit (where they are vulnerable)
  2. Hurtbox
    The area where the character hurts others (where they attack)
  3. Bounding Box
    The space the character occupies according to obstacles (platforms, walls)

Then I organized my colliders in game to represent this scheme.

Final box design of my Goat character

And I did the same thing with the enemies.

Then I set up my game engine (Unity) with the following rules:

  1. Player Hurtboxes should collide with Enemy Hitboxes
  2. Enemy Hurtboxes should collide with Player Hitboxes
  3. Players or Enemies bump into the environment

(I explain how to do all of this in Unity further below)

The Results

I’m really happy with this solution, it solves a lot of the problems of the other four.

The player can get a stomp even when the Goat is a few pixels away. This fixes the problem with Tight Hitboxes. See how the Goat’s Hurtbox and the Enemy’s Hitbox connect even from some distance:

But they can still narrowly avoid an Enemy on the upward jump. This is an improvement over the Loose Hitboxes. Check out how the Goat’s Hitbox and Enemy’s Hurtbox near-miss eachother in this gif:

The player gets room to breath, but can still stomp without feeling cheated.

I didn’t want my game to be too easy, but now I at least have control over how easy it is. For example, I could increase the width of the Goat’s Hurtbox and the Enemy’s Hitbox with no negative side effects. This would make it easier for the Goat to get a Stomp in, and still stay safe while jumping upward.

It’s pretty easy to get this all set up.

Implementation in Unity

Step 1: Setup your layers

For a one-size-fits-all solution to this problem in Unity, you’ll need six layers. You could probably get away with less, but this really makes it obvious what’s happening under the hood.

6 Layers for Hitboxes That Feel Good™️

Step 2: Setup the collision matrix

You’ll need to tweak the collision matrix to support this scheme, as follows:

Notice how few checkmarks there are. There’s really only 3 collisions we need to worry about

  1. Player hurts an Enemy
  2. Player gets hurt by an Enemy
  3. Player or Enemy bump into the environment

Step 3: Setup your characters’ boxes

Now for the actual characters. Each box will be represented as a Collider2d (this concept works in 3d too however). Since Unity only allows one Collider per GameObject, we need to store 2 of the Colliders on child GameObjects. This makes the solution a bit more complex, but it’s manageable.

Here’s an example of my player prefab, Goat’s, hierarchy:

My parent GameObject gets the Bounding Box Collider. Therefore, it’s also on the BoundingBoxes layer.

The two children, Hitbox and Hurtbox, get their Colliders on the appropriate layers as well.

Note that all my colliders have Is Trigger checked. That’s because I don’t use the built in Unity Physics system for Ready Set Goat. I handle all of the collision interactions manually, and triggers simply notify me when they happen. Triggers don’t simulate physics reactions, they just alert you when an overlap occurs. If you want to use Unity Physics with this approach, you might want to uncheck the Is Trigger box, and add some RigidBody’s to the root GameObject.

Anyway, here’s that gif of the goat again, showing what these 3 colliders look like in one image.

The same approach is taken with my enemy characters, except I use the EnemyHitboxes and EnemyHurtboxes layers instead:

Step 4: Attack!

For the final step, you’ll need to add code that reacts to collisions. I like to put “attack” code to the attacker itself. A simple way to do that is to put a script on your Hurtboxes’ GameObjects.

For example, on my Player’s Hurtbox, I want to handle Stomps.

A script that detects when a Player stomps on an Enemy, and applies damage as necessary

Your script might look something like this

using UnityEngine;

public class Stomp : MonoBehaviour {
    private float _prevY;

    private void FixedUpdate() {
        _prevY = transform.position.y;
    }

    private bool IsDescending() {
        return transform.position.y < _prevY;
    }

    private void OnTriggerEnter2D(Collider2D collider) {
        if (IsDescending()) {
            Debug.Log("Player stomped an Enemy. Destroying the Enemy.");
            Destroy(collider.gameObject);
        }
    }
}
C#

One thing to note here: Stomps can only occur if the player is descending vertically. They can’t stomp upward. For simplicity’s sake, I’m calculating that by tracking their previous y coordinate and ensuring that their current y coordinate is lower.

Your enemies will need to attack too. Let’s add a script for that:

The attack script that enemies have

And the code is pretty similar, except we don’t need to check if the enemy is descending vertically:

using UnityEngine;

public class KillPlayerOnTouch : MonoBehaviour {
    private void OnTriggerEnter2D(Collider2D collider) {
        Debug.Log("Enemy touched a Player. Killing Player.");
        Destroy(collider.gameObject);
    }
}

Step 5: Report

There’s one thing missing here. In both the Stomp and KillPlayerOnTouch scripts, the colliding object (a Hitbox) gets destroyed. If you recall, that object is actually a child of the character. We want to destroy the entire character, not just their Hitbox. To do this, one more script can help.

Note that I’m no longer working on the Hurtboxes. I’m placing this script on the Hitbox. Here’s an example of my Player’s Hitbox:

The InformParentOnDestroy script will simply call Destroy on a specific object when the Hitbox itself is destroyed. We could guess what the Parent is, say, by calling transform.parent.gameObject. But in practice, your character prefab hierarchies will be much deeper and complex. It’s better to be explicit here. Here’s an example of what that code looks like:

using UnityEngine;

public class InformParentOnDestroy : MonoBehaviour {
    public GameObject Parent;

    private void OnDestroy() {
        Destroy(Parent);
    }
}

Simple enough.

Next Steps

This example makes a lot of assumptions, and there’s still a lot of work to do to get this production ready. But if you’re working on a similar game mechanic, I hope this sets you in the right direction.

Some other things I’ve done to make sure Ready Set Goat feels good to the player.

Choose hit/hurtboxes that favor the Player

The favored character (usually the Player), should have a larger hurtbox than their hitbox. The unfavored character (the Enemy), should have the opposite; a larger hitbox than hurtbox. That will ensure that they Player’s Stomp gets detected before the Enemy’s touch. You can experiment with other hitbox/hurtbox sizes to accomplish something similar though.

Fix Your Timestep!

This can help ensure that every subtle movement the characters make is processed, independent of the framerate. It’ll ensure your characters don’t end up moving through eachother, leaving you with a chicken and egg problem (did the player’s stomp land first or did the enemy’s hit land first?).

Execute Stomp code before KillPlayerOnTouch code

There’s no great way to ensure one component’s OnTriggersEnter methods get executed before another’s in Unity.

My personal approach is to create a new Monobehaviour (let’s call it CollisionResolver) and add it to the stage. Now Stomp and KillPlayerOnTouch must no longer Destroy any GameObjects directly. Instead they must inform CollisionResolver that a collision occurred. CollisionResolver can then process all the collisions it heard about on Update, in whatever order it chooses. In my game, it would process all Stomp reports before KillPlayerOnTouch‘s, because I want to favor the Player’s attack.

That approach is just a bit outside the scope of this post, so I leave it as an exercise to the reader.

 

11 Tips for Building a One-Button Game

The allure of one-button games for developers is clear: it’ll be easy! Easy to:

  • Build quickly
  • Onboard users

Both of these can be true, but there’s something missing: developers also want their games to be good. With that pesky requirement, suddenly we’re trying to:

  • Build something fun quickly
  • Onboard users and keep them playing

The result: there are lots of one-button games, particularly on mobile, but few that succeed.

We started building Ready Set Goat as part of a one-button game jam in late 2019, lured by the siren’s song of a quick and easy project. Many months later, we’ve built something we’re proud of (and can’t stop playing), but only after a series of detours. In the process, we collected some tips to share with the next one-button victims. Of course, many of these apply to all styles of game:

Ready Set Goat in action…note the Gondolas for tip #3
  1. One button doesn’t mean one action. In Ready Set Goat (aka RSG), you can tap to jump, double-tap to double-jump, and long-press to jump a little higher, all with a single jump button.
  2. Make your actions context-specific. Press jump when on the ground? Do a ground jump. Already in the air? Do an air jump. After you squash an enemy? Bounce off for an extra jump. Chain together three hits in a row? Something special happens, but I won’t spoil the surprise.
  3. Automate everything that isn’t your primary action. In RSG, we used our one button for jumping, so our goat-agonist moves forward automatically. When the goat hits a wall, it reverses direction and heads back the other way. To add control, we placed two gondolas on the map that you can hit to reverse direction. Voila, the jump button now grants some directional control.
  4. Optimize for performance and fast re-plays. Most successful one-button games are going to be arcade-y. This means fast action and careful timing. If you want players to get hooked, it has to perform smoothly on your chosen platforms, and it has to be easy to replay over and over again. Don’t put unnecessary features or obstacles in your game flow that will slow players down.
  5. Avoid glitch frustration. You face stiff competition for players’ attentions, and with this type of game, early frustration for perceived unfairness is a killer. In RSG, this meant lots of jump forgiveness and careful tuning of the hitboxes.
  6. Script the early game. New players should be able to learn and improve quickly, and at least one scripted early stage helps. Many one-button games end up being “endless” style games with randomized challenges, but this can really frustrate new players.
  7. Show the player how they’re improving. In RSG, we save your high score, congratulate you on every new one, and have a leaderboard. All pretty standard stuff. One less-common piece we added: your average score. Even though you won’t set a new high score every run, you can see your average tick up over time as you improve. Very satisfying.
  8. Make sure you can play for a few minutes or a few hours. One-button games are often used as a quick diversion, but not exclusively. Your game needs to be fun for 5 minutes on your lunch break, 15 minutes on the bus, or three hours at home on the weekend. If you can’t check all of those boxes, you will struggle to attract long-term players.
  9. Hidden depth for more advanced players. New players should feel like they can master the game quickly, while advanced players should constantly find new ways to edge up their scores. This requires hidden layers of strategy that emerge as you improve. In RSG, we included a number of subtle ways to move around the map and combos that become essential to progress. The higher your scores go, the more hints we drop about advanced play.
  10. Cut features early and often. If you want to build a simple game, keep it simple. Avoiding scope creep is key to any kind of product development, but especially so for a one-button game. An example: we toyed with a wall-cling feature early on, but quickly discarded it when the first few iterations just weren’t fun.
  11. Cute art. Well, it made sense for us: RSG is a game about a cute goat. Make sure your art style matches the gameplay, but for broad-appeal you can’t go wrong with cute.

Disagree? Got more tips to share? Hit me up any time at eric@subpixel.net!

 

The Golden Rule of Indie Game Dev

Every indie game you’ve played is a survivor. For each success there are a hundred failures, and for every failure, another thousand left unfinished. Maybe 10,000. The reason: everyone who loves games wants to make one, but they are the hardest art form to make well. They are interactive and interdisciplinary and asymptotic. They are deeply engaging and endlessly frustrating, to build and to play. Players are unforgiving and the market is saturated. And yet we keep trying, because our love for games blinds us to the odds, the logistics, the costs, and the doubts.

The lucky few that launched…

So, you’ve been working on that dream project but launch seems impossibly far away. Forget a success; how can you escape the fate of the countless that never even finish? I generally eschew one-size-fits all advice, but there is one golden rule that I believe every successful game developer follows:

Play your game, all the time, every day.

Play it constantly. Play it to the point of exhaustion. Play it until your friends and family intervene. Play it some more.

Play until your eyes bleed. Figuratively.

There are three big questions that explain why:

1. Is it fun?

You’ll know based on how much you want to play. If you’re spending hours playing and it’s not fun, you’ll be motivated to keep tweaking or pivot. Are you running out of things to do? So will your players. Are you getting bored? Your players are asleep. Hoping to get friends to help to build, test, launch, or share it? Good luck, unless you have a serious bankroll. The truth is, if it’s not fun, you won’t even want to finish it yourself. You need to keep iterating on your gameplay until you want to play it all the time, every day. The only way to know that you’ve arrived is by playing it again, and again, and again.

2. What should I build next?

The more you play, the more you’ll focus on what works and what doesn’t, and the better ideas you’ll have. Play the game for a few hours and you will find yourself wanting a few specific things. Those things should be the top of your priority list. Skip playtime for a few days and you’ll find yourself wasting time on non-essential work that derails the project.

3. Is it working?

You can’t rely on others for deep testing. Any tester can tell you if something works, but only you will know if the game works as intended. Of course, the best way to test is to play the game, all the time, every day. You need to know the quirks, the bugs, the rough patches, and the holes so that you can prioritize your work and (probably) cut scope down to what is essential for a launch-able product.

Suggestions

So, how do you follow the golden rule? A few suggestions based on my experience over the last decade in both game and non-game software development. (Note: these generally apply to any type of product development, but I’m focused on game dev.)

Ease of Access: Make sure your game is accessible to you. Whatever your target platforms, you should be set up for easy and frequent deploys of your latest work. There should be no friction for you to play your game any time.

When working on Ready Set Goat, we used Unity’s Cloud Build to push the game directly to our phones multiple times daily.

We also prioritized offline play early so that we could pick up the game any time/anywhere.

Schedule Play: It’s not a break or a luxury, it’s a necessity. Expect to have several hours of playtime built in to every full day of work, for everyone on the team.

For Ready Set Goat, we started and ended every day with rounds of play. We also took the game on vacations where we’d have no cell service, ensuring we’d play the game for lack of anything else to do. When addiction to the game started really eating into dev progress, we knew we’d hit on something.

Prioritize Fun: When choosing what to build each day, focus on things that are going to make you want to play more. Forget platform support, art pipeline, optimizations, matchmaking, tutorials, or anything else that isn’t going to make your daily play more fun. All that will come later once you’ve got the fun part down and you’re hooked on the gameplay.

On Ready Set Goat, hours of play revealed too many unexpected deaths for our goat-agonist. The feeling of the game being “unfair” was really frustrating, so we scrapped feature additions and platform expansion to focus on something specific: the hitboxes. My dev partner Jimmie detailed the solution here, and without that investment we’d have wasted time expanding a sub-par game to multiple platforms, surely to fail on all of them.

Following this golden rule won’t guarantee you a good game, but I do believe this is the safest guiding principle for getting a game finished. For those who made it this far, I’d love your thoughts and feedback at eric@subpixel.net.

 

So, what is Subpixel?

Welcome! We’re a new studio building games. We’ve worked together in gaming and non-gaming over the past decade, and it turns out games are more fun. We’ve got two projects in the works:

Ready Set Goat: A free mobile platformer that’s not quite like anything else you’ve ever played. Click here for more details; live in early 2020!

Secret project: A massive adventure of a 2.5d side-scroller with strategic combat, original platforming puzzles, and layer upon layer of mystery. Occupying a giant 100 story skyscraper, you’ll unravel the mystery as you explore every floor and squeeze every ounce out of your abilities. This is a game we can’t wait to play, so we’re working fast to get an early alpha ready in 2020.

We’ll be looking for collaborators in art, programming, and beta testing, so reach out (contact@subpixel.net) if you’re interested or just want to say hello!

– Jimmie and Eric