Kain
December 22, 2012, 8:50pm
1
Hello everybody, I’m trying to learn SDL using the tutorials for some time now, and I’m trying to figure out how to make a 2D platformer. I have figured out most of the stuff, but I have some problems getting jumping, gravity and tile collision to work together.
I’m currently trying to make a very simple platformer with a tile based level, but I can’t quite get the character to behave right when he collides with the tiles.
This is the piece of code that seems to cause me all the trouble.
You may need a barf bag.
const int gravity = 10;
void sprite::events()
{
if( event.type == SDL_KEYDOWN )
{
switch( event.key.keysym.sym )
{
case SDLK_d:
vel += SPRITE_WIDTH / 4;
break;
case SDLK_a:
vel -= SPRITE_WIDTH / 4;
break;
default:
{
}
}
if( event.key.keysym.sym == SDLK_SPACE && jump == false )
{
jump = true;
jvel = -50;
}
}
if( event.type == SDL_KEYUP )
{
switch( event.key.keysym.sym )
{
case SDLK_d:
vel -= SPRITE_WIDTH / 4;
break;
case SDLK_a:
vel += SPRITE_WIDTH / 4;
break;
default:
{
}
}
}
}
void sprite::move( tile * tiles[] )
{
x += vel;
box.x = x;
}
if( ( x < 0 ) || ( x + SPRITE_WIDTH > LEVEL_WIDTH ) || collision( box , tiles ) == true )
{
x -= vel;
box.x = x;
}
y += jvel;
jvel += gravity;
box.y = y;
if( jvel >= gravity * 5 )
{
jvel = gravity * 5;
}
if( y > LEVEL_HEIGHT )
{
y = 0;
x = 0;
jvel = 0;
}
if( collision( box , tiles ) == true && jump == true )
{
jump = false;
y -= jvel;
box.y = y;
jvel = 0;
}
if( collision( box , tiles ) == true ) jvel = 0;
if( collision( box , tiles ) == false && jvel >= gravity ) jump = true;
}
When the character stands on a tile, he continuously jumps 10 pixels up and down.
From what I understand, this is caused by gravity. When the character collides with the tiles, y (the character position) gets reduced by jvel, and jvel becomes 0. But then, gravity makes jvel 10 again, and y gets reduced by it to start all over.
If I do not turn jvel to 0, the character will stand OK on the ground, but if he jumps on a platform he will be standing jvel pixels above it.
I’m not sure what to do here, so some help would be appreciated.
Thank you in advance.
Take a look at this
http://www.parallelrealities.co.uk/2011/10/intermediate-game-tutorial-4-tile-based.html
Basically, you should check each axis separately, so test the horizontal movement first, then the vertical movement. If the character hits a wall, set the velocity of that axis to 0 and place the character as close to the wall as possible.
Kain
January 3, 2013, 3:23pm
3
Thanks.
I actually figured it out without having to seperate vertical from horizontal collisions.
Here is the code for anyone interested.
class sprite
{
private:
SDL_Rect box;
int jvel;
int y;
bool jump;
int offs;
int vel;
int frame;
int status;
public:
sprite( int X , int Y );
void events();
void jumps();
void move( tile * tiles[] );
void scamera();
void show();
};
sprite::sprite( int X , int Y )
{
jvel = 0;
y = Y;
jump = false;
offs = X;
vel = 0;
frame = 0;
status = FR;
box.x = offs;
box.y = y;
box.w = FW;
box.h = FH;
}
void sprite::events()
{
if( event.type == SDL_KEYDOWN )
{
switch( event.key.keysym.sym )
{
case SDLK_d:
vel += FW / 4;
break;
case SDLK_a:
vel -= FW / 4;
break;
default:
{
}
}
}
if( event.type == SDL_KEYUP )
{
switch( event.key.keysym.sym )
{
case SDLK_d:
vel -= FW / 4;
break;
case SDLK_a:
vel += FW / 4;
break;
default:
{
}
}
}
}
void sprite::jumps()
{
Uint8* keystate = SDL_GetKeyState(NULL);
if(keystate[SDLK_SPACE] && jump == false )
{
jump = true;
jvel = -50;
}
}
void sprite::move( tile * tiles[] )
{
offs += vel;
box.x = offs;
if( ( offs < 0 ) || ( offs + FW > LW ) || col( box , tiles ) )
{
offs -= vel;
box.x = offs;
}
y += jvel;
box.y = y;
if( col( box , tiles ) == false && jvel >= gravity )
{
jump = true;
}
if( col( box , tiles ) == true )
{
if( jump == true && jvel >= 0 )
{
jump = false;
}
if( jvel >= 0)
{
y -= jvel - (TH % jvel);
jvel = 0;
}
else
{
y -= jvel;
jvel = 0;
}
box.y = y;
}
jvel += gravity;
if( jvel >= gravity * 5 )
{
jvel = gravity * 5;
}
if( y > LH + 50 )
{
y = 0;
offs = 0;
status = FR;
}
}
void sprite::scamera()
{
camera.x = ( offs + FW / 2 ) - SW / 2;
camera.y = ( y + FH / 2 ) - SH / 2;
if( camera.x < 0 )
{
camera.x = 0;
}
if( camera.x > LW - camera.w )
{
camera.x = LW - camera.w;
}
if( camera.y < 0 )
{
camera.y = 0;
}
if( camera.y > LH - camera.h )
{
camera.y = LH - camera.h;
}
}
void sprite::show()
{
if( vel < 0 )
{
status = FL;
frame++;
}
else if( vel > 0 )
{
status = FR;
frame++;
}
else
{
frame = 0;
}
if( frame >= 4 )
{
frame = 0;
}
if( status == FR )
{
apply( offs - camera.x , y - camera.y , face , screen , frame * FW , 0 , FW , FH );
}
else if( status == FL )
{
apply( offs - camera.x , y - camera.y , face , screen , frame * FW , 100 , FW , FH );
}
}
…And the main loop!
int main( int argc , char * args[] )
{
bool quit = false;
if( init() == false )
{
return 1;
}
if( loadf() == false )
{
return 2;
}
timer fps;
sprite s( 50 , 50 );
tile * tiles[TT];
if( settiles( tiles ) == false )
{
return 5;
}
while( quit == false )
{
fps.start();
scrolling();
if( edit == true ) edshow( current );
while( SDL_PollEvent( &event ) )
{
if( event.type == SDL_KEYDOWN )
{
if( event.key.keysym.sym == SDLK_l )
{
edit = !edit;
}
}
if( event.type == SDL_QUIT )
{
quit = true;
}
if( edit == false )
{
s.events();
}
else
{
edevents( tiles );
}
}
if( edit == false )
{
s.jumps();
scrolling();
s.move( tiles );
s.scamera();
}
else
{
edcamera();
}
for( int t = 0 ; t < TT ; t++ )
{
tiles[t]->show();
}
if( edit == false )
{
s.show();
}
if( SDL_Flip( screen ) == -1 )
{
return 4;
}
if( fps.gett() < 1000 / FPS )
{
SDL_Delay( ( 1000 / FPS ) - fps.gett() );
}
}
edsave( tiles );
clean( tiles );
return 0;
}
1 Like