How to Build Hit Boxes In JavaScript for Game Development
Gaming is so prevalent in society that it is hard to find someone who has never played a video game. From hardcore Starcraft champions to Candy Crush moms, there’s a flavor of gaming for everyone. While many people enjoy games, game mechanics aren’t always as widely understood as the strategies used to advance to the game’s next level. In this article I would like to narrow that knowledge gap by introducing the idea of hit boxes through theory and then through building a game.
In an effort to increase my skills as a software engineer I have decided to build a number of games using only HTML, CSS and Vanilla JavaScript. Mastery in development comes from building systems that users love to break, so video games make a lot of sense for learning. This is the first of a number of articles I hope to write on common game mechanics that can be gamed and broken by users if not correctly implemented.
Hit Box Theory
I mentioned the term hit box earlier, but what is a hit box? Hit boxes are a watch dog type of object that watch for and react to collisions with other objects. A collision is any occurrence of two objects attempting to occupy the same space. This concept is similar to a collision you may have with a door while walking through a dark room at night.
We don’t have to worry about hit boxes for rigidly built games like Candy Crush where objects move in predefined directions and increments, but what about games like Fortnite? How does Fortnite know that our character shouldn’t be able to walk through walls and cars or even other characters?
To be able to ensure these real world rules of physics, Fortnite introduces an idea of hit boxes. Your character occupies a space in a 3D level and a hit box is like a full body suit that covers your entire character. This full body hit box watches for any collisions, or objects trying to occupy the same space as your character, and will trigger events such as preventing forward movement if you collide with a building, or a change in vertical position if you walk off the edge of a cliff.
This is a very general view of a hit box. Most 3D models are composed of a number of hit boxes. This hit box composition serves two main functions, the first being to allow abstract shapes to better model complex objects. The second reason is to map specific reactions to specific hit boxes. This idea is very common in shooter games as we don’t just care about collisions between a character and a bullet, but we also care about what part of a character the collision occurs on. If we were adding hit boxes to our character, let’s call him Jerry, then we may care about and have different reactions to a collision to Jerry’s head versus his legs.
This concept of hit boxes sounds a lot like literal 3D boxes, but hit boxes can be any 3D shapes, 2D shapes or even a singular coordinate. In theory you could create very complex meshes that would match a characters every contour, but this is not only unnecessary but also nearly impossible. As a game developer you want to keep track of as few points and objects as possible. By doing so you are able to reduce the computing power needed to run the game.
A good example of this simplicity can be found in the popular game, Street Fighter. While Street Fighter is known for their complex character designs and quick button combos, the way they model their characters’ hit boxes is very simple. Check out the following hit box modeling for Street Fighter:
Notice the detail (if you can see it through the shading) of the character’s model compared to the abstractness of the hit boxes. The key of any kind of development is to reduce complex systems into easy to understand and code rules for declaring how an object works. Here we take a very complex 3D model, that acts in a 2D plane, and reduce the hit boxes to literal 2D squares.
While some games may implement more complex meshes and more hit boxes for the sake of real life modeling, it’s not always necessary. For review, hit boxes are a way to watch for and trigger events for a specific object when it collides with other objects. We set hit boxes to know when two objects overlap, and respond accordingly.
A Real Life Game — (Viking Run)
To look at a real example of hit boxes in code I decided to build a recreation of Google’s Dino Jump (the full code can be found here). This game is an infinite loop 2D scrolling game in which you are a Viking, Sven, who is traveling back to his ship after visiting the local QuikPillage convenience store. The penguins, who are dormant much of the year, are back to panhandling for fish. If you touch them, by viking law you must stop and share some of your fish. This will end your game. You can jump to avoid the penguins, but even a viking can only jump so high.
Now that we know how to play, how do the game mechanics work? The most important mechanism for this game is creating and applying hit boxes. For this game our two elements that we care about are our viking, Sven, and our penguin, Finn who we crudely modeled below:
While it’s easy to draw circles, it’s not as easy to calculate circle hit boxes. Instead of circles, we will use squares that give us nice straight edges (I’m not a fan of using pi unnecessarily in coding). Now that we have decided Finn and Sven are squares, do we really need to pay attention to all of their sides?
Remember, each side represents a potential infinitesimal collection of points that we have to keep track of. Okay, you don’t have to convince me, let’s ditch some edges. I would like to get rid of three edges of the squares that model Sven and Finn’s hit boxes. Let’s take a look at what we have left and then talk about our assumptions that allow this simplification:
Now we have five sides, but how did we get here? Again, I don’t want to watch everything or even a lot of things. How did we ditch three of our eight sides? With our 2D infinite scroller game we have one main collision which occurs when Sven has both horizontal and vertical overlap with Finn. To collide we must have:
- Sven’s right side position must be greater than Finn’s left side position
- Sven’s right side position must be less than Finn’s right side position
- Sven’s bottom side position must be less that Finn’s top side position
Note: I am not accounting for the back side and trailing conditions in this example. If you want a challenge, try to add the additional checks for collisions involving Sven’s back side.
If all of these conditions are met then we can confirm a collision has occurred and will have something like this:
Simple rules, right? Now that we’ve boiled down our watchers, let’s code this game. I assume that you have a certain amount of experience with HTML and CSS so I won’t go through how I added in our characters, or animated the cool clouds in the background. If you have questions on setting up structure and styling please look at the source code in my repo (https://github.com/dpericich/Viking-Run).
Once the HTML and CSS is in place, we will need to use JavaScript to access our characters. Again, I am not going to walk through all the logic for starting and stopping the game or accessing our nodes. What I am concerned with is our ‘checkForCollisions()’ function. Assuming that we have pulled in our viking as ‘character’ and our penguin as ‘obstacle’ we can put together the following collision watcher:
Here we use the DOMRect object returned by calling getBoundingClientRect() on our saved HTML nodes to get information about the position of our element relative to the viewport. This DOMRect object is great for making calculated scroll animations based on object position, but also allows us to access many position attribute values.
In the function checkForCollisions(), we use setInterval to set our timer for checking for collisions. With a value of 10, this check will be run every 10ms which is sufficient for our game. Inside the interval, we will get the obstacle and character DOMRects and then store the boolean values of checking our 3 position conditions listed above. If all three conditions are true simultaneously, then we have correctly detected a collision and will end the game for the user.
I highly recommend trying to recreate this game, or at the very least cloning the repo locally to be able to mess with the code. (https://github.com/dpericich/Viking-Run)
Conclusion
We’ve talked about what a hit box is and how it’s used in video games. With this base knowledge we were able to create a simple 2D game based around the idea of game ending collisions, and fish. I hope that you enjoyed this article and better understand why your favorite games work the next time you boot up your console. Leave a comment down below of other game topics you’d like me to dive into, or even your favorite current game!
Notes
https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect