Impact - Stabbing, Fx + ShakingGetting combat to feel great is hard, here's where I'm at and what I've found to work well.
Please borrow and steal as you see best.
Full disclosure, I learned a bunch from a great
Gamasutra post. As far as I know, path-based wounds, stabbing and blood pooling are new techniques.
Camera ShakeMost of the best fighting games use some form to this, basically, it makes any hit feel more impactful.
BeforeAfterCamera ShiftGames like Soul Calibur made liberal use of this, I suppose shifting the camera in any direction could be made to work, but I found shifting it vertically was the only thing that didn't make people nauseous. In fact, using just vertical shift, without shake can cause a bit of nausea.
BeforeAfterHit ReactionsYou can go nuts with these. Punch-Out!! Wii is the gold standard here IMO. For an early prototype of North, I tried to replicate the Punch-Out!! model, but it made everything feel too cartoony.
Alternatively, Dark Souls uses the same hit reaction for all normal hits, and then special reactions for things like Backstab or Knockdowns.
I've decided to go with the Dark Souls model for now, and will continue to evaluate as I get further down the road.
BeforeAfterSpecial FxMark of the Ninja always comes to mind when I think about great 2D special fx. Their classic sprite sheet, rather than particle effect approach is ultimately where I want to take things. Trying to fit it into North's art style has proven tricky -- we're more angular while Mark is more classic Disney.
For now, I'm using rather basic special effects. I'll dive into these in more detail with a separate entry.
One thing I'm happy with is the decision to add miss fx which are different from hit fx. Below when the enemy misses, his mace smashes the ground, when he hits, blood bursts from the hero.
BeforeAfterLastly, I'll call out my most subtle effect, blood droplet "pooling". I spent way too much time on technical approaches (including implementing a physics based blood pool flowing system) to get this to look decent. In the end using a simple particle system, with a collision event on Ground impact was the best.
Slow MotionStreet Fighter is famous for using freeze-frame to increase the impact of a hit as well as allowing an attack to read better. God of War on the other hand, makes liberal use of slow mo. I felt the God of War model better suited my goals. The freeze-frame approach either felt too sticky or too arcadey.
BeforeAfterMaskingThis is something that I was always terrified of tackling. Essentially what I'm after is allowing stabbing weapons to pierce a target's body, rather than simply pass in front or behind.
I thot of two ways to approach this. I took the hard way.
One way, the easier way, is to divide a character into two halves, a set of front and back ordered sprites...
... then I could have all weapons on a middle plane and all attacks would look like they pierced their target.
Option two aka the hard way, is to use masking, specifically the Unity plugin
Sprite Mask.
In retrospect, building everything in two half feels like the "right" way, but by the time I thot about weapons stabbing targets, I had already built and animated my hero as well as several enemies. Hopefully the masking approach pays unforseen dividends down the road
.
The goal was to create a composite mask for any target, and then allow any weapon to be dynamically masked by that target when the two overlap (as they do on a hit).
Getting all this to work was a bit of a journey, he's how I approached things.
Note, the below assumes you're using 2D rigs for your hero and enemies. (Things are a lot easier to mask if you're using traditional sprite sheets.) For reference, I use a rig similar to the one detailed by zombiegorilla in this
Unity post.
0, I built special masked sprites for the chest and skirt, all other masks (arms, legs, etc) can just use their normal sprite. The pink is just there to show the original sprite size, in-game its transparent.
1, Take your top level sprite or container, for me its Pelvis, and add a Sprite Mask component to it. Set 'Type' = None, 'Inverted' = True, 'Show Mask Graphics' = False. (Only if you are using lit sprites) Under Advanced, 'Force default material on' = True, 'Sprites Shader' = Diffuse.
2, Go through all of your standard sprites (these will not be part of the mask) and add the 'Skip Masking' component. This tells the Sprite Mask at the top of your hierarchy to not include these sprites.
3, Go through your hierarchy and add masks sprites "beside" all of your non-masking sprites. I did this by duplicating the non-masking sprite, then adding the 'Sprite Masking Part' component, and then removing the 'Skip Masking' component.
That should be it for setting up the masks, your actor should look exactly the same as before you added any masking. Now we write a bit of code to dynamically add the attacking weapon to the Sprite Mask and have it masked out.
A bit of context first. I handle collision of weapon to target using box colliders set as triggers (I don't let Unity physics handle any collisions, anywhere, it's too unpredictable when overlaps occur). I manipulate the weapon collider independently of the actual weapon within each attack animation. I do this because Unity makes a mess of colliders when the object is being transformed inside an animation. Basically, the collider gets out of sync with the weapon and gives you very undesirable results.
In addition, I use an another separate object to handle "point of contact", noted as 'Damager' in the image below. This is also manipulated independently in each attack animation and allows me to get the "sweet spot" of an attack when a hit occurs without any math. (Which terrifies me BTW, I used to have a reoccurring nightmare about having to go back to University to retake my math courses :|.)
The code for the dynamic assignment of the attacking weapon is straight forward.
void OnTriggerEnter(Collider other)
{
if (other.CompareTag ("Weapon"))
{
IDamager hitter = (IDamager)other.GetComponentInParent(typeof(IDamager));
if (hitter != null)
{
_spriteMask.maskedObjects.Add(hitter.GetActiveWeaponSprite().transform);
_spriteMask.updateMaskingComponents();
}
}
}
The collider for weapon hits is tagged as "Weapon".
IDamager is a simple interface that is implemented by the Damager_MeleeWeapon and added to the gameobject that owns the collider for weapon hits.
GetActiveWeaponSprite(), simply returns the active weapon used by the attacker. I set the active weapon in each attack animation through the inspector within each attack animation.
The documentation for Spite Mask informs you to call updateMaskingComponents() after adding sprites.
All of this is adding the active weapon to the list of 'Masked Objects (outside this mask hierarchy)'. At runtime when there is a collision the attacking weapon, tagged with "Weapon", is added to this list and masked by the target.
WoundsWhen a weapon hits a target, I don't just want it to feel like it went through the target, but after the hit is done, I want the wound to be visible. I've found this very difficult to get exactly right in 2D, as I can't yet figure out how to exactly detect the intersection point of the weapon relative to the flat 2D sprite. So for now, I cheat.
(I'm not fairly happy with the results here, but they're not perfect. The wounds don't go exactly where the weapon pierces the body. And although it really bums me out, I think I can live with it.)
I use the impact point of an attack, based on that 'Damager' object that I mentioned before and then given the "Path" of the attack (stab, Vertical or Horizontal) I present the appropriate visual at randomly generated location +/- a small amount from the impact point.
Here is GIF of wounds as well as masking, the Bugbear gets a bit of revenge so I can show you the wounds on its back and side
.
Okay, that's it. I have a bunch of things I want to add and refine, but I'm happy with this start. If you have any questions or comments, fire away. Cheers!