how to go about platformer physics

Started by
7 comments, last by OrangyTang 15 years, 6 months ago
after achieving a certain functionality with my game framework, I wanted to test it out writing a very simple 2D jump'n'run. nothing, I repeat nothing unexpected: run, jump, collect stuff, exit through a door. now the framework doesn't restrict you to tile-basedness, it can handle any convex shape with an arbitrary number of sides (collision and display). the game, however, is tile-ised, for simplicity's sake. there isn't really any physics support in the framework (vector reflection with elasticity and friction), and it isn't used at this stage. the collision detection is separating axes theorem based, does swept testing too - it's results are a collision normal and penetration magnitude (for overlap), and a time data for collisions forward within a given timeframe. gravity and friction (the latter is really just a scalar < 1.0f that the entities' x axis velocity gets multiplied by - I'm guessing this isn't the right approach) is applied on every moving object, at every frame. the problems I've encountered are the following: * at times, when walking a row of tiles, a collision with a tile's side is detected (rather than with its top). * the height of jump varies, presumably depending on how deep the character is into the tile thanks to gravity (although it gets moved out of collision when there is no collision -- so I don't understand) I've found a good couple of threads on 2d platformer physics, and some tutorials -- unfortunately they are more or less specific to tile based games. here they are: http://www.gamedev.net/reference/articles/article694.asp http://www.tonypa.pri.ee/tbw/index.html what would be the most accurate and effective approach to gravity, falling, and jumping? if you only calculate gravity and collision with floors while the character is airborne, how do you determine that the character had walked off a ledge? thanks for your input in advance.
Advertisement
The first (couple) of times I tried a platformer I used the same approach (that is, a simulated particle which had forces and gravity applied to it at all times). Unfortunately as you've found it's very hard to get it to feel like a proper platformer and you get lots of awkward behaviour (like hitting the "side" of platforms you're trying to walk on, etc.).

I've found it much better to explicitly have two different character states - on ground and in air. In the air you do physics-based movement in 2d with forces and gravity, and on the ground you snap to a particular surface and move left/right along it (basically physics along a 1d axis, which may or may not be actually horizontal in the game world).

In both states you need to explicitly check every frame to see if you should switch to the other state. So if you're walking you need to see if your surface movement takes you off the edge of the current surface, or if jump has been pressed, at which point you change state and set up your initial velocity for the jump. If you're in the air you need to be checking to see if you've now collided with the ground in which case you change state and snap to the closest point on the surface you've landed on (if your collision is accurate, the snap shouldn't be visually noticable as it'll be precise to within a pixel).

In a tile based game you need to check after moving left or right if you're still in the same tile or not. If you're not then you need to see if there's another solid tile you can move onto (and hence lock to that surface now) or if not then you start falling.

In a non-tile based game (like I use, with line segments or polys for surfaces) then you need so way of figuring out "adjacent" surfaces if you walk off the end of one.

Hope that helps.
Quote:Original post by OrangyTang
...

I've found it much better to explicitly have two different character states - on ground and in air. In the air you do physics-based movement in 2d with forces and gravity, and on the ground you snap to a particular surface and move left/right along it (basically physics along a 1d axis, which may or may not be actually horizontal in the game world).

In both states you need to explicitly check every frame to see if you should switch to the other state. So if you're walking you need to see if your surface movement takes you off the edge of the current surface, or if jump has been pressed, at which point you change state and set up your initial velocity for the jump. If you're in the air you need to be checking to see if you've now collided with the ground in which case you change state and snap to the closest point on the surface you've landed on (if your collision is accurate, the snap shouldn't be visually noticable as it'll be precise to within a pixel).

...


Thank you so much! I have to say, I felt very lost -- I got to the point of giving the bool isAirborne variable to the entities, but I used it nowhere as consistently as you do (partly because I was a beginner enough to think that realistic physics work=L).

I get the 1d breakdown (a vector perpendicular to the collision normal of the platform can be used to determine the 1d axis to walk along), but how do I determine whether the character is off the edge? (collision test collects vertex data and calculates face normal data from it, but these vectors are normalized and there's no way to relate them back to the original vertices)

One possible solution that comes to mind is to keep checking against collision with the platform at (character.x, character.y + gravity.y). then, on no collision look for another platform or set the character airborne. it looks a bit crude, or does it?

Quote:Original post by OrangyTang
In a non-tile based game (like I use, with line segments or polys for surfaces) then you need so way of figuring out "adjacent" surfaces if you walk off the end of one.

this will probably be a BSP tree, or storing pointers , or something... any related books out there that you have found useful?
I should also mention that the player control/physics are very important in a platformer - after all you're going to spend most of the game moving the player around and jumping, so it's got to feel spot on. Don't be afraid to try a few different approaches and put aside a good chunk of time tweeking and refining them. And don't be afraid to add special cases or deviate from "proper" / physically accurate methods if that's what you need to get it to feel right.
Quote:Original post by trajectorymodifier
I get the 1d breakdown (a vector perpendicular to the collision normal of the platform can be used to determine the 1d axis to walk along), but how do I determine whether the character is off the edge? (collision test collects vertex data and calculates face normal data from it, but these vectors are normalized and there's no way to relate them back to the original vertices)

When on the ground I like to store the player's position as relative to the current surface, so something like:

struct SurfacePosition{  LineSegment2D* surface;  float distance;}


'distance' is the distance along the surface segment, so moving just means adding your current speed to it. Then if it's less than zero, or greater than surface.length() you've stepped off the edge.

Depending on your levels you might have surfaces represented as just single line segments, or if you need something more complicated then chains of line segments, with next and previous pointers. Then if you walk off an edge you see if there's another edge to switch to, or if it's NULL then fall. If need be you can wrap up the code to navigate along a series of segments into it's own SurfaceFollower class so it can be easily shared between your player movement and your enemies.

I'm rusty on BSPs, so no links there I'm afraid. I'm sure someone else can provide. [grin]
Quote:Original post by OrangyTang
When on the ground I like to store the player's position as relative to the current surface, so something like:

*** Source Snippet Removed ***

'distance' is the distance along the surface segment, so moving just means adding your current speed to it. Then if it's less than zero, or greater than surface.length() you've stepped off the edge.

Depending on your levels you might have surfaces represented as just single line segments, or if you need something more complicated then chains of line segments, with next and previous pointers. Then if you walk off an edge you see if there's another edge to switch to, or if it's NULL then fall. If need be you can wrap up the code to navigate along a series of segments into it's own SurfaceFollower class so it can be easily shared between your player movement and your enemies.


Awesome.=) In the meantime I've been checking out Snowman Village and I can see how this approach is versatile. I'm guessing Sega have used something similar in the Sonic games (man, how much I wondered how did they implement those loops), etc.

I'll think of something how to calculate these surfaces from tiles, as I want to keep redundancy to a minimum. For my shapes, I just store the vertices in a Ring container, then collision detection connects the dots for itself. But probably, in a game-specific use, the engine can create chains of surfaces from tiles that are adjacent... or dunno.
Quote:Original post by trajectorymodifier
Awesome.=) In the meantime I've been checking out Snowman Village and I can see how this approach is versatile. I'm guessing Sega have used something similar in the Sonic games (man, how much I wondered how did they implement those loops), etc.

Yeah, one of these days I'm going to have a crack at doing a proper Sonic clone with loops and corkscrews and all that jazz. I guess they do something similar, but with slightly more complicated 1d physics so you can fall off loops when going too slow or stick to it if you're going fast enough. Particularly in the later 2d games (Sonic 3, S&K) there's sequences where your character is "on rails" and following something more complex than the actual tile map, suggesting they've got invisible paths in the levels they're using for collision.

Quote:I'll think of something how to calculate these surfaces from tiles, as I want to keep redundancy to a minimum. For my shapes, I just store the vertices in a Ring container, then collision detection connects the dots for itself. But probably, in a game-specific use, the engine can create chains of surfaces from tiles that are adjacent... or dunno.

Generating them from a tile map is certainly a good idea. For Snowman Village all the levels were randomly generated so I didn't need a level editor. For my current platformer I'm trying to use a vector editor for the maps but to be honest it's not working out particularly well and I'm thinking of switching to something tile-based and generating the collision surfaces from the tiles (either at level load or at compile time depending on how fast it ends up being).
Quote:Original post by OrangyTang
Quote:Original post by trajectorymodifier
Awesome.=) In the meantime I've been checking out Snowman Village and I can see how this approach is versatile. I'm guessing Sega have used something similar in the Sonic games (man, how much I wondered how did they implement those loops), etc.

Yeah, one of these days I'm going to have a crack at doing a proper Sonic clone with loops and corkscrews and all that jazz. I guess they do something similar, but with slightly more complicated 1d physics so you can fall off loops when going too slow or stick to it if you're going fast enough. Particularly in the later 2d games (Sonic 3, S&K) there's sequences where your character is "on rails" and following something more complex than the actual tile map, suggesting they've got invisible paths in the levels they're using for collision.

I was hoping that these aren't necessary -- but for complex structures like those corkscrews the tile map certainly isn't enough information. It took me a minute or two to figure how to go about walls (vs floors) but I guess, collision detection mechanics work as normal whilst in "1d mode" - it's gravity that doesn't. In other words, I really only have to calculate the walkable surfaces. For huge stages, like those in Sonic, there must be some means to break down the map into smaller units; I can't see the engine traversing everything at every frame. Probably this is where a tile based approach will kick in, the tile being the size of the screen. (I'm guessing this will ultimately result in the sacrifice of the easily readable text-based maps I'm using.... or will it?)

Funny to see the design fall back to something similar to rooms in those oldschool non-scrolling platformers.=)

Quote:Original post by OrangyTang
Quote:Original post by trajectorymodifier
I'll think of something how to calculate these surfaces from tiles, as I want to keep redundancy to a minimum. For my shapes, I just store the vertices in a Ring container, then collision detection connects the dots for itself. But probably, in a game-specific use, the engine can create chains of surfaces from tiles that are adjacent... or dunno.

Generating them from a tile map is certainly a good idea. For Snowman Village all the levels were randomly generated so I didn't need a level editor. For my current platformer I'm trying to use a vector editor for the maps but to be honest it's not working out particularly well and I'm thinking of switching to something tile-based and generating the collision surfaces from the tiles (either at level load or at compile time depending on how fast it ends up being).

Oh yeah, I was about to start working on something I called a "generic format level editor". I'm so glad I didn't, yet.=) The objective is to provide a tool for creating huge maps of convex shapes that can be positioned freely, have an arbitrary number of sides, and can either be textured / plain. I was gonna start on using layers for parallax scrolling -- now this is something that's most probably killed by the "room" (screen-sized tile) approach, or is it?

Well, up to now I didn't write a hell of a lot documentation... This is probably the point where I ought to start doing it...
Quote:Original post by trajectorymodifier
I was hoping that these aren't necessary -- but for complex structures like those corkscrews the tile map certainly isn't enough information. It took me a minute or two to figure how to go about walls (vs floors) but I guess, collision detection mechanics work as normal whilst in "1d mode" - it's gravity that doesn't. In other words, I really only have to calculate the walkable surfaces. For huge stages, like those in Sonic, there must be some means to break down the map into smaller units; I can't see the engine traversing everything at every frame. Probably this is where a tile based approach will kick in, the tile being the size of the screen. (I'm guessing this will ultimately result in the sacrifice of the easily readable text-based maps I'm using.... or will it?)

IIRC, Sonic 3 actually stored the levels in a compressed form and decompressed them on the fly as you moved through them. Pretty slick for such and old game. Of course they had the advantage that on a cartridge there's no seek times like on a physical disk, and read times are tiny too. For a pc game I wouldn't worry about the streaming, but instead use something like a quadtree to divide your level up and speed up collision checks.

This topic is closed to new replies.

Advertisement