Reworking A 5 Year Old Mess
As mentioned in the first blogpost, Ronn For Your Life is a project I started back in 2011, worked on it for a few months, and then put it on hold for personal reasons. So when I picked it back up a few months ago, the codebase was more than 5 years old. I've developed a lot as a coder and a game designer since then; things I didn't even think about back then are now glaringly obvious to me, and needed a complete rework. Back then I just coded away until stuff worked, then proceeded to the next task.
Today I do things different; make the simplest things you will be doing 90% of the time feel good on their own before doing anything else. Just moving around should feel good even with nothing else in the game; if such a fundamental part of it doesn't feel good, the very foundation is built to fail.
Just moving around did not feel good. In fact it felt horrible. I had slapped together some very basic movement and collision that was sketchy at best, and buggy at worst. Basic platforming was too hard and frustrating, because the collision system required jumps to be pixel perfect to register; and bumping the game up from 30 FPS (my standard back then) to 60 FPS just made the collisions run twice as fast, demanding even more machine-like precision from the player.
You might be one or two pixels from the ground (thereby technically not allowed to jump), but at 60 FPS you won't register the difference, making it look and feel like the game's fault. I analysed the problem and concluded that I needed to introduce 3 steps, which I will discuss below.
Step 1: Input Buffer
Usually in a game, you press a key (such as jump) and the game checks if you're allowed to jump or not (typically by checking if you're on the ground or not). If you are, you jump; if not, the input request is ignored and that's it. With an input buffer, the game remembers your "jump" request for a short while - maybe just half a second or so, and then the game will keep checking as long as the input request is in memory instead of just once.
So, imagine that you're landing from a jump, and to your eyes it looks like you're on the ground, so you press JUMP - however, the collision system actually still has you a couple of pixels above the ground. But being only 10 miliseconds since you pressed the jump key, the input buffer still remembers; and during that time, your character has actually landed on the ground (so the collision system finally agrees with your human perception), and your jump is triggered. Everyone is happy! This all happens so fast that there's no perceivable difference to the player, but it makes a world of difference in terms of feelings of frustration and responsiveness.
It's obviously important to get the window of time just right; too little a buffer won't make much of a difference, while too big a buffer will just give weird and unexpected results. The setting depends highly on the type, speed, feeling and difficulty the game aims for.
Step 2: Ledge Grace
So we've already seen the effect of a pixel-perfect collision system combined with high-speed refresh rates; not a good cocktail. The "step 2" problem is actually caused by the same effect as we solved with input buffers, but the solution isn't exactly the same. You've tried it before; you're running along at a good pace in a platformer, approaching a ledge and you're about to attempt a difficult wide jump. Obviously you wait to jump at the very last second to get the furthest jump distance - but alas, the stupid game didn't register your jump, and your character just runs off the ledge and descents into certain death. Once again, it feels like the game's fault, but technically, you were just one or two pixels off the ground, and therefore not allowed to jump. Input buffers won't save you here, because you pressed too late and not too early.
What we need here is ledge grace.
It works kind of like input buffers, but the other way around; whenever the player is on ground and allowed to do on-ground stuff (such as jumping), our ledge grace is a little value that is constantly reset. As soon as the player is no longer on the ground, our ledge grace value starts to increase until we're back on the ground (where it's once again reset), so that while not on ground, the value will keep growing. With this value we can check how long it's been since the player walked off the ledge; and if it was below a certain threshold (again, typically much less than a whole second), we will allow jumping. So if even if the player is an inconceivable tiny amount of pixels off the ground, not really visually significant or even perceivable, the game will still treat it as if the player was still on the ground, leading to a much more responsive and fair feeling.
As you can see, what input buffers and ledge grace does is really the same thing; it treats the game fair to how the human eye perceives and experiences the game, instead of how the pixel-perfect computerized system sees it.
Example of ledge grace at work (greatly exaggerated for demonstration purposes)
Here you can clearly see that the player waits a significant amount of time after walking off the ledge before jumping, but the jump still registers.
Step 3: Better Collisions
The final step is fairly simple and mundane, but still vital to the overall feel; before, something as simple as bumping into a box or a wall felt jaggy, and sometimes would just throw the player back a few pixels. I reworked the entire collision system; this step doesn't really include any smart design theories or techniques - I had just coded a shitty collision system, a system which only merit was being so pixel-perfect in exactly all the wrong places that it actually spawned my implementation of the two first steps.
However, while doing something as boring as making it more reliable, I also did do something a bit more interesting; I introduced a system to check for diagonal resistance, so that I can control the amount of decrease or increase in movement speed when the player is moving up or down a slope. Before, the player just moved at the default movement speed when walking up a steep slope, which felt weird; and walking down a slope was even worse, as the player was actually just "falling" a few pixels every time he walked off another pixel of the slope, rather than actually climbing down the slope, properly grounded to it. I changed this too, so now the player stays on the slope as he's moving down, with an increase in movement speed - if I desire.
Just take a quick look and see which one you like better.
Old slope collisions
New slope collisions
All of this actually wasn't that bad - I reckon it took about one or two day's worth of work, with research, implementation, testing and balancing all accounted for. And it was more than worth it! Even with my new and more design oriented perspective, I can move forward with the rest of the game with good conscience; something as simple as moving through that little obstacle course of boxes in and of itself now feels good and is fairly fun - compared to before, where it actually felt like a chore.
If there is a lesson to be learned here, at least for me, it's to be very mindful of especially the smallest but most basic parts of your game; something as simple as moving around is something I think is easily overlooked by many new game designers, because they're so excited about making the rest of that cool game they have in their mind, that they just want to get the basics out of the way and quickly move on. But restrain yourself; make a basic little movement ground with literally nothing else to do but move, jump, crouch and whatever else basic stuff you can do in terms of navigating the game world; does it feel good? Is it responsive? Is it remotely fun? If it isn't, then analyze why that is, and what you can do to change that. In many types of games, moving around is what the player is going to be doing 90% of the time - if something as basic and simple as that doesn't feel good on it's own, then it's like building a big nice house on a foundation of pastries. The house may be big and nice, but it's going to crash and fall.
Also, sometimes it's worth to think about what's more important for the game feeling you want to convey; adhering to strict computerized "correctness" - or implementing some conscious leniency for the sake of making the game feel more fair to the human perception. Because at the end of the day, your game is going to be played by humans, not computers. Unless you're doing something incredibly avant-garde, in which case, I... I can't help you.