Speed and gravity
This page describes how to implement basic physics in sidescroller games (running around, jumping, etc.). This should help you make your game have decent controls and in many cases may be more than enough. You should be able to also apply similar concepts in other situations.
Note: this page doesn't talk about collision (that's a whole topic that deserves a page on its own and depends on the particular game), only speed physics.
Subpixels
First of all: using pixels for position is a bad idea. Think about it: if the slowest speed you can store is 1px, then that'll be 60px per second (since you add it every frame). You can't do slower than that, and the next step is 120px per second. That's way too much.
Instead we must use a smaller unit (a "subpixel"), or in other words, make it so that 1px is a large number, e.g. 65536 (a power of two). Then if e.g. we want to move 0.75px per frame (45px per second), we could store it as 0.75 × 65536 = 49152. This is called "fixed point".
To show something on screen, we divide the number by a pixel, but we keep the whole number while doing physics. Division on the 68000 is slow, so make sure to use a power of two (then you can use bit shifting, which is much faster!).
Iwis says
You may want to look at our page on fixed point to get a better idea of what we're talking about as well as some hints on how to do this in an optimal way.
Iwis says
If you're using SGDK, it already comes with fixed point stuff. Look at
the fix32
type and relevant functions like
fix32Add
(add two fix32
values) and
fix32Int
(get the integer part out of a fix32
).
X and Y speeds
There are two speed values you should need:
- Horizontal speed (X speed)
- Vertical speed (Y speed)
They should be self-explanatory. The "speed" is pretty much how many pixels an object moves every frame. You should keep track of the speed of every object across frames, so store it as part of their current state.
Every frame, after you've done all speed calculations (what we're going to see after this), you can apply the result by simply adding the speed to the current position:
x = x + x_speed
y = y + y_speed
Running around
The simple choice to make a character walk or run is to set the horizontal speed (X speed) to how fast they should move, but that doesn't feel natural. Instead, you should accelerate the speed, i.e. make the speed increase over a few frames.
Acceleration is simply incrementing speed every frame:
x_speed = x_speed + acceleration
However, you don't want to keep going faster non-stop! You want to impose a limit on how fast you go (known as the "speed cap"). So if we cross the speed cap, we prevent the speed from going beyond that. Note that if we were already going faster we do nothing at all (to prevent awkward situations).
The reason for checking whether acceleration is positive or negative is to cope with the fact that the limit needs to match, nothing more.
if acceleration == 0
do nothing
else if acceleration > 0 and x_speed < limit
x_speed = x_speed + acceleration
if x_speed > limit then x_speed = limit
else if acceleration < 0 and x_speed > -limit
x_speed = x_speed + acceleration
if x_speed < -limit then x_speed = -limit
end
Stop running
When we release the left/right button we want to decelerate, i.e. start slowing down. Deceleration is decrementing the speed every frame. We check that it doesn't cross 0 (since it may not land exactly on it), but it's otherwise similar to what we did above.
if x_speed == 0
do nothing
else if x_speed > 0
x_speed = x_speed - deceleration
if x_speed < 0 then x_speed = 0
else if x_speed < 0
x_speed = x_speed - deceleration
if x_speed > 0 then x_speed = 0
end
Running into a wall
When moving around you may run into a wall and will need to react to it. Just set horizontal speed to 0 to stop.
Jump and fall
Besides horizontal speed (X speed), there's also vertical speed (Y speed). Gravity is accelerating the vertical speed every frame (e.g. increase it 0.25px every frame). When you hit the floor, the vertical speed is reset back to 0, so that next time you fall you will start by falling slowly again.
y_speed = y_speed + gravity
Jumping is pretty much setting the vertical speed to a large negative number, e.g. -7px per frame. So, if the jump button is pressed and you're on the floor, change the vertical speed then let gravity do its job (and make sure to make a "boing" noise!).
if jump_button and on_floor
y_speed = -jump_speed
play_sfx jump
end
Friction while in the air
If something feels off, that's because while you're in the air it should be harder to change your horizontal speed! To fix this, we can make both acceleration and deceleration to be half as strong while we're in the air (do this before doing the speed calculations):
if not on_floor
acceleration = acceleration >> 1
deceleration = deceleration >> 1
end
Variable jump height
In many genres it's common to be able to jump higher or lower depending how "hard" (how long) you press the jump button. There are several approaches to this, I'm going to focus on the Project MD approach since it's simple.
The idea is: if you're moving upwards and you aren't holding down the jump button, then you fall faster. Which means that applying gravity now should look more like this:
y_speed = y_speed + gravity
if y_speed < 0 and (not jump_button)
y_speed = y_speed + extra_gravity
end
Useful numbers
Here are some numbers that have served me well from experience, mainly intended for the kind of platformers you normally see on 4th generation consoles. You should tweak these depending on your needs (try them, play, then change anything that feels off).
- Running max speed: 2.5px per frame
- Running acceleration: 0.25px per frame
- Initial jump speed: -7px per frame
- Usual falling speed: 0.25px per frame
- Extra falling speed: 0.25px per frame more
If you're making a game more like Sonic (where the emphasis is on slowly gaining and keeping momentum), then something like this may fit better:
- Running max speed: 5px per frame
- Running acceleration: 0.125px per frame