3

In fear of reinventing the wheel, I wonder:

What is the best approach to implement a smooth grid based game character movement on a top-down Tiled (2D) map with libGDX?

The character should keep moving smoothly between tiles as long as an arrow key is pressed (or a touch event occures on certain direction of the character) and should finish confined to a grid position on key / touch release. The movement should be independent from the frame rate.

I would be glad about some already implemented examples that can be studied and lead to a proper libGDX API usage.

Jens Piegsa
  • 7,399
  • 5
  • 58
  • 106
  • I wrote an article about smooth tile-based movement, hope it helps https://paladin-t.github.io/articles/smooth-tile-based-movement-algorithm-with-sliding.html – pipipi Nov 18 '20 at 05:53

2 Answers2

6

Test if a specific button is pressed (or screen touched), if it is, set the correct target tile (the tile where the player will go) in a field and start moving there, this Movement will only end when the player is in the next tile. When that happens, check for input again to keep moving (i.e. repeat).

Lets suppose, your tile width/height is 1, and you want to move 1 tile per second. Your user pressed Right arrow key. Then you just set the targettile to the tile just right of the player.

if(targettile!=null){
    yourobject.position.x += 1*delta;
    if(yourobject.position.x>=targettile.position.x){
        yourobject.position.x = targettile.position.x;
        targettile = null;
    }
}

This code is simplified for right movement only, you need to make it for the other directions too.
Dont forget to poll the input again if the player is not moving.

Edit:

InputPolling for keys:

if (Gdx.input.isKeyPressed(Keys.DPAD_RIGHT)){

InputPolling for touchs (cam is your camera, and touchPoint is a Vector3 to store the unprojected touch coordinates, and moverightBounds a (libgdx) Rectangle):

if (Gdx.input.isTouched()){
    cam.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
    //check if the touch is on the moveright area, for example:
    if(moveRightBounds.contains(touchPoint.x, touchPoint.y)){
        // if its not moving already, set the right targettile here
    }
}

And noone already said it, you get the delta as a parameter in the render method, or you can get it anywhere else using:

Gdx.graphics.getDeltatime();

References:

Community
  • 1
  • 1
Daahrien
  • 10,190
  • 6
  • 39
  • 71
  • Thanks for your help. Can you please explain a bit more where to call that code from and how to do the input polling? Furthermore I am not sure about how to compute the `delta` according to a changing frame rate. – Jens Piegsa Jan 07 '14 at 07:39
  • 1
    @JensPiegsa You get the delta by LibGDX as a parameter in your render() methods. Otherwise you can get it via Gdx.graphics.getDeltaTime(). – noone Jan 07 '14 at 08:14
  • 1
    Edited my answer :) you call that in the render method (every frame) – Daahrien Jan 07 '14 at 08:54
  • Thanks again for the essential hints that actually led to a working solution. – Jens Piegsa Jan 08 '14 at 01:20
2

This is only for one dimension but I hope you get the idea, by checking the directional keys and adding/subtracting 1 and then casting it to an int (or flooring) will give you the next tile. If you release the key you still have an unreached target and the entity will keep moving until it reaches the target tile. I think it would look something like this:

void update()(
    // Getting the target tile
    if ( rightArrowPressed ) {
        targetX = (int)(currentX + 1); // Casting to an int to keep 
                                       // the target to next tile
    }else if ( leftArrowPressed ){
        targetX = (int)(currentX - 1);
    }

    // Updating the moving entity
    if ( currentX < targetX ){
        currentX += 0.1f;
    }else if ( currentX > targetX ){
        currentX -= 0.1f;
    }
}
Daahrien
  • 10,190
  • 6
  • 39
  • 71