SDL Player movement & animation glitch

This topic is 1820 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

This is a video of the bug ill explain and post code below

when you just press down the player moves down with the animation

if you press a another direction key with down it will take it direction sprite and move down

heres the code

        if(in_game_state == 1 && app_state == 5) {
keystate = SDL_GetKeyState(NULL);
if(keystate[SDLK_DOWN]) {
if(btnPressed == 1) {
if(player_pos == 2) {
player_pos = 1;
}else if(player_pos == 1) {
player_pos = 3;
}else if(player_pos == 3){
player_pos = 2;
}
}else{
player_pos = 2;
}
move_y -= 1;
btnPressed = 1;
}else if(keystate[SDLK_UP])   {
player_pos = 11;
move_y += 1;
btnPressed = 1;
}else if(keystate[SDLK_RIGHT]) {
player_pos = 8;
move_x -= 1;
btnPressed = 1;
}else if(keystate[SDLK_LEFT]) {
player_pos = 5;
move_x += 1;
btnPressed = 1;
}else{
btnPressed = 0;
}

}


Defining variables

in_game_state -  is just that it that the player is on the map and is allowed to move

app_state - is just so we know we are on the map part of the game

btnPressed - is just if the button pressed or not

move_x - is the players position on the screen

move_y - is the players position on the screen

player_pos = is the position of the sprite being used ( this is how im doing the animation )

Edited by MrPhoenix

Share on other sites

I don't use SDL to know exactly how the keystate works, but if it works how I imagine then:

if(keystate[SDLK_DOWN]) {
...
}else if(keystate[SDLK_UP]) {
...
}else if(keystate[SDLK_RIGHT]) {
...
}else if(keystate[SDLK_LEFT]) {
...
}


should be

if(keystate[SDLK_DOWN]) {
...
}else if(keystate[SDLK_UP]) {
...
}

if(keystate[SDLK_RIGHT]) {
...
}else if(keystate[SDLK_LEFT]) {
...
}


Because that if/else meant that if down was pressed, then it wouldn't account for any other keys being pressed.

It's ok to have the down/up and left/right in a if/else block because the user won't press both at the same time (or at least, you can't move both up AND down), but the user CAN move both up/down AND left/right at the same time.

Share on other sites

Milcho if i dont do it the way it is, then the player has the freedom to move in more then one direction at once which i dont want

Share on other sites

Ah, I didn't realize that you didn't want player to move in more than one direction at once.

Then the solution isn't to use the keystate.

What you need is to receive the key-down events from SDL - I'm sure there's some way. Because what you're talking about then is basing the direction the player is moving based on which key was pressed last. In pseudo-code you would need some function that would look something like this:

void ReceiveSDLKeyEvent ( key )
{

if (key == SDLK_UP)
{
movementDirection = MOVE_UP; //define MOVE_UP as some value, or make it part of an enum
}
else if (key == SDLK_DOWN)
{
movementDirecton = MOVE_DOWN;
}
else if (key == SDLK_LEFT)
{
movementDirecton = MOVE_LEFT;
}
else if (key == SDLK_RIGHT)
{
movementDirecton = MOVE_RIGHT;
}

}



After this, your loop (the code you posed) would look something like this:

if( movementDirecton == MOVE_DOWN ) {
...
}else if( movementDirecton == MOVE_UP ) {
...
}else if( movementDirecton == MOVE_RIGHT ) {
...
}else if( movementDirecton == MOVE_LEFT ) {
...
}


You can't do that with just using states, because knowing the state won't tell you which key was pressed last. (admittedly there's a roundabout way to keep track of the last state, and extract the information of which key was pressed last by comparing the last and current keystates, but that's just reworking what I'm sure SDL already provides you with - which is the even information)

Share on other sites

i think the error is when i change the player_pos int

Share on other sites
Yeah, if player_pos does not have a value from 1-3 and btnPressed is 1, then the code isn't going to change player_pos if you are holding the down button. I would change this code:
                    if(btnPressed == 1) {
if(player_pos == 2) {
player_pos = 1;
}else if(player_pos == 1) {
player_pos = 3;
}else if(player_pos == 3){
player_pos = 2;
}
}

by changing the line "}else if(player_pos == 3)}" to just "}else{", which will allow the code to respond correctly if player_pos is taking a value corresponding to the other movement directions.

Share on other sites

If you arrange your sprite sheet so that rows represent directions to face and columns represent steps being taken then you can keep your direction separate from the animation step and just calculate your source rect x/y independently.

http://imageshack.us/photo/my-images/843/bigsprites.jpg/sr=1

If no direction is held then set the animation counter to zero. Otherwise set the direction and increment the animation counter. You can loop the animation by modulating it whenever you increment it:

int main() {
int x = 0;
int y;
for(y = 0; y < 16; ++y) {
++x;
x %= 4;
printf("X is %i.\n", x);
}
}

It may also help both readability and utility if you implement a direction function:

enum DIRECTION {
DIR_DOWN,
DIR_UP,
DIR_LEFT,
DIR_RIGHT,
DIR_NONE
};

DIRECTION dir4() {
if(keystate[SDLK_DOWN]) return DIR_DOWN;
if(keystate[SDLK_UP]) return DIR_UP;
if(keystate[SDLK_LEFT]) return DIR_LEFT;
if(keystate[SDLK_RIGHT]) return DIR_RIGHT;
return DIR_NONE;
}


Remember that you can optionally assign values to enumeration keys:

enum STUFF {
THINGY = 1,
CRUD = 2,
HERP = 17
};

An alternative to all this would be to simply detect the circumstance where the player is holding two directions and pop up a window that says

"Electric Slide Bonus

555 points

You dancin' GOOD!"

Share on other sites

i think the error is when i change the player_pos int

Are you trying to make it so that if Down is pressed and the user presses Right then your player will continue moving down, without changing the sprite to be using the 'move right' animation?

Or are you trying to make it so that if Down is pressed and you press Right, the player starts moving to the right, with the correct animation?

Share on other sites

this is what i ended up coming up with that seem to work pretty good

    static void player_movement(void) {

if(control_event.type == SDL_KEYDOWN) {

switch(control_event.key.keysym.sym) {
case SDLK_DOWN:
if(btnPressed == 1) {
if(player_pos == 2) {
player_pos = 3;
}else if(player_pos == 3){
player_pos = 1;
}else{
player_pos = 2;
}
}else{
player_pos = 2;
}
move_y -= 1;
break;
case SDLK_UP:
if(btnPressed == 1) {
if(player_pos == 11) {
player_pos = 12;
}else if(player_pos == 12) {
player_pos = 10;
}else{
player_pos = 11;
}
}else{
player_pos = 11;
}
move_y += 1;
break;
case SDLK_RIGHT:
if(btnPressed == 1) {
if(player_pos == 8) {
player_pos = 9;
}else if(player_pos == 9) {
player_pos = 7;
}else{
player_pos = 8;
}
}else{
player_pos = 8;
}
move_x -= 1;
break;
case SDLK_LEFT:
if(btnPressed == 1) {
if(player_pos == 5) {
player_pos = 6;
}else if(player_pos == 6){
player_pos = 4;
}else{
player_pos = 5;
}
}else{
player_pos = 5;
}
move_x += 1;
break;
default:
break;
}
btnPressed = 1;

}else{
btnPressed = 0;
}

}


only thing now is that the animations seem to fast

Share on other sites

So you did end up using the keydown events, as I suggested. The keystates you were using before are distinctly different from the keydown events. Keystates are persistent, and won't just report which key was last pressed - which is what the key events report. :)

The reason your animation may be odd now is because the key down events are automatically sent at a certain rate (determined by the OS) when you hold down a key. Proper animation should be done outside the key down handling, you should set some flag to indicate which direction you're moving in, and animate somewhere else (outside the keydown event handling) based on that flag. See what I suggested in my previous post.

Share on other sites

Milcho i am looking for sprite artist and coders for my project since im looking to make it open source on github

Share on other sites

Milcho i am looking for sprite artist and coders for my project since im looking to make it open source on github

I thought you were saying that your animation was too fast - and I assumed you had the correct sprites for the animation. I still think your actual animation should be done outside the keydown handling, but if your problem is with the animation sprites not being as you want them, then I can't help you.

Share on other sites

i was not asking to help with sprite, i was just letting you know i might be making the project open source soon and thought you might have some interest in it!

im working on the animation at the moment

here some of the maps so far :)

First map

route connecting to the next city

and the Ember City that the route connects to

Share on other sites

I anticipated your animation speed problem when I was making my previous post but figured I'd wait until you asked. (It's the next issue to arise in the logical order of what you're doing here.)

Simply use an animation step that is a multiple of the number of your animation frame count and then select the frame by integer division. This is going to get progressively more difficult if you don't either rearrange your sprite sheet or else abstract the positioning so that you can refer to the direction and animation separately. This function is already really big.

Check this out:

enum DIRECTION {
DIR_DOWN = 0,
DIR_LEFT,
DIR_RIGHT,
DIR_UP,
DIR_NONE
};

const int ANIMATION_FRAMES = 4;
const int ANIMATION_RATE = 15; //play with this number to control animation speed
const int ANIMSTEP_MAX = ANIMATION_FRAMES * ANIMATION_RATE;

DIRECTION dir4(Uint8* keystate) {
if(keystate[SDLK_DOWN])  {return DIR_DOWN;}
if(keystate[SDLK_LEFT])  {return DIR_LEFT;}
if(keystate[SDLK_RIGHT]) {return DIR_RIGHT;}
if(keystate[SDLK_UP])    {return DIR_UP;}
return DIR_NONE;
}

DIRECTION direction = DIR_DOWN;
int animstep = 0;

static void player_movement(void) {
//This should really be called from outside and handed in.
//You want the same control state for the whole logical frame.
Uint8* keystate = SDL_GetKeyState(NULL);
DIRECTION dir = dir4(keystate);
if(dir == DIR_NONE) {
animstep = 0;
return;
}
direction = dir;
++animstep;
animstep %= ANIMATION_MAX;
switch(direction) {
case DIR_DOWN:  player_y += speed; break;
case DIR_UP:    player_y -= speed; break;
case DIR_RIGHT: player_x += speed; break;
case DIR_LEFT:  player_x -= speed; break;
}
}



After running player_movement your direction will be in 'direction'.
The frame of animation to render for that direction will be 'animstep / ANIMATION_RATE'.

dir4() imposes an order of precedence on your direction buttons, but that's really a pretty common thing in 4-direction schemes. If you really feel the need to direct according to the last button pressed then you can create a precedence function that reacts to key events and rewrite dir4() to prioritize that direction:

int priorityState = SDLK_DOWN;
DIRECTION priorityDir = DIR_DOWN;

void react_to_key_event() {
if(control_event.type == SDL_KEYDOWN) {
switch(control_event.key.keysym.sym) {
case SDLK_DOWN:
priorityState = SDLK_DOWN;
priorityDir = DIR_DOWN;
break;
case SDLK_LEFT:
priorityState = SDLK_LEFT;
priorityDir = DIR_LEFT;
break;
case SDLK_RIGHT:
priorityState = SDLK_RIGHT;
priorityDir = DIR_RIGHT;
break;
case SDLK_UP:
priorityState = SDLK_UP;
priorityDir = DIR_UP;
break;
}
}
}

DIRECTION dir4(Uint8* keystate) {
if(keystate[priorityState]) {return priorityDir;}
if(keystate[SDLK_DOWN]) {return DIR_DOWN;}
if(keystate[SDLK_LEFT]) {return DIR_LEFT;}
if(keystate[SDLK_RIGHT]) {return DIR_RIGHT;}
if(keystate[SDLK_UP]) {return DIR_UP;}
return DIR_NONE;
}


Driving input by control events is heavy on the simulation (movement must be processed for every message) and reliant on the rate at which messages are generated and etc. While the code above times the animation based on the framerate, it's in the correct format to be adjusted to a variable timestep if and when you decide to do so. The best practice is to break the system up such that all inputs (not just control input but network input and etc as well) are available, then captured at their current state and passed through a single logical frame.