Help with creating smooth 2D movement


#1

I have been attempting to implement some “smooth feeling” movement for a top-down action type game. The goal is to have the player slow down when changing direction and when stopping. The problem I have is the movement feels very inconsistent. Sometimes the “slow down” period feels very long and other times is feels just right, even though I have tried to code it to be consistent. Frankly it is so subtle it is hard to explain. I have compressed the project and uploaded it here:


If you could open it yourself and try moving the player around you can maybe see what I mean. Any feedback or advice is appreciated.
I believe all the relevant code should be in this section within the Player class:

  //move Player
  void movePlayer() {
    //selecting next Tile
    //if player is within a certain distance of the currentTilePos input is being given
    if (moving) {
      if (pos.dist(currentTilePos) < currentBoard.tileSize/2) {
        //pick possible next tile if there is certain input
        //also change directio variable
        int c = currentCol; //possible col
        int r = currentRow; //possible row
        switch(imd) {
        case 'w':
          c = currentCol;
          r = currentRow -1;
          md = imd;
          break;

        case 'd':
          c = currentCol +1;
          r = currentRow;
          md = imd;
          break;

        case 's':
          c = currentCol;
          r = currentRow +1;
          md = imd;
          break;

        case 'a':
          c = currentCol -1;
          r = currentRow;
          md = imd;
          break;
        }
        //if possible next tile is not out of bounds and not a wall make nextTile 
        if (c < currentBoard.cols && r < currentBoard.rows) {
          Boolean isWall = false;
          int i = 0;
          while (i < currentBoard.walls.size() && isWall == false) {
            Wall wall = currentBoard.walls.get(i);
            if (wall.col == c && wall.row == r) {
              isWall = true;
            }
            i++;
          }
          if (isWall == false) {
            currentCol = c;
            currentRow = r;
            currentTilePos = currentBoard.floor[c][r].pos.copy();
          }
        }
      }
    }

//detect if direction has just changed
    if (md != prevmd) {
      directionChange=true;
    }

//if changing direction
    if (directionChange) {
      //if magnitude of movement vector is less than 1
      if (moveVector.mag() < 1) {
        //direction change process is over, set speed to min for heading in new direction
        directionChange = false;
        speed = minSpeed;
      } else {
        //otherwise apply a slowdown to last frame's move vector and move player
        moveVector.mult(slowRate);
        pos.add(moveVector);
      }
    }

//if player is no longer changing direction
    if (!directionChange) {
      //if player is on the target tile (roughly lol)
      if (PVector.dist(pos, currentTilePos) < currentBoard.tileSize/2) {
        //apply slowing
        speed = speed*slowRate;
        //if speed is under the minimum
        if (speed < minSpeed) {
          //if player is very close to target speed may slow all the way down to zero once it is low enough
          //this prevents weird shaking on the spot h
          if (PVector.dist(pos, currentTilePos) < 2) {
            if (speed < 0.01) {
              speed = 0;
            }
          } else {
            //otherwise stop slowing at min speed until player gets closer to target
            speed = minSpeed;
          }
        }
        //if player has not reached the target tile
      } else {
        //apply acceleration
        speed = speed+accRate;
        //cap at top speed
        if (speed > topSpeed) {
          speed = topSpeed;
        }
      }

//target is center pixel of current tile
      PVector movementTarget = currentTilePos.copy();
      //produce vector from pos to target
      moveVector = PVector.sub(movementTarget, pos);
      //apply current speed
      moveVector.setMag(speed);
      //move player
      pos.add(moveVector);
      //record current input for detecting possible direction change next cycle
      prevmd = md;
    }
  }

However this might not make much sense outside the context of the full project so that is why I have linked it. Thanks for reading!

edit: after further investigation it seems the “moving” variable is false at times even when I am holding a key, causing the player to move to a tile and stop completely before moving to the next when direction is changing. Not very sure how to begin debugging something like this.


#2

The first thing you should try to fix is the method in which you move the player with keys. A well excepted way of doing this is using a boolean[] array in the player class and coding it like this:

void keyPressed() { 

  //player movement input
  //set direction to key pressed
  switch(key) {

    //WASD
  case 'w':
  case 'W':
    santa.moves[0]=true;
    break;

  case 'd':
  case 'D':
    santa.moves[1]=true;
    break;

  case 's':
  case 'S':
    santa.moves[2]=true;
    break;

  case'a':
  case'A':
    santa.moves[3]=true;
    break;
  }
}

All you have to do is add a velocity depending on which moves[] are true, and make sure to do the same in keyReleased()

It would also be a good idea to control all the other key functions this way


#3
  switch(keyCode) {

    //WASD
  case 'W':

#4

Also I’m a little confused by the idea of columns and rows in your player code. Do you only wish to move your player in the x and y axis, but not both at the same time?


#5

the player is on top of a board of floor tiles, when a movement key is pressed it will move towards the center of an adjacent tile. I think you are correct that the issue lies within my method of processing input.