Hello Patrons,
It has been a long week! Spaceboy Games just launched its first game on Steam as an acting publisher. We worked together with Mike Studios (Philippines) to bring High Noon Revolver to PC, Mac, and Linux! Alongside that, I have been hard at work on more #Fara development (overworld traversal code, mainly). With that being said, I thought that I would take a break from all of my top-down design and visual effects to talk to all of you about developing a high-quality platformer. This entry will be theory-based (i.e. I won’t be supplying you with any sample code), however, I feel that if studied attentively, this guide will help take your 2D game(s) of the platformer genre to the next level.
A Not-so-broad Look at the Platformer Genre
Alright, so for those of you who have been following me for awhile, you probably get a mental image of the kind of game that I am talking about when I talk about platformers. The term ‘platformer’ is an incredibly-inclusive umbrella that covers everything from level-based, side-scrolling games like Super Mario World, to open-world, exploration titles like Castlevania. I happen to enjoy a sub-genre of platformer that one could describe as fast-paced, with micro-levels, and a focus on simple, but deceptively deep mechanics. This would include games like Super Meat Boy (Team Meat), INK (ZackBellGames), and HackyZack (Spaceboy Games). Because I self-proclaim that I specialize in this area of the genre, I will be focusing on features that apply to these types of games today.
1) A hardcore platformer is no place for realistic controls. The best platformers have what I call duct-tape physics. Duct-taped together because the physics could not exist in any system. They are often inconsistent. Not inconsistent in the sense that they are unpredictable, but inconsistent meaning that values may change given different scenarios (acceleration, friction, and speed(s) may change depending on whether you are on the ground or in the air, for example).
2) The pacing of these games is generally fast. Fast is relative and shouldn’t be hard on the player’s eyes. As you increase the game’s overall speed, consider zooming the camera out to adjust how much screen distance is covered over time.
3) Again, speed is relative. Consider whether or not your game would improve from also having the option to walk rather than run.
4) Allow for practically instantaneous acceleration, as well as friction (be able to stop on a dime) when the player is grounded. Slippery controls are a no-go at high speeds.
5) Include the ability to jump variable heights! If the player is still moving upward while the jump key is released, cut the player’s vertical speed by a fraction. This allows the player to have a higher jump arc when holding the jump key for longer periods of time.
6) To further the player’s ability to nail more a precise jump height (particularly in situations where the player must squeeze into a tight gap the size of a single tile), some developers choose to add an additional frame or two to the player’s hang-time when their vertical velocity is zero. Remember to check that the jump button is still being held down at the very peak of the jump (when the player’s vertical velocity is zero or has already passed zero back into positive values), lock vertical velocity to zero, and don’t allow the player to fall any further for another frame or two. That added hang-time does wonders for making the highest of jumps more feasible!
7) Typically, to determine whether or not the player can jump, you check two things: if the jump key was pressed, and if the player is grounded. One way to make your controls feel more responsive here is to add an input buffer for one or both of these requirements. For example, check if the player is on the ground during this frame *or* if the player was on the ground during the last frame. This case is very common when a player is trying to clear an abnormally wide horizontal gap and has just barely ran off of the end of a platform prior to tapping the jump key. This can be taken even further by also checking if the player will have made contact with the ground during the next frame, which can be determined by evaluating the player’s velocity (you can choose to only use this case when the player is a few pixels or less from the ground to better conceal the buffer. The illusion of responsive controls fails when the hand-holding becomes visible to the player).
8) The same input buffer can be applied to games with a double-jump. Often, a frame or two before the player makes contact with the ground, the player will try to jump. In the case of games with a double-jump, the second jump (if not already used) would be triggered. Otherwise, no jump at all would be performed without an input buffer. My recommendation would be that if you have a double-jump, go back to the previous point in this article and apply both the previous and next frame buffer. If the player is going to reset their jump and double-jump within the next frame (will be grounded during the next frame), reset their double-jump now and execute their first jump instead.
9) Take the time to implement the proper handling of overlapping inputs! By this I mean the case where the player is pressing both left and right, and/or is pressing both up and down. This issue exists in the first place due of common, *cutesy* little programming/math tricks where you can subtract one input from the other to cancel each other out (but that causes the player to stop moving briefly when you shift from moving up to moving down and have a frame where both keys are held). We can accomplish a fix in several ways, I am sure, but what you are essentially trying to do is change how you determine which keys are being pressed. Personally, I do this by taking time into account. If both left and right are simultaneously held; ask which one was pressed more recently. The more recent input action should take over. This can be tricky, so I will outline a simple example below:
// Initialize
_kLeft = 0;
_kRight = 0;
// Input polling
if (keyboard_check(vk_left)) _kLeft++; else _kLeft = 0;
if (keyboard_check(vk_right)) _kRight++; else _kRight = 0;
// Sample movement trigger(s)
if (_kLeft && (!_kRight || (_kLeft < _kRight))) {
// MOVE LEFT
} else if (_kRight && (!_kLeft || (_kRight < _kLeft))) {
// MOVE RIGHT
}
In words, to move in one direction we check if that direction is pressed, as well as if the opposite direction is *not* pressed or the desired direction was pressed more recently than the opposite direction.
10) Alongside jumping, hardcore platformers also focus on tight wall-jumping. Programmers and designers alike often have a hard time find numbers that allow wall-jumps to look fluid across multiple scenarios.When it comes to wall-jumping, there are really two cases: the player could be climbing a single wall; repeatedly jumping away from the wall while continuously pushing back towards the wall (trying to scale the wall, vertically). The player could also be jumping away from the wall, horizontally, and over a far gap (often back and forth between two walls over a gap). These two cases are very specific and you can presume which the player is intending to perform depending on whether or not the joystick (or keys, etc) are pressed towards or away from the wall. Use this to craft two separate wall jump arcs! Imagine a wall to the players left, if the player is pressing left and activates a wall-jump, kick away from the wall and set an increased vertical velocity to propel the player upwards. If the player is pressing right and activates a wall-jump, skimp on that vertical velocity a bit in favor of horizontal speed; they are likely more worried about jump distance than jump height. Play with these two arcs and how they affect your ability to scale terrain in your game.
11) You may run into a common issue with one of your wall-jumps. When jumping away from a wall, the player naturally pushes off horizontally, and then presses the jump key. If they push away from the wall too early, the player leaves the surface, the wall-jump isn’t triggered, and they fall (often to their death). The solution offered in games like Super Meat Boy, INK, and HackyZack, is the subtle wall stick. When the player begins to push away from a wall, prevent them from doing so for a few frames (I believe that Super Meat Boy does this for 1/8 of a second). It should be just enough time for them to perform the wall-jump. Too long and the game begins to feel unresponsive.
12) Generally, games with wall-jumping also implement a wall-slide, which is really just the alteration of gravity while the player is up against a wall. This is often both the acceleration applied to the player’s fall, as well as the terminal velocity of the fall (fall slower due to additional friction caused by contact with the wall).
13) One common tweak to add to your wall-slide is how friction is handled. The adjusted variables shouldn’t be applied to a player that is still moving upwards along a wall. The player should be able to jump and slide freely up the wall after making contact, rather than being slowed down due to the wall friction that I mention previously. Only after gravity has taken hold of the player and the player’s velocity has dipped back into the positive again, that is when an added friction can be applied.
14) Consider giving extra properties to horizontally moving platforms if wall-jumping is a prominent mechanic in your game. When you are wall-sliding alongside a moving platform, have the player stick to the platform while it is moving *away* from them. Wall-jumping both towards and away from a platform that is moving away is very uncomfortable and essentially strips the player of their wall stick. Another option is to add a similar jump buffer for “was on wall during previous frame”.
15) The friction applied to the player within the air is often referred to as air-resistance. Your chosen air-resistance is one of few variables that often up for great debate among platformer developers. Games like Mario or Megaman have a higher friction value when the player is in the air; often comparable to the friction while grounded. This makes it just as easy to turn around and change direction while jumping. Super Meat Boy on the other hand, has a very low air friction. If you are running and you jump, you maintain your jump arc regardless of whether or not you let go of the joystick or arrow keys. In Meat Boy’s case this is coupled with a proper terminal velocity that allows you to weave in and out of obstacles as you rise and as you fall. Pay attention to your air-resistance options and what these values mean for your style of gameplay.
16) Terminal velocity is the player’s maximum fall speed. The terminal velocity of object’s in your game plays a major part in how *floaty* your controls will feel. A low terminal velocity means that you will have more control over the player as you fall, but falling too slow feels weird to a subset of players. Play with your terminal velocity and how it compares to your air-resistance. These two things are what make these hardcore platformers feel the way that they do.
17) Platforming in tight quarters is often less enjoyable than platforming in large, open areas. Again, this is where meat boy shines. If you want to keep things indoors and keep the challenge up in the players face, consider adding some oil to your corners. This is a technique used to nudge the player, by a pixel or two, around tight corners. One example could be running and jumping out of short tunnel. Rather than bump the player’s head on the way out, you can push the player outward and upward if the clip is subtle. This also works for jumping up onto platforms. If you are one or two pixels short of a platform, you can push the player up and over. This will take some tweaking, but will feel completely natural when implemented properly.
Lesser Notes
That just about covers my technical tips & tricks for polishing your platformer controls and mechanics. I will quickly outline a few other things to take note of (in lesser detail).
– Never forget to add player feedback! Every action should have a reaction. When you jump, spawn particles, when you land spawn particles. Squash and stretch your character on big impacts. Gamepads still have rumble! SCREENSHAKE!
– Typically, the closer to a square your player is, the better this type of controls/mechanics will feel. An exaggerated wall-jump tends to look pretty bad with multiple-tile tall characters (compare Super Meat Boy to Megaman X).
– If your game is difficult, cut your respawn time in half. Cut in half again!
– Avoid tutorials where possible. Teach the player through solid level design.
– In the same way, introduce mechanics in a structured way. First, introduce the mechanic within a somewhat safe space. Then use it in a few more difficult scenarios. After the player is comfortable with the mechanic, combine it with previously introduced mechanics.
– Remember that your difficulty curve needs subtle dips after extreme challenges. Players appreciate an opportunity to feel like they have improved. If your palms are always sweating, you don’t feel that you have grown.
BOOM! That’s it for tonight, everyone! I hope that these different points will help you in some way. I revisit the games that I mentioned and a lot of these tips when I am designing non-platformers, as well. Find new ways to apply different techniques to different kinds of games. If I missed anything, please share!
Thank you all so much for your continued support,
Z