Author: Pix

  • Hit Detection and the Case against Boffers

    I’ll start with a disclaimer this time, because anybody who knows me knows that I will absolutely go on long, rage-filled rants about this subject if given even half a chance. I guess here’s my half a chance, but I’ll dial back the rage.

    What’s Hit Detection?

    Put simply, detecting whether a volume intersects another volume to produce a ‘hit’. I realise that’s very dry, so imagine you’re in a game, and you try to hit an enemy with a sword. Hit Detection is what’s running when the game is trying to calculate whether that sword swing should count as a hit or a miss (putting aside good old dice rolls, anyway). Should be simple, right? Riiiight? (You’ll have to imagine the knowing eyebrow wiggle, nobody needs to see that at any time).

    Boffers?

    Yes okay, I’m very much making stuff up when I use the word Boffer.

    Well, sort of.

    Boffer is a mostly American LARP term, meaning those big padded swords that you actually use to hit each other when you’re pretending to be knights and goblins or whatever. They can end up looking quite silly in their hugeness. I couldn’t find a good stock image, and I didn’t really want to make someone’s hard work sound laughable. So imagine those things in Gladiators that they use to hit each other. Something like this:

    It’s big, it’s foam, it bears a passing resemblance to a sword, you can hit people with it. The problem I have is not any of those, it’s when you try and transpose the same logic onto a video game.

    What do foam swords have to do with video games?

    Everything, and ideally, nothing. One of the most common things I see when I see people making sword-based combat games is admittedly, a complete no-brainer. It’s basically the first thing you’d think of. It mostly works, mostly.

    Just stick a capsule collision volume to the sword (to act as invisible foam to your boffer), and let the animation do the work. Brilliant, right?

    Et voila! A hitty thing! Just detect when the capsule touches something, there’s your hit. Why are you complaining so much Pix?

    Your hitty thing has a few minor drawbacks, and I mean minor drawbacks in a very British way. But it’s hard to explain without a little explanation about frames.

    Frames

    In theory, you should never have to worry about frame pacing, right? We live in a world where computers are (more or less) powerful enough to do whatever we want. In practice, <insert unhinged cackling here>.

    Think of a game as like an interactive flipbook animation. It ‘flicks’ through each page of the flipbook at a given rate (framerate), and that produces the motion on screen. Where games diverge from movies, flipbooks, zoetropes and the like is for each page (or frame), the game is making decisions about what should be shown next.

    In an ideal world, these frames are shown at regular intervals, but say, for example, your paper flipbook costs a lot of money to turn to the next page. If somebody doesn’t have that much money, they may skip a few pages to save a bit of cash. To keep everything nicely lined up, we can lengthen the time it takes to get from frame to frame to make sure the timings still work and we don’t just have the subject of our flipbook teleport around.

    So applying this to a video game:

    Your game has a framerate, it could be 30fps, it could be 60fps, it could be anywhere between 4fps and 120fps. You can’t even safely assume that the time between frames is the same every time, unless you’ve locked the framerate (which is its own can of worms, tl;dr players *really* don’t like it).

    Why does that matter though? Because it determines the locations your sword is physically in when it calculates the hit. You don’t (typically, I’ll get to it later) sweep the entire path of the sword, you just receive whatever locations the animation produces frame by frame.

    Imagine your character is swinging your sword, the framerate is normal (we’ll assume a solid 60fps to make it easier). Just assume every diagram I’ve lovingly MSPainted here is from a top down perspective. What you actually get back out of this is something like this:

    Pretend I drew the character’s arm in there if it makes it easier. Hopefully that shows you what the capsule on your sword *thinks* its location is at those given times. This is the approach that a certain very popular series/engine uses to solve the problem of hit detection. Note the gaps between the capsule positions.

    Now let’s place an enemy somewhere where you’d think you can hit them:

    Starting to see the problem? If that enemy just happens to be in between those frames when you swing the sword, you’ll miss, but it looked like you hit, bad feels are produced all around.

    I’m maybe overblowing this a little, if the sword’s path is sufficiently slow and the framerate sufficiently high, it’s rare (but not impossible) that you’ll hit this situation. So if your game is about zombies fighting each other underwater, in slow motion, you can probably disregard everything I’m about to say.

    As a side note, there’s something else about this that bothers me. If I place an enemy here:

    Then you’ve hit him, but the sword didn’t even touch him. Imagine you’ve got an attack that has a massive backswing. You’ve hit the guy behind you for no reason whatsoever, and now he’s standing there looking a bit bloody and confused. But so are you, you weren’t expecting to hit him.

    But Boffers aren’t completely unsalvageable, even though most of the attempted fixes just make the problem worse.

    You could attempt to plug the holes by making the capsule wider, then you have full coverage over your entire swing, nobody in front of you gets missed. But this just exaggerates the problem of hitting enemies you didn’t expect to hit.

    Not that then.

    Maybe you could try lerping (linear interpolation) the location to cover those inter-frame gaps. In effect, a lerp in this case means to slide the capsule from the location in the first frame to the location in the second frame. I’ll just call it a lerp from now on because it’s shorter. In my first set of diagrams, that would work okay. I’d go so far as to say that’s acceptable so long as you don’t push your luck. So let’s push our luck.

    Let’s discard our lovely stable framerate for a second, and say we’ve produced this:

    What happens if we lerp the capsule between these two locations? It doesn’t follow that arc, certainly, not without a certain degree of mangling. Given the shortest path in the rotation, at some point you’re going to end up with that capsule pointing out of the character’s butt. There’s a somewhat infamous example (although I don’t think it lerps) where a certain demon can hit you with his butt but only at a very specific point in his animation. His butt is certainly not where his weapon looks to be to the player, so it feels unfair (even if it is quite funny to those of us who have debug tools).

    So not lerping then, unless we can really be disciplined about what our animations are doing, and what our framerate is (ha).

    A more recent solution that I’ve run across is one I like a lot more, but it has one major downside. Instead of relying on what the game produces in any given situation, you sample where the capsule *should* be using the animation as authored. If you sample at a fixed framerate, you can ensure full coverage.

    In principle, I don’t mind this. In practice, trying to work out what that fixed interval to sample at should be is a bit of a nightmare, because it depends on target framerate, what the animation is doing and how large your collision capsule is.

    It has one major flaw though, in my mind. Consider the player who is playing on a PC with all the power of a potato. Given the gap between frame 1 and frame 2 will be larger, as the framerate will be lower, you end up doing *more* work per frame as you need to check more of your sampled locations to cover the gap. I’m not sure I like making the less powerful machines do more work, it seems counterintuitive at best.

    So what’s the answer?

    In short, there isn’t one. Yeah, I know. Sorry.

    In a bit more detail, there is one if you’re willing to make sacrifices. In essence you sacrifice a little bit of realism for the sake of gameplay feel and gameplay reliability.

    In other words, Fake It.

    Typically (but not always), there is no reason to go full realism on hit detection. You can get quite far by just making it up.

    This is a really stupid diagram but, by discarding what the animation is doing, and what the weapon is doing, you can just describe a simple 3D shape (volume) for a given number of frames to make up your overlap check:

    I tend to use these pizza slice things, but you could do spheres, cylinders, cubes (yes, even capsules, just don’t sweep them).

    The downside of this is that you don’t get the impression of sword movement, you can’t do a lovely side to side sweep and see each enemy get hit in turn. It’s good enough for most things, and doesn’t have the problem of gaps or wrong hits. But it is boring.

    So maybe we can inject just a little bit of the animation data to make this something a bit more special.

    Sample the location of the weapon every frame, it doesn’t need to be pre-sampled, and then build your volume out of that. This produces full coverage, even if it’s still technically faked, but you still get your sweeping hit:

    Here, the first enemy will receive a hit on frame 1 (he was hit at some point between 0 and 1), and the second enemy will receive a hit on frame 4 (he was hit at some point between frame 3 and frame 4).

    Sure there’s limitations to this too, but there’s probably a followup to this post where I address that and ways to improve it.

    But the takeaway is, just because a Boffer is the first idea, it doesn’t make it the only idea.

  • Resource Mangling (with a side order of Wibble Principle)

    Sometimes even the most basic seeming thing in a game isn’t so basic. It seems so obvious that you just make it without a second thought, and then it sits there being Just Good Enough™. While this is fine, if you’re anything like me Just Good Enough™ will fester in the back of your mind because it’s now Just Not Right™.

    This post is going to be a bit Unreal-centric, but hopefully the underlying message will make sense to other places (possibly not even games!).

    Let’s talk about something that I, until alarmingly recently, would just make without a second thought, and would be one of the very first things I would make when starting a game project.

    Let’s talk about good old HealthComponent.

    No I don’t know how to draw “health”, so he’s got eyes.

    HealthComponent is doing some pretty basic stuff. You’ve got a current health level that is almost certainly a number, out of a max health level that is almost certainly a larger or equal number. You subtract from the current health when you take damage, or add if you are healed. If you run out of health, then you’re (probably) dead (don’t get me started). You might also just want to do insta-death, so set the health to zero regardless.

    Honestly? This served me well for many years (hindsight note: actually no it didn’t), so I never bothered to change it. It only gets a bit weird when you have multiple numbery things to track.

    So let’s make it more complicated. Now you want to cast spells. Enter ManaComponent.

    Superficially, this guy looks like a different component entirely. In reality, all I’ve done is changed the words and given him a snazzy wizard hat.

    To really belabour the point: You’ve got a current mana level that is almost certainly a number, out of a max mana level that is almost certainly a larger or equal number. You subtract from the current mana when you cast a spell, or add if you drink an ether. If you run out of mana, then you (probably) want to inform the player. You might also just want to do insta-OOM, so set the mana to zero regardless.

    I could go away and put endless hats on HealthComponent and call it a new name. What about Poise or Stagger? What about Encumbrance? What about Experience Points (yes, I’m assuming that you can lose Experience Points, which is a thing we did in the dark ages)? What about Ammunition for your vast array of guns? What about Money?

    At which point, you’re probably thinking “Money? Pix has gone insane.” No really, if it’s a number, you can put a different hat on the same underlying structure, and have that structure represent something else.

    Wibble Principle

    I’m going to take a step back here to show a little creative thinking exercise that I tend to use when I’m trying to unpick something that feels wrong in code.

    Put simply, I take the description of the feature, and start replacing the proper nouns with a nonsense word. I like to use wibble, because it’s fun to say. Somebody a lot smarter than me called it a ‘metasyntactic variable’, I call it Wibble Principle.

    So let’s try and make a wibble component.

    So why wibble? What is wibble? The beauty of wibble is: we don’t care what wibble is. It doesn’t have to mean anything. I could have used plerbs, or blorts, or fliblets. The point being, that wibble can replace all of the more specific concepts like health, mana, poise etc. and the underlying structure still holds.

    We still modify wibble when something wibbly happens. We still notify the player when they are out of wibble (what does that even mean? Do we care? Nope!). I added notifying the player when they have a full complement of wibble, because it seems useful regardless (why not wibble harder when your wibble is maxed?). We also can force wibble to the minimum or maximum values, and the minimum doesn’t even need to be zero.

    I use this when I’m trying to extract the core structure from a rather fluffy gamey feature, and that generally results in a) a more flexible design but b) more confused people around you if you actually mention wibble. Don’t actually call it WibbleComponent.

    The Final Structure

    Knowing that we can extract a whole bunch of concepts (i.e. anything that could be represented as a bar, or a resource gauge, or a percentage, it’s just numbers) out into one generic structure means that in Unreal-land, we can just have one ActorComponent that houses more than one of these ‘resource’ structures. In effect, we have our HealthComponent with all its different hats in one place, under one component, and the hat becomes all that it should have been in the first place: A Decoration, or an Identifier.

    But why?

    I’ll be honest, if you really want to have multiple functionally similar (or even identical components) to manage different resources, then I won’t stop you. However, consider the following:

    • What if we want to redirect health damage to a depletable shield? How about if we only want to redirect a portion of the damage?
    • What if we want to lose money instead of health when we’re wearing specific armour?
    • What if we want casting spells to cost crystal shards (or whatever) instead of Mana, but only if you’re a Crystal Wizard?
    • What if you actually do have Wibble, and if you have wibble, it’s consumed instead of something else?

    Multiple components here becomes messy, you’d need to have something that comes in and marshals the function calls to the correct redirected component. It’s doable but it’s either a function on the character, or yet another component. If you have all this under one component structure, you can just modify whichever resource that the incoming damage is set to modify. You don’t have to care what the modification is or where it came from. Just that This Damage Changes This Resource.

    The flip side of this is that depleting a resource becomes a listener that is waiting for the deplete, and the actual “what happens” bit lives elsewhere. So e.g. Death does not live on the HealthComponent, just “I have died, please do something about this”. Given how complicated and messy Death can be (I swear I’m still talking about games), this is actually more of a blessing in my eyes.

    Further Wibbly Applications

    Once you’ve embraced wibble (or nonsense word of choice), it opens up other possibilities. You’ve boiled your system down to its base principles, and you’re free to think of all of this in a systematic and abstract way.

    Which means you can get creative with it. Or at least make yourself laugh.

    As a final takeaway, what this model of resource mangling can produce is: You could model a transaction in a shop the same way that you could punch someone in the face.

    • You: “Hello shopkeep. Just these please!”
    • Shopkeep: “That’ll be £5”. And then he punches you square in the face.
    • You don’t even flinch, it didn’t hurt. But you look in your wallet and £5 is gone.
    • Shopkeep: “Cheers mate, see you next time!”
  • The X/Y Problem (as illustrated by poorly drawn horses)

    I love the X/Y problem, but I don’t love when people fail to apply it, or even haven’t heard of it. It is, in my opinion, one of the fundamental things to learn in programming, and that goes double for games, especially when working in a team.

    So what is it?

    The very dry, very boring explanation is: when somebody asks for a feature, always ask “why”, rather than just accepting the “what”. More detail from a smarter human here: https://xyproblem.info/, I’m just going to focus on making this amusing.

    Fair warning: If you’re not good with the idea of using stupid metaphors in place of actual software features, you might want to run away now (screaming optional).

    On with the horses

    You’ve just received a very strange request for a feature: somebody has strolled up to you and said “I want a pony!”. They’re serious, a pony is what they want, and what they need, and they’re very clear about it being a capital P pony.

    So you set out to make the best damned pony you reasonably can. You’re probably thinking something like this:

    Original public domain image from The Rijksmuseum

    The experienced beings in the audience may have spotted your first mistake. You didn’t enquire what the pony was for, why do they want it, and why a pony.

    I get it, it’s Friday afternoon, it’s 4.30pm, the person who requested this has a history of requesting Weird Stuff, and you’re still halfway through their previous request of 500 teacups, one digestive biscuit, and curiously, no tea. So you just get on with it.

    You’re happily carrying on with building your pony, and you get to the point where it’s mostly able to do pony stuff. Problem is, it looks like this:

    This pony is clearly not ready for production, or even remotely finished.

    Feature Requester has been doing some thinking in the meantime, and points out to you that the pony should be able to carry a person. Fine, ponies are supposed to be able to do that, so you add a saddle.

    But Feature Requester comes back, and says “Oh, didn’t I mention it should be able to fly?”. So you backlog actually finishing the pony because if it starts to look even more like an actual pony it definitely won’t be able to fly. Time to add wings, I guess.

    You show this to Feature Requester, who sighs and shakes their head. “I wanted it to be able to go faster than this”, they say with a tone of voice that’s somewhere between disappointment and implying you have the brain of a particularly uncreative iguana.

    But that’s okay, you can augment the flight speed by having rocket boosters. Pony can fly, pony can fly real fast. It’s not particularly safe but at least the rocket exhaust isn’t pointing directly at the saddle. Nobody wants lightly toasted toes.

    Feature Requester is now happy with the flight speed of a traditionally non-flighted animal, and you’re starting to get a little annoyed at how much your Perfect Pony Programming is getting polluted with things a pony isn’t even really supposed to do.

    “Oh, yeah, the pony? It needs to be able to survive without water”.

    Your brain just flashes a massive warning sign that reads simply: “WHAT”. But you carry on, you’ve started now, no point in starting a new pony when you’ve been working on this little guy for weeks.

    In a flash of inspiration, and more than a little bit of idea theft, you give the pony a stillsuit. Now he can make his own water (from something you vaguely remember reading when you were 15).

    Pleased with this compromise, you sit down and start thinking about pulling “finish the pony” out of the backlog. But Feature Requester, bless ’em, is back again.

    “I think the pony should be able to survive without air, that would be easy right?”, they say in a message received at 6.45pm from their phone. You saw it because you’re now massively behind on the pony feature and wanted to do some sneaky catching up.

    You sigh, shake your head, and start designing some kind of pony helmet. It’s trickier than you’d think because a pony has a surprisingly long head and most helmets are designed for something more orb-like.

    Feeling a sense of victory, surely the pony is perfect this time and you can get back to making your perfect pony (with all this weird nonsense tacked on, but we just have to live with it now).

    “Hello!”, reads a message from Feature Requester. Just hello. You start to feel a bit like you’re sinking through the floor.

    You respond with a message that is trying desperately not to say “Please just get on with it, what do you want now?”.

    “I just realised that the pony also needs to be able to carry a week’s worth of cargo. Sorry I didn’t mention it earlier but I only just thought of it”.

    You’re starting to feel a bit sorry for this poor pony, it’s already looking a little bit like it’s begging for just a fresh carrot and a packet of polos. Maybe a nice field to run around in. But you persevere.

    This poor pony is running out of space where you can plausibly add more stuff to it. But with some clever trickery, you work out that you can dangle the cargo like a hot air balloon underneath the belly. It’s all starting to look a bit silly but it still technically fits the request.

    You send your flying, rocket powered, self sustaining, cargo carrying pony off to feature requester. It’s better to confirm that this is what they want now rather than finish the job and have them not want it.

    “Yeah this is perfect!” they exclaim with joy. “I’ll get on with using it right away”.

    You hedge your bets by pointing out that bits of this pony might be quite hard to use, like the wings keep hitting you in the face when you’re sitting in the saddle, or it’s really hard to see over the helmet so you might have to lean to the side.

    Feature Requester isn’t listening, they’re poking and prodding the pony who would really, really like to just go back to the stable and contemplate the meaning of its existence.

    You finally ask Feature Requester what they wanted the pony for, even though now, you’re not sure you want to know. But it’s your responsibility to know, because you have to maintain this pony.

    “Oh, I wanted to be able to go to the moon”.

    So after all of that, what they really wanted was…

    The moral of the story is: When asked for a pony, do make sure that what you actually should be building is in fact a pony, and not a rocketship.