SDL Player movement & animation glitch

Started by
12 comments, last by Khatharr 11 years, 2 months ago

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 )

Advertisement

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

Your code:


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.

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

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)

i think the error is when i change the player_pos int

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.

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!"

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

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?

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

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.

This topic is closed to new replies.

Advertisement