informa
Featured Blog

Hitboxes and Hurtboxes in Unity

What are Hitboxes and Hurtboxes? aren't they the same? and how do we implement them in Unity?

Hitboxes and Hurtboxes in Unity


Explanation

So what are hitboxes and hurtboxes anyway? Aren’t they the same thing?

Well... The answer can differ depending on who you ask, but here we will abide to the notion that hitboxes and hurtboxes are two different things and have different uses, as any fighting game worth mentioning does.

Hitbox is an invisible box (or sphere) that determines where an attack hits.

Hurtbox on the other side is also an invisible box (or sphere), but it determines where a player or object can be hit by a Hitbox.

 

In this image from SFIV, the red box is the hitbox and the green one the hurtbox

 

It’s worth mentioning that the size and position of both hitboxes and hurtboxes change depending on the frame of animation that is playing:

 

Gif from Killer Instinct. Look how the hitboxes only appear on hitting frames and move with the sword

 

In the Killer Instinct example we can also see a third type of box, the Pushbox (the yellow one, the Hurtbox is the empty green box). A Pushbox is a box that represents the physical occupied space by a character, and keeps characters from overlapping.

In most fighting games or brawlers there’s two other types of boxes that for simplicity we will not cover:

The grab or throw box, that determines where a character can be grabbed or thrown, and the block box that determines where an attacked player that’s pressing the back button will start blocking a certain attack instead of walking backwards.

 

All these boxes are really important from a design point of view. Hitboxes and Hurtboxes of an attack determine not only how many frames an attack hits but also the blind spots of that attack and how vulnerable it leaves the player.

For a great explanation on this focused on Street Fighter check this video

 

So now that we have our terminology straight, let’s start working.


What we want

Let’s check every type of box we’ll cover and see what we want from them:

 

Pushbox: We need for two pushboxes to collide with each other and not overlap (that’s why it’s called a pushbox, it pushes the other character). Pushboxes should only interact with other pushboxes.

Hurtbox: It can register a hit, but it should not collide in the physical sense. Hurtboxes should only interact with Hitboxes.

Hitbox: It should be able to check if it’s overlapping a Hurtbox in arbitrary frames. It should only interact with Hurtboxes.


Using Unity default components

The first approach one could do is map every kind of box that we talked about to a Unity default component. The obvious choice is some type of Collider.

The Pushbox can be directly mapped to a Collider plus a Rigidbody. That behaves exactly as we want it, it collides with things and doesn’t overlap.

The only part we need to worry about (besides setting our Rigidbody as we want it) is the part about only colliding with other Pushboxes. If you’re familiar with Unity physics system, you already know that the solution is to use Layers and the Layer Collision Matrix. For clarity we can create a layer called Pushbox, assign it to our object and set the collision matrix so Pushbox only collides with Pushbox.

 

 

 

 

For Hurtboxes we can use Collider using isTrigger. This ensures that it won’t collide in a physical sense and will only register other colliders entering it’s area. For actually registering the hit we will need to add a script in the same object that implements OnTriggerEnter, probably check the tag of the incoming collider to check that the one that triggered the event is the one we want and then do whatever damage and health calculations our game needs. You are probably familiar with this approach.

We also need to create the layers Hurtbox and Hitbox, and use the Layer Collision Matrix again to make Hurtbox only collide with Hitbox and vice versa.

 

  • Note that we don’t need a Rigidbody, but only because I’m assuming that every trigger we will add is a child object of the Pushbox object that already has a Rigidbody. This is important because Colliders without a Rigidbody in itself or some of its parents will be set as Static by Unity and moving them will be really inefficient.

  • Also, we will probably need to distinguish between a Hitbox from the player or one from the enemies. The same goes for Hurtboxes. This way we can make the Hitbox from the player only hit the Hurtbox from the enemies, and the Hitboxes of the enemies only hit the Hurtboxes of the player. You don’t need this if you want to allow friendly fire, but you gotta be careful to avoid a player hitting its own Hurtbox.

 

Hitboxes are maybe the least clear in how we should implement them. What we can use is a Collider using isTrigger to avoid a physical collision, but there are really no colliders that “enter” a Hitbox. Actually is the other way around: A Hitbox “enters” (or checks if it’s overlapping) a Hurtbox. Nevertheless, we need a Collider or Unity will never call OnTriggerEnter in our Hurtbox.

For dealing damage to the Hurtbox we will need to add a script in the same object, so that our Hurtbox can use GetComponent<T> and get it to know how much damage needs to be dealt. You can also do it the other way around, OnTriggerEnter gets called for both Colliders. We also need a way to make our Hitbox active only when we want to and not in every frame or when the character is not attacking for example. For this we can’t just disable our script because as the documentation says: “Trigger events will be sent to disabled MonoBehaviours, to allow enabling Behaviours in response to collisions”.

What we can do is enable and disable the collider, or add a boolean property to our script that handles if it should hit or not.

 

Problems

  • Hierarchy: we need to have a script in every object with a Collider to be able to respond to OnTriggerEnter. If you like to have your scripts in the same place for organizational reasons, you will need to create a script just to delegate the call to your other object. 

  • Overhead: With this approach our Hitboxes have a lot of functionality we don’t need. 
  • Events: We rely on OnTriggerEnter for our functionality. Using Unity events may not be a problem, but there’s reasons to at least think if we should. You can also check this (in the section“Avoiding expensive calls to the Unity API”) to know more.
  • Visuals: If you want to use different Hitboxes for different attacks, not only will the aforementioned problems repeat a lot, but you will have a lot of visual clutter on your editor window.
  • Little flexibility: Using Colliders as Hitboxes means that if you want to change the shape of the collider, for example from a Box to a Sphere, you’ll have to remove the BoxCollider and add a SphereCollider manually (or implement an editor script to do this for you)

 


Rolling our own

As you probably realized while reading the previous section, Pushboxes and Hurtboxes map pretty much okay to Unity default components.

Hurtboxes still have the problems mentioned and we will solve some of them, but the entity that seems to need its own abstraction is the Hitbox.

If you’re making a combat heavy game with a lot of attacks and combos, you probably want to have all your attacks neatly organized in an object and use several combinations of Hitboxes for any of them.To do that you would need a script that strictly delegates OnTriggerEnter calls to your active attack or something along those lines.

You don’t want to create a specific Hitbox object for every attack, here we can reuse the same changing the size!


Hitboxes

Our new component will need to cover the following:

  1. Have Hitbox behaviour: It should be able to check if it’s overlapping a Hurtbox in arbitrary frames. It should only interact with Hurtboxes. 
  2. Have a visual representation in the Scene view. 
  3. Be customizable and flexible. 
  4. Ideally, not depend on Unity API events. 
  5. Be independent enough to allow an script in another object to use it. 
  6. Not be coupled to a specific attack, Hitboxes should be usable by several different attacks.

 

Behaviour

First off, how do we check if some area is overlapping a Collider? The answer is to use UnityEngine.Physics.

Physics has a lot of methods that can do what we want. We can specify the shape we want (Box, Sphere, Capsule) and also if we want to get the Colliders we hit (if any) as an array or pass an array to be filled with them. You shouldn’t worry right now about this, but the first one is allocating a new array, the other just fills the one you already had.

 

Let’s start by checking a rectangular area and see if we hit something. For this we can use OverlapBox.

 

We need to define the dimensions of the box we want to check. For this we need the center of the box, its half-extents, its rotation and the layers that it should hit. Half-extents are the half of the size in every direction, for example if you have a box with size (2, 6, 8) its half extents would be (1, 3, 4).

For the center you can use the GameObject transform position and for the rotation the GameObject’s transform rotation or you can add public variables to set them specifically.

Half-extents is simply a Vector3 so expose it and use it.

For the layers to hit you can expose a public property of type LayerMask. That will let you select layers via the inspector.

 

Collider[] colliders = Physics.OverlapBox(position, boxSize, rotation, mask);

if (colliders.Length > 0) {
    Debug.Log("We hit something");
}

 

If you set that correctly and the box we are projecting is overlapping a Collider in the correct mask when you call this, you should see the message on the console.


Visual Representation

That’s cool but... It’s not very functional. Right now we can’t see the box we are defining anywhere, it would be really difficult to set correct sizes and positions of Hitboxes this way.

So how do we draw our box on the Scene view but not in-game? Using OnDrawGizmos.

As its documentation says: “Gizmos are used to give visual debugging or setup aids in the scene view.”, just what we were looking for!

We need to give our Gizmo a color and a transformation matrix. Don’t worry about it, we’ll just create a matrix with the position, rotation and scale of our transform.

 

private void OnDrawGizmos() {
    Gizmos.color = Color.red;

    Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, transform.localScale);

    Gizmos.DrawCube(Vector3.zero, new Vector3(boxSize.x * 2, boxSize.y * 2, boxSize.z * 2)); // Because size is halfExtents

}

 

If you want, you can use OnDrawGizmosSelected instead to only draw the box when you select the object.


Customization and flexibility

Customization is a broad subject and it’ll depend a lot on what kind of game you’re making and what functionality you are looking for.

In this case we will allow for a quick change in hitbox shape and color. If you are using Anima2D or some kind of bone oriented animation, you’ll probably want to also allow for the Hitbox to scale following a bone scale.

Changing the shape it’s as easy as adding a boolean and changing OverlapBox to some other shape, for example OverlapSphere. You need to add a public radius property to configure the sphere. Remember that you’ll also need to change what’s inside OnDrawGizmos to actually draw the new shape (in our example, DrawSphere).

Note that we are not adding a new component or removing anything, it’s just a boolean that will select the shape to overlap when it checks collision. That allows us to change the shape of our hitbox basically for free depending on the attack (or even in the same attack if we want to).

For color, I would like for a Hitbox to change color depending on if it’s inactive, if it’s checking for collisions or if it’s actually colliding with something. We will also need those states for our logic later so let’s add them.

 

We’ll create an enum for the state and 3 colors and add them as properties of our Hitbox.


public enum ColliderState {

    Closed,

    Open,

    Colliding

}

 

Your class probably looks something like this:

 

public class Hitbox: MonoBehaviour {
    public LayerMask mask;

    public bool useSphere = false;

    public Vector3 hitboxSize = Vector3.one;

    public float radius = 0.5f;

    public Color inactiveColor;

    public Color collisionOpenColor;

    public Color collidingColor;


    private ColliderState _state;


    /*

        and your methods

    */

}

 

And now you can update your gizmos replacing the line Gizmos.color = Color.red; with a call to a new method:

 

private void checkGizmoColor() {
    switch(_state) {

    case ColliderState.Closed:

        Gizmos.color = inactiveColor;

        break;

    case ColliderState.Open:

        Gizmos.color = collisionOpenColor;

        break;

    case ColliderState.Colliding:

       

Latest Jobs

Infinity Ward

Woodland Hills, California
11.3.21
Sr. Multiplayer Design Scripter/Programmer

Disbelief

Cambridge, Massachusetts
11.3.21
Jr. Programmer

XSEED

Torrance, California
11.3.21
Head of Marketing
More Jobs   

CONNECT WITH US

Register for a
Subscribe to
Follow us

Game Developer Account

Game Developer Newsletter

@gamedevdotcom

Register for a

Game Developer Account

Gain full access to resources (events, white paper, webinars, reports, etc)
Single sign-on to all Informa products

Register
Subscribe to

Game Developer Newsletter

Get daily Game Developer top stories every morning straight into your inbox

Subscribe
Follow us

@gamedevdotcom

Follow us @gamedevdotcom to stay up-to-date with the latest news & insider information about events & more