Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LibGDX Flip 2D Sprite Animation

I have an animation that I want to play flipped if the user presses the 'A' key on their keyboard. The animation is facing right in the file and is played normally until I try to flip the texture.

This is what happens when I try to flip the texture when the player's direction has changed: Issue

As you can see, the animation plays fine in the beginning but when I change the player's direction it alternates between the flipped frame and the unflipped frame.

This is my player class:

package unit22.game;

import java.util.ArrayList;

import unit22.core.Utils;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class Player {

    private PlayerState state = PlayerState.STANDING;
    private Direction direction = Direction.RIGHT;
    private int x = 0, y = 0, width = 0, height = 0;
    private PlayerInputProcessor inputProcessor;
    private String name;

    private ArrayList<Action> actions;
    private Animation standingAnim;
    private Animation movingAnim;
    private float stateTime;
    private SpriteBatch spriteBatch;

    private Action currentAction;
    private Animation currentAnimation;

    public Player(String name, int x, int y, int width, int height) {
        this.name = name;
        setBounds(x, y, width, height);

        if(getX() > 400) setDirection(Direction.LEFT);

        this.actions = new ArrayList<Action>();

        standingAnim = new Animation(0.06f, Utils.loadTextureAtlas("standing", "textures/characters/animations/" + name + "/").getRegions());
        //movingAnim = new Animation(0.06f, Utils.loadTextureAtlas("moving", "textures/characters/animations/" + name + "/").getRegions());

        stateTime = 0f;
        spriteBatch = new SpriteBatch();
    }

    public void update() {
        stateTime += Gdx.graphics.getDeltaTime();
        switch(state) {
            case STANDING:
                if(currentAnimation != standingAnim)
                    currentAnimation = standingAnim;
                break;
            case MOVING:
                if(currentAnimation != movingAnim)
                    currentAnimation = movingAnim;
                break;
            case ACTING:
                Animation anim = new Animation(0.06f, Utils.loadTextureAtlas(currentAction.getName(), "textures/characters/animations/" + getName() + "/").getRegions());
                if(currentAnimation != anim)
                    currentAnimation = anim;
                break;
        }

    }

    public void render() {
        TextureRegion currentFrame = currentAnimation.getKeyFrame(stateTime, true);

        if(getDirection() == Direction.LEFT) {
            currentFrame.flip(true, false);
        }

        System.out.println("Direction: " + direction + ", Flipped: " + currentFrame.isFlipX());

        spriteBatch.begin();

        spriteBatch.draw(currentFrame, x, y, width, height);

        spriteBatch.end();
    }

    public ArrayList<Action> getActions() {
        return actions;
    }

    public void addAction(Action action) {
        this.actions.add(action);
    }

    public void setActions(ArrayList<Action> actions) {
        this.actions = actions;
    }

    public void setInputProcessor(PlayerInputProcessor inputProcessor) {
        this.inputProcessor = inputProcessor;
    }

    public PlayerInputProcessor getInputProcessor() {
        return inputProcessor;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public Direction getDirection() {
        return direction;
    }

    public PlayerState getState() {
        return state;
    }

    public void setDirection(Direction direction) {
        this.direction = direction;
    }

    public void setState(PlayerState state) {
        this.state = state;
    }

    public int[] getBounds() {
        return new int[] {x, y, width, height};
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public void setBounds(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

}

In my render method I am checking whether the player's direction is left and if it is then to flip the current animation frame.

This is how I am handling my input:

package unit22.screens;

import unit22.core.Game;
import unit22.core.Screen;
import unit22.game.Direction;
import unit22.game.Player;
import unit22.game.PlayerInputProcessor;

import com.badlogic.gdx.Gdx;

public class Playing extends Screen {

    Player player;

    public Playing(Game game, String name) {
        super(game, name);
    }

    @Override
    public void init() {


        player = new Player("misaka", 100, 100, 188, 380);

        player.setInputProcessor(new PlayerInputProcessor(player) {
            @Override
            public boolean keyTyped(char character) {
                if(getPlayer().getX() <= 14) getPlayer().setX(15);
                if(getPlayer().getX() + getPlayer().getWidth() >= 1024 - getPlayer().getWidth()) getPlayer().setX(1024 - getPlayer().getWidth() - 15);

                if(character == 'a' || character == 'A') {
                    getPlayer().setX((int)(getPlayer().getX() - (900 * Gdx.graphics.getDeltaTime())));
                    if(getPlayer().getDirection() == Direction.RIGHT) {
                        getPlayer().setDirection(Direction.LEFT);
                    }
                }

                if(character == 'd' || character == 'D') {
                    getPlayer().setX((int)(getPlayer().getX() + (900 * Gdx.graphics.getDeltaTime())));
                    if(getPlayer().getDirection() == Direction.LEFT) {
                        getPlayer().setDirection(Direction.RIGHT);
                    }
                }

                return super.keyTyped(character);
            }
        });

        getInputHandle().addProcessor(player.getInputProcessor());
    }

    @Override
    public void render(float delta) {
        super.render(delta);

        player.update();
        player.render();
    }

}

I've been trying to figure this problem out for a couple of hours now and haven't made any progress. Any idea why this would be happening?

like image 635
Liam Potter Avatar asked Jan 17 '15 14:01

Liam Potter


3 Answers

The problem is here:

if(getDirection() == Direction.LEFT) {
    currentFrame.flip(true, false);
}

The flip method permanently flips the original TextureRegion that the Animation class is referencing. So you either need to make sure you flip it back each time after flipping and drawing it, or do this, which I think is simpler:

Never flip it but instead use a negative width when drawing it with SpriteBatch, like this:

boolean flip = (getDirection() == Direction.LEFT);
spriteBatch.draw(currentFrame, flip ? x+width : x, y, flip ? -width : width, height);
like image 78
Tenfour04 Avatar answered Oct 08 '22 07:10

Tenfour04


If somebody is still wondering, negating scaleX or scaleY works like a charm and does not hurt performance (like TextureRegion.flip does for example).

Example:

draw(region, x, y, originX, originY, width, float height, 
     (flip ? -1 : 1) * scaleX, float scaleY, float rotation);
like image 6
Leo Hilbert Avatar answered Oct 08 '22 07:10

Leo Hilbert


I know that question is old but I think it is better solution

currentFrame = walkAnimation.getKeyFrame(stateTime, true); 

if(!currentFrame.isFlipX())
   currentFrame.flip(true, false);

  batch.draw(currentFrame,  0,0,0,0); 
like image 4
Adacho Avatar answered Oct 08 '22 05:10

Adacho