Search code examples
javakeylistenerkeyevent

Java 2D Game - KeyListener / KeyEvent class - issue with idle animations


I have the following code:

package myprojectgame.entities.creature;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import myprojectgame.Game;
import myprojectgame.Handler;
import myprojectgame.gfx.Animation;
import myprojectgame.gfx.Assets;
import myprojectgame.input.KeyManager;
import static myprojectgame.input.KeyManager.keys;



public abstract class Player extends Creature {
   // Animations ---> while moving
   private Animation down;
   private Animation up;
   private Animation left;
   private Animation right;

   // Animations --> idle
   private Animation down_idle;
   private Animation up_idle;
   private Animation left_idle;
   private Animation right_idle;

   // Last pressed boolean variable --> initialize it to down 
   public boolean lastPressed = handler.getKeyManager().down;


public Player(Handler handler,float x, float y) {
    super(handler,x, y,Creature.DEFAULT_CREATURE_WIDTH,Creature.DEFAULT_CREATURE_HEIGHT);
    bounds.x = 16;
    bounds.y = 14;
    bounds.width = 25;
    bounds.height = 43;

    // Animations --> while moving instantiation 
    down = new Animation(300,Assets.player_down);
    left = new Animation(300,Assets.player_left);
    right = new Animation(300,Assets.player_right);
    up = new Animation(300,Assets.player_up);

    // Animations --> while idle instantiation
    down_idle= new Animation(500,Assets.player_down_idle);
    right_idle= new Animation(500,Assets.player_right_idle);
    left_idle= new Animation(500,Assets.player_left_idle);
    up_idle= new Animation(500,Assets.player_up_idle);
}

@Override
public void tick() {
    down.tick();
    up.tick();
    right.tick();
    left.tick();
    down_idle.tick();
    up_idle.tick();
    right_idle.tick();
    left_idle.tick();
    getInput();
    move();
    handler.getCamera().centerOnEntity(this);
}

private void getInput() {
    xMove = 0;
    yMove = 0;

    if (handler.getKeyManager().up) {
        yMove = -speed;
        lastPressed = handler.getKeyManager().up;
    }
    if (handler.getKeyManager().down) {
        yMove = speed;
        lastPressed = handler.getKeyManager().down;
    }
    if (handler.getKeyManager().left) {
        xMove = -speed;
        lastPressed = handler.getKeyManager().left;
    }
    if (handler.getKeyManager().right) {
        xMove = speed;
        lastPressed = handler.getKeyManager().right;
    }
}

@Override
public void render(Graphics g) {
    g.drawImage(getCurrentAnimationFrame(),(int) (x - handler.getCamera().getxOffset()), (int) (y - handler.getCamera().getyOffset()),(width),(height), null);
}


private BufferedImage getCurrentAnimationFrame() {
    if (handler.getKeyManager().left && lastPressed == handler.getKeyManager().left) {
        return left.getCurrentFrame();
      } else if ( !(handler.getKeyManager().left)) {
        return left_idle.getCurrentFrame();
      }

    if (handler.getKeyManager().right && lastPressed == handler.getKeyManager().right) {
        return right.getCurrentFrame();
      } else if ( !(handler.getKeyManager().right) && lastPressed == handler.getKeyManager().right) {
        return right_idle.getCurrentFrame();
      }

     if (handler.getKeyManager().up && lastPressed == handler.getKeyManager().up) {
        return up.getCurrentFrame();
      } else if ( !(handler.getKeyManager().up) && lastPressed == handler.getKeyManager().up ) {
          return up_idle.getCurrentFrame();
      } 

     if (handler.getKeyManager().down && lastPressed == handler.getKeyManager().down) {
        return down.getCurrentFrame();
       } else if ( !(handler.getKeyManager().down) && lastPressed == 
          handler.getKeyManager().down ) {
            return down_idle.getCurrentFrame();
       } 

    return null;

    }
}

The problem is that I cannot get my getCurrentAnimationFrame() method to return the proper idle animations(or in this iteration of my code,any other animation besides left and left_idle).

My keys are defined in my KeyManager class like this:

    up = keys[KeyEvent.VK_W] || keys[KeyEvent.VK_UP];
    down = keys[KeyEvent.VK_S] || keys[KeyEvent.VK_DOWN];
    left = keys[KeyEvent.VK_A] || keys[KeyEvent.VK_LEFT];
    right = keys[KeyEvent.VK_D] || keys[KeyEvent.VK_RIGHT];

How can I properly implement Key events/strokes to return the right animations on key release/key press?


Solution

  • Your conditions and boolean logic are overcomplicated:

    private void getInput() {
      xMove = 0;
      yMove = 0;
    
      if (handler.getKeyManager().up) {
          yMove = -speed;
          lastPressed = handler.getKeyManager().up;
      }
      //...
    }
    
    private BufferedImage getCurrentAnimationFrame() {
      if (handler.getKeyManager().left && lastPressed == handler.getKeyManager().left) {
        return left.getCurrentFrame();
      } else if ( !(handler.getKeyManager().left)) {
        return left_idle.getCurrentFrame();
      }
      //...
      return null;
    }
    

    Is same as:

    private void getInput() {
        xMove = 0;
        yMove = 0;
    
        KeyManager km = handler.getKeyManager();
        if (km.up) {
            yMove = -speed;
            lastPressed = true;
        }
        //...
    }
    
    private BufferedImage getCurrentAnimationFrame() {
        KeyManager km = handler.getKeyManager();
        if (km.left && lastPressed) { 
        // replaced lastPressed == handler.getKeyManager().left since km.left must be true
            return left.getCurrentFrame();
        } else if (!km.left) {
            return left_idle.getCurrentFrame();
        }
        //...
        return null;
    }
    

    I do not see where are you setting lastPressed to false, looks to me it will be set to true (with first key press) and remains true. Since it is always true, your condition in getCurrentAnimationFrame is effectively:

    private BufferedImage getCurrentAnimationFrame() {
        KeyManager km = handler.getKeyManager();
        if (km.left) {
            return left.getCurrentFrame();
        } else {
            return left_idle.getCurrentFrame();
        }
        //UNREACHABLE CODE!
        return null;
    }
    

    Even if it doesn't remain true, your code is like if left, return left animation, if not left return left idle animation. I think your somehow mixed booleans with key codes as you say "lastPressed variable is supposed to store the value of the last pressed key". I would probably define direction enum:

    public enum DirectionEnum {
    LEFT, RIGHT, UP, DOWN;
    }
    

    And use it like this:

    DirectionEnum lastPressed = null;
    
    private void getInput() {
    xMove = 0;
    yMove = 0;
    KeyManager km = handler.getKeyManager();
    if (km.up) {
        yMove = -speed;
        lastPressed = DirectionEnum.UP;
    }
    //...
    }
    
    private BufferedImage getCurrentAnimationFrame() {    
        KeyManager km = handler.getKeyManager();
        if (lastPressed == null) {
            if (km.left) {
            return left.getCurrentFrame();
            }
            //...
        } else {
            switch (lastPressed) {
            case DOWN:
                if (!km.down){
                    return down_idle.getCurrentFrame();
                }
                break;
                //...
            default:
                throw new RuntimeException("Invalid direction " + lastPressed);
            }
        }
        return null;
    }
    

    But I do not know if it is correct, because lastPressed will be null only first time (or never), and then you will see only idle animations. So you should probably decide when to set lastPressed back to null?