Animação de Sprites

Bom dia, ou boa noite, gente. Possuo uma classe chamada Player, e nela, tenho um objeto do tipo Sprite, e um objeto do tipo BufferedImage, assim:

public class Player extends GameObject implements KeyListener{
    private BufferedImage currentSprite;
    private Sprite sprites;

Bem, dentro dessa classe Sprite, eu tenho isso:

public class Sprite {
    private String className;
    private BufferedImage sprites[];
    private ImageIcon face;
    private static final ResourceBundle CLASSES_SPRITE = ResourceBundle.getBundle(Constants.CLASSES_FILE_PATH);
    private static final String SPRITES_PATH = "/client/datafiles/graphics/characters/";
    private static final String FACES_PATH = "/client/datafiles/graphics/characters/faces/";

    /**
     * Initializes the sprite.
     * @param className Name of the choosed class.
     */
    public Sprite(String className){
        BufferedImage spriteSheet;
    
        this.className = className.toLowerCase();
        int spriteWidth = Integer.parseInt(CLASSES_SPRITE.getString(this.className + ".width"));
        int spriteHeight = Integer.parseInt(CLASSES_SPRITE.getString(this.className + ".height"));
        String spriteName = CLASSES_SPRITE.getString(this.className + ".sprite");
    
        try {
            String spriteURL = SPRITES_PATH.concat(spriteName);
            spriteSheet = ImageIO.read(getClass().getResource(spriteURL));
            this.sprites = SpriteSheetDivider.getSpriteDivided(spriteSheet, spriteWidth, spriteHeight);
            spriteURL = FACES_PATH.concat(spriteName);
            face = new ImageIcon(ImageIO.read(getClass().getResource(spriteURL)));
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Returns the selected sprite.
     * @param spriteIndex the sprite index to be returned.
     * @return a BufferedImage containing the selected sprite.
     */
    public BufferedImage getSprite(int spriteIndex) {
        return sprites[spriteIndex];
    }

Basicamente, a classe sprite pega uma SpriteSheet e a divide em 16 sprites, considerando que há esse número de sprites na SpriteSheet. O método getSprite() retorna uma sprite das 16. Voltando para a classe Player, possuo listeners que movem o personagemd, e métodos que deveriam animar o mesmo, assim:

@Override
public void keyPressed(KeyEvent e) {
    int cod = e.getKeyCode();
    if (cod == KeyEvent.VK_W) {
        animateUpSprite();
        up = true;
        down = false;
        right = false;
        left = false;
        vY = -SPEED;
    } 
    if (cod == KeyEvent.VK_A) {
        animateLeftSprite();
        up = false;
        down = false;
        right = false;
        left = true;
        vX = -SPEED;
    } 
    if (cod == KeyEvent.VK_S) {
        animateDownSprite();
        up = false;
        down = true;
        right = false;
        left = false;
        vY = SPEED;
    } 
    if (cod == KeyEvent.VK_D) {
        animateRightSprite();
        up = false;
        down = false;
        right = true;
        left = false;
        vX = SPEED;
    }
}

@Override
public void keyReleased(KeyEvent e) {
    int cod = e.getKeyCode();
    if (cod == KeyEvent.VK_W) {
        desanimate();
        vY = 0;
    } 
    if (cod == KeyEvent.VK_A) {
        desanimate();
        vX = 0;
    } 
    if (cod == KeyEvent.VK_S) {
        desanimate();
        vY = 0;
    } 
    if (cod == KeyEvent.VK_D) {
        desanimate();
        vX = 0;
    }
}

Os métodos animate****Sprite() nada fazem além de mudar a variável currentSprite com o método getSprite(), e o desanimate fazem a mesma coisa.

O Player é inserido dentro de um GameLoop, que o atualiza e o renderiza 60 vezes por segundo.

Bem, agora que o código foi explicado, a minha primeira dúvida é o seguinte:
Como eu animo o meu personagem com esses recursos?

Eu já tentei um bocado de coisas, até multithreading para animá-lo, mas nada deu certo até agora.

Provavelmente a classe do seu personagem deve ter um método que é invocado pelo GameLoop para atualizar o estado do personagem, geralmente esse tipo de método recebe o tempo decorrido como parâmetro.
Basicamente você tem que verificar se seu personagem está num estado de movimento e aí escolher o frame certo a ser renderizado.
Não vejo necessidade de multithreading pois o GameLoop é quem deve “sincronizar” o estado de todos os elementos do jogo.

1 curtida

Opa staroski, obrigado por responder! Como eu escolho esse frame certo para mudar o estado do personagem?

Não sei te responder, não sou eu quem está implementando o jogo, não sei como ele funciona.

Quais os critérios que você tem para decidir o frame a ser usado?

Sei lá, se seu personagem se move numa determinada velocidade e você tem 3 frames de movimento, você pode calcular quanto tempo cada frame precisa ser exibido e com base no tempo decorrido ir trocando quando necessário.