I've noticed a lot of people in the help section looking for assistance with controlling their jumps in platformers. I thought I'd put together a quick lesson (not tutorial, a tutorial is a walkthrough, while a lesson is to help you learn something) about jumping and include a few events I use that help me a lot. So without further ado:
PERFECTING JUMP CONTROL
Note: This lesson is based on much of the same environment as in Deadeye's Platform School. To simplify, I'll assume you're using that as a starting point and just add the jump control functionality to it.
Note: If you are using the latest version of Construct, and you are loading up Deadeye's tutorial, you will notice that a lot of it doesn't work. To fix most of this, simply open up the Animation and Controls event sheets and find every condition that references "Player % 1", open the condition, and click "Finish". This will replace "Player % 1" with "Player 1" and solve those pesky problems.
Final note (I promise): If you decide to skim the lesson because you aren't interested in the theory, just do the things written in italics. They're the most important elements.
Each platformer is different. The size of the character in relation to the environment, the character's movement speed, the nature of the action element (are the challenges platform driven, melee combat driven, ranged combat driven, etc.), and various other factors combine to determine what jump mechanics look---and more importantly, feel---best for the game. This is why you're here, presumably: the default jump mechanics, no matter how much you tweak them, look or feel wrong, and you want to make them better.
The lesson goals are:
- Set up text events to debug, or observe, the relevant data generated by jumping
- Set up key events to modify properties during gameplay
- Set up condition events to add more controllable variable-height jumps
The term "debugging" usually refers to the process of removing bugs from your code, but it has become synonymous with keeping an eye on your code's execution to see what's going on and manage it. Our first goal involves creating a debug gui that will show us important data, in this case vertical speed and jump strength. We need to be able to see these variables because in the next goal we'll be setting up keypress events that can change them.
First, you should relegate the debug gui elements to a layer that can be hidden or shown at runtime, because this makes it easy to remove it from the playable game while keeping it available to you for testing. Create a new layer on top, and in the layer's properties set the name to something descriptive like "Debug Gui", and set the Scroll X Rate and Scroll Y Rate to 0%. You'll use this layer to create your text fields.
You need text fields to write variable data to, so on the Debug Gui layer, create two Text objects and call them "DebugTextVectorY" and "DebugTextJumpStrength". If you want to tweak even more options, you can add a text field for each option you want to view. Format these text fields however is convenient for you---they won't appear when you compile the game so don't worry too much about it. I find that bold, white text with a transparent background is most unobtrusive.
Now we need to create the events that will spit out variable data to the debug fields. Create a new event sheet and name it "Debug". Complex debugging routines can bog down the system so it's important to keep them separate from the rest of the game so you can easily disable them when it comes time to compile the game.
We're going to want to know vertical speed later, so we're going to want to know the player's VectorY value, so create a new event with the System->Always condition. Add an action to the event: Select the DebugTextVectorY object and add the Set Text action, setting the value to
"Player.VectorY: " & Player[Platform].VectorY[/code:3895go0j][/i] (note: if your player object is named something other than player, you should[i] change Player[Platform] to [i]YourObject[/i][Platform])[/i]
Take note: the "&" symbol means you're concatenating, or adding, the variable to the end of the text.
If you run the game now, every time you jump you'll get a ridiculously long floating point number. Cripes! Let's fix that: [i]change [code:3895go0j]Player[Platform].VectorY[/code:3895go0j] to: [code:3895go0j]Round(Player[Platform].VectorY)[/code:3895go0j][/i] Now you'll be given a less accurate, but more usable number (since you're never going to set your VectorY to 158.1736495717794381). These sorts of functions are invaluable (and not just for debugging), so you'll probably want to keep [url=http://sourceforge.net/apps/mediawiki/construct/index.php?title=System_Expressions]this list of them on the Construct Wiki[/url] bookmarked.
You're done setting up your debug screen...
Or next step is to set up events that allow us to change the player's jump strength on the fly by adding to or subtracting from the value in increments of 10. This functionality will allow us to fine-tune the jumping so that the height is precisely what we want (we can also set up similar functionality for gravity and so forth to allow even more precision).
[i]Add another event, this time with a keyboard/mouse "On key released" condition and choose a key from the list that will serve as your "increase jump strength" key[/i]. I prefer Numpad Add. [i]Now add two actions: first, select your Player object and set its Jump Strength to [code:3895go0j]Player[Platform].JumpStrength + 10[/code:3895go0j] Second, set the text of DebugTextJumpStrength like you did with DebugTextVectorY, this time setting the value to: [code:3895go0j]"Player.JumpStrength: " & Player[Platform].JumpStrength[/code:3895go0j][/i]
This event can be duplicated and modified so that it responds to a different keypress (as implied, I prefer Numpad Substract) and subtract 10 instead of adding 10. You now have a functional jump tweaking system! Run your game and try it out. Now it's very easy to change the mechanics of your jumps and see instantly what those changes do.
[h2][b]Variable Height Jumps[/b][/h2]
Construct has a built-in system called "jump sustain" that allows you to apply different gravity to a jump for a specific length of time as long as you're holding the jump key, which supposedly lets you set up variable-height jumps. I find this system to be very unsuitable for jumping---in my opinion, it feels more like my character has a jet-pack on than that I'm controlling jump height.
In typical platformers like Mario Brothers or Megaman, you can stop jumping in mid-air by releasing the jump button, and that's the functionality we're going to emulate here, because it is what the players expect, and we must always remember the golden rule of interface design:
[b]If the players are used to a convention, unless you have a good reason to change that convention, use it; likewise, if players are not used to a convention, unless you have good reason to implement it, leave it out.[/b]
The initial concept behind setting this functionality is actually very simple: we want to stop the player's vertical motion if the jump button isn't being pressed. [i]So, create an event in the Controls event sheet with the conditions "Player is jumping" and "Control is not pressed", and add an action to set Player[Platform].VectorY to 0[/i]. Now if you run the game, you'll that if you let go of the jump button before you reach the apex of your jump, you'll stop and drop to the ground. Excellent.
But it's a little sudden, and it doesn't look as smooth as a natural jump. This is where the VectorY debug field we made earlier will come in. As our character jumps, we can see his vertical speed and get an idea at what speed the crest of his jump starts.
What we're going to do is set up a private variable that will determine the minimum speed the character must be going to allow for an early crest---if the character is going slower than the variable, he's already cresting. [i]Under the player's properties tab, create a new private variable called "Jump Cap" and set its default value to something that seems reasonable like 50[/i]. You could also set up a debug event like we did before to change Jump Cap on the fly and tweak your settings.
Next, [i]modify the jump event you made earlier by adding a new condition: in this case we'll use System->Compare to find out if: [code:3895go0j]Player[Platform].VectorY is less than Player.Value("Jump Cap")[/code:3895go0j][/i](upwards motion is expressed in negative numbers, so lower numbers mean faster upwards movement)
[i]Now modify the action, changing 0 to Player.Value("Jump Cap").[/i] [b]IMPORTANT NOTE: Wherever possible, when creating custom behavior, use private variables instead of hard-coded values! This allows you to make changes (in this case, change the designated "crest speed") in one location, instead of having to wade through all of your events and change every instance.[/b]
With this set, your player will crest every jump instead of just sharply falling off. If the value of 50 is still too sharp, you can continue to tweak it until you have what looks like a nice smooth crest.
I hope this will help some of you! I'd like to say in closing how important a good, solid control scheme is. I'll paraphrase an article written by Ben Cousins in [i]Develop Magazine[/i] (August 2002):
[quote:3895go0j]Hit games with well-received gameplay have level lengths clustering around 1 minute 10 seconds, characters that jump have elapsed time in the air clustering around 07 seconds, and the elapsed time to perform three combat moves in succession clusters around 2 seconds. These should be considered constants for good gameplay.
Of course, there are always exceptions, but you should always consider the impact every element of your game has on the player, right down to the jump strength.