Search code examples
java2dtile

Transition 2D player Position A to B on JAVA


Am having issues trying to figure out how to translate or 'animate' my player's position to a new tile, this is what am doing :

            if (input.right){
                x += 1;

Right now am listening for key.inputs and then x++/x-- or y++/y-- on my players position that makes him move pixel by pixel, but i want my player to move exactly to the next tile(32 pixels) with one hit of the key with like a linear transition from the player's tile position to the next tile over time?

Something like (pseudo code i think..)

            if input && walking false
               walking = true
               increment 1 by 1  32 ints to X over time?
               after completed walking = false

I still cant even figure out the logic behind something like that.

An example is the movement in a game called Tibia.

Now Some bits of my code (player related)..

GAMECLASS >

public Game()

    player = new Player(playerSpawn.x(), playerSpawn.y(), key);
    player.init(level);

public void run()
    ....
    render();
    frames++;
    ....

public void update()
    key.update();
    player.update();
    level.update();

public void render()
    .....
    int xScroll = ( player.x + 32) - screen.width / 2;
    int yScroll = ( player.y + 32) - screen.height / 2;
    level.render(xScroll, yScroll, screen);
    player.render(screen);

    for (int i =0; i < pixels.length; i++){
        pixels[i] = screen.pixels[i];
    }

SCREENCLASS >
    ......
    public int[] pixels;
    public int[] tiles = new int[VIEW_SIZE * VIEW_SIZE];
    .....
    public void renderTile(int xp, int yp, Tile tile){
      xp -= xOffset;
      yp -= yOffset;
     for (int y = 0; y < tile.sprite.SIZE; y++){
        int ya = y + yp;
        for (int x = 0; x < tile.sprite.SIZE; x++){
            int xa = x + xp;
            if (xa < -tile.sprite.SIZE || xa >= width || ya < 0 || ya >= height) break;
            if (xa < 0) xa = 0;
                pixels[xa + ya * width] = tile.sprite.pixels[x + y * tile.sprite.SIZE];
             }
         }

      }

    //THIS IS THE METHOD CALLED TO RENDER THE PLAYER > SEE BELLOW AT THE PLAYER CLASS FOR THE CALL

    public void renderMob(int xp, int yp, Mob mob){
       xp -= xOffset;
       yp -= yOffset;
      for (int y = 0; y < 64; y++){
        int ya = y + yp;
        int ys = y;
        for (int x = 0; x < 64; x++){
            int xa = x + xp;
            int xs = x;
            if (xa < -64 || xa >= width || ya < 0 || ya >= height) break;
            if (xa < 0) xa = 0;
            int col = mob.getSprite().pixels[xs + ys * 64];
            if (mob instanceof Chazer && col == 0xFF9b0000) col = 0xff54ff00;
            if (col != 0xFFFF00FF) pixels[xa + ya * width] = col;
          }
        }   

      }

    PLAYERCLASS >

        public Player(int x , int y, Keyboard input){
          this.x = x;
          this.y = y;
          this.input = input;
          }

        //PLAYER UPDATE
        public void update(){

        if (anim < 7500) anim++;
        else anim = 0;

        if (input.down)  ya = 1;
        if (input.up)    ya = -1;
        if (input.left)  xa = -1;
        if (input.right) xa = 1;

        //CHECK BELLOW TO THIS MOVE METHOD

        if (xa != 0){
            move(xa, 0);    
        } else if(ya != 0){
             move(0, ya);
          } 
        }


         clear();

       }

   //HERE ANIMATION AND CHOOSE WHAT SPRITE = WHERE PLAYER IS LOOKING AT

   public void render(Screen screen){

    if (dir == 0) {
        sprite = Sprite.player_n;
        if (walking) {
            if (anim % 20 > 10){
                sprite = sprite.player_n1;
            } else {
                sprite = sprite.player_n2;
            }
        }
    }
    if (dir == 1) {
        sprite = Sprite.player_e;
        if (walking) {
            if (anim % 20 > 10){
                sprite = sprite.player_e1;
            } else {
                sprite = sprite.player_e2;
            }
        }
    }
    if (dir == 2) {
        sprite = Sprite.player_s;
        if (walking) {
            if (anim % 20 > 10){
                sprite = sprite.player_s1;
            } else {
                sprite = sprite.player_s2;
            }
        }
    }
    if (dir == 3) {
        sprite = Sprite.player_w;
        if (walking) {
            if (anim % 20 > 10){
                sprite = sprite.player_w1;
            } else {
                sprite = sprite.player_w2;
            }
        }
    }

    // ADDING OFFSET CUZ THE PLAYER IS DOUBLE THE SIZE OF THE TILE

    int xx = x - 42;
    int yy = y - 42;

    screen.renderMob(xx, yy, sprite);

}


    //THIS IS HOW I MOVE THE PLAYER

    public void move(int xa, int ya){

    if (xa != 0 && ya != 0){
        move(xa, 0);
        move(0, ya);
        return;
    }

    if (xa > 0) dir = 1;
    if (xa < 0) dir = 3;
    if (ya > 0) dir = 2;
    if (ya < 0) dir = 0;

    if(!collision(xa, 0)){
            x += xa;
    }
    if(!collision(0, ya)){
            y += ya;
    }

}

Thanks alooot!

**Run method!

    public void run() {
    long xlastTime = System.nanoTime();
    long timer     = System.currentTimeMillis(); 
    final double xns = 1000000000.0 / 60.0;
    double delta = 0;
    int frames   = 0;
    requestFocus();
    while(running){
        long xnow = System.nanoTime();
        delta += (xnow-xlastTime) / xns;
        xlastTime = xnow;
        while (delta >= 1) {
            update();
            delta--;
        }
        render();
        frames++;

        if (System.currentTimeMillis() - timer > 1000){
            timer += 1000;
            frame.setTitle(title + " | " + frames + " fps");
            frames = 0;
        }
    }
    stop();

}

Solution

  • What I would do is declare two int fields in the player class:

    private float xToMove = 0;
    private float yToMove = 0;
    

    Then, under your input event:

    if (input.down && yToMove == 0)  
        yToMove = -32;
    if (input.up && yToMove == 0)    
        yToMove = 32;
    if (input.left && xToMove == 0) 
        xToMove = -32;
    if (input.right && xToMove == 0) 
        xToMove = 32;
    

    And finally, in your Player class's update method:

    public void update()
    {
        if (xToMove > 0)
        {
            xToMove--;
            x++;
        }
        if (xToMove < 0)
        {
            xToMove++;
            x--;
        }
        if (yToMove > 0)
        {
            yToMove--;
            y++;
        }
        if (yToMove < 0)
        {
            yToMove++;
            y--;
        }
    }
    

    Of course this is simplified a bit but the concept is there

    EDIT: to change the speed. Note that xToMove and yToMove have been changed to floats.

    You can use a float to represent the amount of time 1 move takes

    float period = 1000; //The time one move takes in milliseconds
    

    Somewhere you should calculate the number of pixels to be moved each frame. You could make a calculateSpeed() method or just throw it into the constructor or something. Depends on if you want speed to change during the game.

    float speed = 32f / (fps * (period / 1000f));  //fps should be obtained dynamically and should be a float
    

    Then when you update you should do this:

    if (xToMove > 0)
    {
        xToMove -= speed;
        x += speed;
        if (xToMove <= 0)
        {
            //Set these guys to nice even numbers to prevent problems
            xToMove = 0;
            x = (float) Math.round(x);
        }
    }
    

    Also make sure that x and y are floats.

    EDIT 2: fps

    int frames   = 0;
    int fps = 60;
    requestFocus();
    while(running){
        long xnow = System.nanoTime();
        delta += (xnow-xlastTime) / xns;
        xlastTime = xnow;
        while (delta >= 1) {
            update();
            delta--;
        }
        render();
        frames++;
    
        if (System.currentTimeMillis() - timer > 1000){
            timer += 1000;
            fps = frames;
            frame.setTitle(title + " | " + fps + " fps");
            frames = 0;
        }
    }
    stop();