Featured Post

Introduction

    Hello and welcome to the Spike Development Diary. Spike, as it is currently called, is a platform fighting game I (Chris Fabiano) am dev...

Friday, May 15, 2020

Entry #1: Hitboxes

    For this blog's first entry we are going to talk about one of Spike's most complex mechanics, hitboxes. A hitbox is simply a collision trigger that is generated in order to represent an attack. The collider covers the area that is the attack's effective range and, upon overlapping with another player's collider, registers a hit. Another worthwhile term to know is "hurtbox" which is simply the collider that each player has on them that represents their body and causes them to be hurt by attacks that successfully overlap with it.


    As you can see in this image, there are two green outlines on the character's legs and one on her body. The two on her leg represent the effective range of the kick she is performing and the pill shape on her body is her hurtbox. If the hitboxes on her leg collide with the hurtbox of another player, the game will register a hit and hurt the victim based on the hitbox's info.
    The basic idea of a hitbox is very simple and easy to create, but what happens when you want a move more complex than just one blob of harm? What happens when you want multiple hitboxes over time like the two shown in the image above? This is where the process becomes much more complex. In this post I will take you through a list of problems I occurred as I developed this aspect of the game and how I solved them.

Problem 1: Multiple hitboxes at one time.
    There are two reasons for why a fighting game developer may want two hitboxes to exist at the same time. Those reasons would be covering complex areas and wanting a move that changes it's effect based on where the opponent contacts it. If you had a complex pattern of colliders with all the same data, you could simply make the players only capable of receiving one hit from each attack. This process is it's own beast and will be gone over in the other section. The other reason for having multiple colliders requires an extra step, however.
    Going back to the picture demonstrating the boxes, the circle on the end of her leg represents a stronger and more damaging hit that the longer, pill shaped collider covering the rest of her leg. The second hitbox exists so that if the player uses the move very close to the opponent, they will still be rewarded with a small hit that keeps the opponent away but offers no real advantage. This rewards the player for using the move at a safe distance and adds extra thought to how and when certain moves should be used. Now, what happens when the opponent overlaps with both of those colliders on the same frame? Which collider's info will they use to receive the attack?
    For this, we will need a priority system.

    This is a simple piece of code for checking every hitbox collided with this frame and filtering them by priority. Every time a hitbox collides with an opponent, instead of hitting them, it actually just adds itself to their personal queue of hits. Every frame, each player will check to see if their queue has any hits in it, and if it does, it will begin to check their priority. As it goes through the whole list, it keeps updating a variable called "hit" to the information of the hitbox with the highest priority. After this code, the player will act according to being hit (will be covered in a later post!) using the information it saved in the "hit" variable and then clear the queue for next frame.
    With this system in place, players are guaranteed to only register one hit per frame and as long as every part of a move is saved with a different priority, they will always be hit by the most important part. This creates a small design dilemma as the designer must decide which part of the attack should be collided with in the event of a simultaneous collision, however, clever implementation can be used to create attacks with complex and mechanically expressive behavior.

Problem 2: Hitboxes that last more than one frame
    If a player had to overlap their attack with an opponent on just the right frame (1/60th of a second in this case) it would make hitting the opponent very difficult. For this reason, most hitboxes will persist for a few frames before disappearing. Some will move along with the move if the part they are attached to does the same and some will stay still. This quickly runs into a problem. What happens when the opponent is moved by a hit, then the hitbox also moves the next frame, causing a second collision with the same boxes?

Here the hitbox of this sword attack successfully overlaps with the opponent.

2 frames later, the opponent has started to move away, and the sword swipe has moved down, bringing the hitbox along with it. Unity will consider this a new collision.

    What this means is we must keep track of hitboxes that have already collided with each player. We also can't just record the collided hitbox but also the other hitboxes involved with the move. We wouldn't want the strong part of a move colliding on one frame, and then the weak part on the next, registering a new hit.

    Immediately after the priority has been considered, this code checks to make sure that the hit has a proper owner. This is due to deadly stage elements that don't have a player owner and thus work a little differently. If the owner is not null, it calls the owner's UpdateAllHitlogs() function and passes it both itself and it's shield, which has a secondary hitbox and must also be logged.

    The function does two things. It adds the passed player to it's own log as well as the logs of every active hitbox it has out at the time. The hitboxes must have their logs updated so that if they collide with a player and they have the same id as one in their logs, they won't act off of the collision. The player must keep track of each other player they have hit in the case that they make a new hitbox part way through the attack. If halfway through an attack a new hitbox is created but is not a distinct hit from the ones that already exist, it must be created with the same logs that those existing ones have built up in their lifetime, so it is given a copy of the player-kept log. This player-kept log is cleared at the end of each attack so it is blank when the next one starts.
    That wraps up the steps in place to make sure that players are being hit by the right colliders at the right times. Each of these steps were added in different iterations as I kept realizing new flaws or inconsistencies in the design. A lot of different ideas with holding the logs in different places and trying to update them as easily as possible to keep the code efficient were implemented and this is the one that stays simple while still keeping track of everything it needs to to allow me to create interesting attacks with interesting collision.
    In the next post I will discuss what happens when a player gets hit and where the hit's data is stored which will introduce mechanics such as knockback, hitstun, and damage.

No comments:

Post a Comment