Fun with Frames.TLDR--How I implemented a per-frame hitbox system. Also, playable demo with debug draw. See end for demo instructions.Like the other games in the series,
Thief of Vallas: Working Title is an action RPG. With larger sprites and more complex moves on both the player character's and NPC's parts, I've started borrowing from the beat-em-up genre. My experience with it is somewhat limited, so I've had to do a crash course in conventions like frame data, frame advantage, etc. Luyren shared this video with me a few days ago:
(link!) and I also found another
quick guide (link!).
The most significant technical challenge has been finding a way to define hitboxes that update every frame. I'm separating
hitboxes from
collision boxes here, since I want each to serve a different purpose. Collision boxes function as they always have on the physics side. A "hitbox", on the other hand, is only used to determine if and where one actor's attack hits another actor.
I'll use the player character, Marika, as my example. My next big hurdle will be drawing the images for the first few enemies. They've been set as placeholder images for a while, but I need the proper ones so I can configure their hitboxes.
Drawing Hitboxes. Since Stencyl doesn't provide a hitbox editor for my purposes, I had to make my own. I thought about making a full-featured editor, like I did for so many of these kinds of problems with my isometric games. In fact, I will probably do that at some point. When I'm done with this game, I'll be porting a lot of these features over to my isometric framework. For now, GIMP was an easy solution. I took my sprite sheet and drew the hitboxes over it.
Here is the running animation. I don't want to get too elaborate matching hitboxes to body parts--nor do I think I need to with all of my sprites roughly 20x48px. The three basic areas are: Head, Torso (referred to as Body in-engine), Legs. The intensity of red (or green for attack hitboxes) defines a damage factor. Brighter red is most vulnerable; brighter green is most powerful. I probably won't end up using the power factor for attacks, since each attack already has its own damage roll--but it wouldn't hurt to have the data there if I find a use for it later.
I didn't have this system in mind when I drew these sprites, so I got a bit lucky with how some of them worked out. Here is the dodge backward animation, which best protects Marika's head and least protects her feet.
When I'm done, I export the hitbox images without the sprite background. I drew the head, body, leg, and attack boxes on separate layers. I can have as many boxes and layers as I want, but two boxes cannot overlap on the same image. Overlapping boxes have to be placed on separate images.
Managing the Data. Loading the data into the game was fairly simple, but I needed somewhere for that data to go. I made a new HitBox class. I'll link the two .hx files below, which you're welcome to use if you want. They would go in as "Freeform Mode" behaviors.
HitBoxVUtility (supporting macros)One of the nice features of HitBox is that it automatically adjusts for facing left or right. All of the coordinates are defined with the character facing right.
The database is the clunkiest part of the whole operation. The Hitboxes are stored in this beast:
public static var mapDB:Map<String,Map<String,Map<Int,Array<HitBox>>>>;
which represents the nested data: actor type name -> animation name -> frame -> list of hitboxes for that frame
A better solution would be to attach the hitbox data to the Actor class, or even Animation class. I still might do that in the future. For the moment, though, I want to avoid modifying the Stencyl engine. I'm early enough in development that I can see wanting to update my version of Stencyl before I'm done. This works for now, and I structured my behaviors so that upgrading would be easy if I decide to do that later.
Keeping the hitbox data up-to-date requires a simple retrieval from the database. All of my animated characters have an "Animations Handler" behavior. 'FrameUpdate', below, will update the hitbox data. Updating an animation sets 'Last Frame' to -1, which forces an update whenever the animation itself changes.
FrameUpdate calls HitboxUpdate that does the actual retrieval. The 'set hitboxes to' is the only line I would have to update if I update my clunky database to a more elegant solution. This is inefficient, but I don't think I'll be doing enough database look-ups for that to matter.
The final routine lives inside each of the attack behaviors. The custom block checks if any of the player's attack hitboxes overlaps with any of the enemy's damage hitboxes. This check occurs once per frame. If an attack animation has multiple frames with hitboxes, then multiple hits are possible.
So far, the only attack to take advantage of this feature is the relatively slow standing-kick, which makes it one of the more powerful attacks in the player's early arsenal.
Another sneaky attack is in the axe throw animation. When Marika reaches her arm back to throw an axe, if an enemy is standing behind her then they'll get clonked on the head.
Loading the Images. I use the Image API to load the aforementioned hitbox images. Each image is a table, with one animation per row and one frame per column. For each 64x64px frame, I scan for an upper left corner of a box. When an upper left corner is found, I trace the top and right edges to find the lower right corner. I don't really need to draw the whole box, but there's no reason not to.
Demo!Here is a playable demo of what I have so far. My target platform isn't Flash, but it seems to work okay. I haven't balanced the attacks yet. (Largely, because I haven't updated the enemy sprites either) The instructions have not been updated since I changed a few other things around. To hang off of a ledge and climb, you no longer need to press 'up'. If you're facing right, hold down the right key; likewise for left.
The move point system is the same from
Towers of Vallas. If Marika runs out of move points, her attacks become weaker. Climbing drains energy the most, but she won't fall off a ledge if energy hits 0. Getting a 'Cool Bonus' restores energy to full. You can get a 'Cool Bonus' by quickly collecting all of the like-colored gems in a group.
Press '1' to toggle the hitbox debug drawing.
Game!
http://www.stencyl.com/game/play/42250