0

I am at the end of the learning libgdx game development book, and the final chapter is about animating a sprite (bunny). When it should load the game screen an error happens, i really cant figuered out where I have made something wrong what i am sure that is with the animated bunny, I went step by step and here it is where the error happens:

reg = animation.getKeyFrame(stateTime, true);

If you could give me a hint i would be grateful.

BunnyHead.java

package com.packtub.libgdx.canyonbunny.game.objects;

import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.packtub.libgdx.canyonbunny.game.Assets;
import com.packtub.libgdx.canyonbunny.util.AudioManager;
import com.packtub.libgdx.canyonbunny.util.Constants;
import com.packtub.libgdx.canyonbunny.util.CharacterSkin;
import com.packtub.libgdx.canyonbunny.util.GamePreferences;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.math.MathUtils;

public class BunnyHead extends AbstractGameObject{
    public static final String TAG = BunnyHead.class.getName();

    private final float JUMP_TIME_MAX = 0.3f;
    private final float JUMP_TIME_MIN = 0.1f;
    private final float JUMP_TIME_OFFSET_FLYING = JUMP_TIME_MAX - 0.018f;
    public ParticleEffect dustParticle = new ParticleEffect();

    //Animacion
    private Animation animNormal;
    private Animation animCopterTransform;
    private Animation animCopterTransformBack;
    private Animation animCopterRotate;


    public enum VIEW_DIRECTION {LEFT,RIGHT}
    public enum JUMP_STATE{
        GROUNDED, FALLING, JUMP_RISING, JUMP_FALLING
    }

    private TextureRegion regHead;
    public VIEW_DIRECTION viewDirection;
    public float timeJumping;
    public JUMP_STATE jumpState;
    public boolean hasFeatherPowerup;
    public float timeLeftFeatherPowerup;

    public BunnyHead(){
        init();
    }

    private void init() {
        dimension.set(1, 1);
        //regHead = Assets.instance.bunny.head;
        animNormal = Assets.instance.bunny.animNormal;
        animCopterTransform = Assets.instance.bunny.animCopterTransform;
        animCopterTransformBack = Assets.instance.bunny.animCopterTransformBack;
        animCopterRotate = Assets.instance.bunny.animCopterRotate;


        //Centra la imagen en el objeto
        origin.set(dimension.x / 2, dimension.y / 2);
        //Pone bordes por colicciones
        bounds.set(0, 0, dimension.x, dimension.y);
        //Se establecen valores fisicos
        terminalVelocity.set(3.0f, 4.0f);
        friction.set(12.0f, 0.0f);
        acceleration.set(0.0f, -25.0f);
        //Ver direccion
        viewDirection = VIEW_DIRECTION.RIGHT;
        //Estado Salto
        jumpState = JUMP_STATE.FALLING;
        timeJumping = 0;
        //Power ups
        hasFeatherPowerup = false;
        timeLeftFeatherPowerup = 0;

        //Particula
        dustParticle.load(Gdx.files.internal("particles/dust.pfx"), Gdx.files.internal("particles"));

    }

    @Override
    public void update(float deltaTime){
        super.update(deltaTime);
        //Maneja la direccion de hacia donde se ve segun la velocidad
        if (velocity.x != 0){
            viewDirection = velocity.x < 0 ? VIEW_DIRECTION.LEFT:VIEW_DIRECTION.RIGHT;
        }
        if (timeLeftFeatherPowerup > 0){
            if (animation == animCopterTransformBack){
                //Reinicia la animacion "Transform" si se recogio otra pluma durante
                //la animacion "TransformBack". De otra manera se quedaria atascada
                //en esta animacion
                setAnimation(animCopterTransform);
            }
            timeLeftFeatherPowerup -= deltaTime;
            if (timeLeftFeatherPowerup < 0) {
                //Se desactiva el Power up
                timeLeftFeatherPowerup = 0;
                setFeatherPowerup(false);
                setAnimation(animCopterTransformBack);
            }
        }

        dustParticle.update(deltaTime);

        //Cambia la animacion acorde al poder de la pluma
        if (hasFeatherPowerup){
            if (animation == animNormal){
                setAnimation(animCopterTransform);
            }else if (animation == animCopterTransform){
                if (animation.isAnimationFinished(stateTime))
                    setAnimation(animCopterRotate);
            }
        }else{
            if (animation == animCopterRotate){
                if (animation.isAnimationFinished(stateTime))
                    setAnimation(animCopterTransformBack);
            }else if (animation == animCopterTransformBack){
                if (animation.isAnimationFinished(stateTime))
                    setAnimation(animNormal);
            }
        }
    }

    @Override
    public void render(SpriteBatch batch) {
        TextureRegion reg = null;

        //Dibujar particulas
        dustParticle.draw(batch);
        //Cambiar color skin
        batch.setColor(CharacterSkin.values()[GamePreferences.instance.charSkin].getColor());

        float dimCorrectionX = 0;
        float dimCorrectionY = 0;

        if (animation != animNormal){
            dimCorrectionX = 0.05f;
            dimCorrectionY = 0.2f;
        }


        //Dibuja imagen
        //reg = regHead;
        reg = animation.getKeyFrame(stateTime, true);

        batch.draw(reg.getTexture(),
                position.x, position.y,
                origin.x, origin.y,
                dimension.x + dimCorrectionX, dimension.y + dimCorrectionY,
                scale.x, scale.y,
                rotation,
                reg.getRegionX(), reg.getRegionY(),
                reg.getRegionWidth(), reg.getRegionHeight(),
                viewDirection == VIEW_DIRECTION.LEFT, false);

        //Reseta el color a blanco
        batch.setColor(1, 1, 1, 1);


    }

}

@Override
    public void update(float deltaTime){
        super.update(deltaTime);
        //Maneja la direccion de hacia donde se ve segun la velocidad
        if (velocity.x != 0){
            viewDirection = velocity.x < 0 ? VIEW_DIRECTION.LEFT:VIEW_DIRECTION.RIGHT;
        }
        if (timeLeftFeatherPowerup > 0){
            if (animation == animCopterTransformBack){
                //Reinicia la animacion "Transform" si se recogio otra pluma durante
                //la animacion "TransformBack". De otra manera se quedaria atascada
                //en esta animacion
                setAnimation(animCopterTransform);
            }
            timeLeftFeatherPowerup -= deltaTime;
            if (timeLeftFeatherPowerup < 0) {
                //Se desactiva el Power up
                timeLeftFeatherPowerup = 0;
                setFeatherPowerup(false);
                setAnimation(animCopterTransformBack);
            }
        }

        dustParticle.update(deltaTime);

        //Cambia la animacion acorde al poder de la pluma
        if (hasFeatherPowerup){
            if (animation == animNormal){
                setAnimation(animCopterTransform);
            }else if (animation == animCopterTransform){
                if (animation.isAnimationFinished(stateTime)){
                    setAnimation(animCopterRotate);
                }
            }
        }else{
            if (animation == animCopterRotate){
                if (animation.isAnimationFinished(stateTime)){
                    setAnimation(animCopterTransformBack);
                    }
            }else if (animation == animCopterTransformBack){
                if (animation.isAnimationFinished(stateTime))
                    setAnimation(animNormal);
            }
        }
    }

Assets.java

package com.packtub.libgdx.canyonbunny.game;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.assets.AssetErrorListener;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.utils.Disposable;
import com.packtub.libgdx.canyonbunny.util.Constants;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.utils.Array;

public class Assets implements Disposable, AssetErrorListener{
    public static final String TAG = Assets.class.getName();
    public static final Assets instance = new Assets();
    private AssetManager assetManager;
    public AssetFonts fonts;

    //singleton: previene de inicializaciones desde otras clases
    private Assets () {}

    public AssetBunny bunny;
    public AssetRock rock;
    public AssetGoldCoin goldCoin;
    public AssetFeather feather;
    public AssetLevelDecoration levelDecoration;
    public AssetSounds sounds;
    public AssetMusic music;


    public void init (AssetManager assetManager){
        this.assetManager = assetManager;
        //Se establece el asset manager que maneja los errores
        assetManager.setErrorListener(this);
        //Carga las texturas atlas
        assetManager.load(Constants.TEXTURE_ATLAS_OBJECTS, TextureAtlas.class);
        //Cargar sonido
        assetManager.load("sounds/jump.wav", Sound.class);
        assetManager.load("sounds/jump_with_feather.wav", Sound.class);
        assetManager.load("sounds/pickup_coin.wav", Sound.class);
        assetManager.load("sounds/pickup_feather.wav", Sound.class);
        assetManager.load("sounds/live_lost.wav", Sound.class);
        //Cargar musica
        assetManager.load("music/keith303_-_brand_new_highscore.mp3", Music.class);
        //Se inicia a cargar los assets y se espera que termine
        assetManager.finishLoading();
        Gdx.app.debug(TAG, "# de assets cargados: " 
        + assetManager.getAssetNames().size);
        for (String a : assetManager.getAssetNames()) Gdx.app.debug(TAG, "asset: " + a);

        TextureAtlas atlas = assetManager.get(Constants.TEXTURE_ATLAS_OBJECTS);

        //activa la opcion "texture filtering" para una dibujado mas suave de los pixeles
        for (Texture t : atlas.getTextures()) t.setFilter(TextureFilter.Linear, TextureFilter.Linear);

        //Crea los objetos del juego
        fonts = new AssetFonts();
        bunny = new AssetBunny(atlas);
        rock = new AssetRock(atlas);
        goldCoin = new AssetGoldCoin(atlas);
        feather = new AssetFeather(atlas);
        levelDecoration = new AssetLevelDecoration(atlas);
        sounds = new AssetSounds(assetManager);
        music = new AssetMusic(assetManager);
    }


    public class AssetBunny {
        public final AtlasRegion head;
        public final Animation animNormal;
        public final Animation animCopterTransform;
        public final Animation animCopterTransformBack;
        public final Animation animCopterRotate;

        public AssetBunny (TextureAtlas atlas){
            head = atlas.findRegion("bunny_head");

            Array<AtlasRegion> regions = null;
            AtlasRegion region = null;

            //Animacion: Conejo Normal
            regions = atlas.findRegions("anim_bunny_normal");
            animNormal = new Animation(1.0f / 10.0f, regions,
                    Animation.PlayMode.LOOP_PINGPONG);

            //Animacion: Conejo Helicoptero - Forma de orejas: nudo
            regions = atlas.findRegions("anim_bunny_copter");
            animCopterTransform = new Animation(1.0f / 10.f, regions);

            //Animacion: Conejo Helicoptero - Forma de orejas: sin nudo
            regions = atlas.findRegions("anim_bunny_copter");
            animCopterTransformBack = new Animation(1.0f / 10.0f, regions,
                    Animation.PlayMode.REVERSED);

            //Animacion: Conejo Helicoptero - Gira Orejas
            regions = new Array<AtlasRegion>();
            regions.add(atlas.findRegion("anim_bunny_copter", 4));
            regions.add(atlas.findRegion("anim_bunny_copter", 5));
            animCopterRotate = new Animation(1.0f/ 15.0f, regions);
        }
    }






}

AbstractGameObject.java

package com.packtub.libgdx.canyonbunny.game.objects;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.graphics.g2d.Animation;

public abstract class AbstractGameObject {
    public Vector2 position;
    public Vector2 dimension;
    public Vector2 origin;
    public Vector2 scale;
    public Vector2 velocity;
    public Vector2 terminalVelocity;
    public Vector2 friction;
    public Vector2 acceleration;
    public Rectangle bounds;
    public float rotation;
    public float stateTime;
    public Body body;
    public Animation animation;

    public AbstractGameObject(){
        position = new Vector2();
        dimension = new Vector2(1,1);
        origin = new Vector2();
        scale = new Vector2(1,1);
        rotation = 0;
        velocity = new Vector2();
        terminalVelocity = new Vector2(1,1);
        friction = new Vector2();
        acceleration = new Vector2();
        bounds = new Rectangle();
    }

    public void setAnimation(Animation animation){
        this.animation = animation;
        stateTime = 0;
    }

    protected void updateMotionX(float deltaTime){
        //Si es diferente de 0 esta en movimeinto
        if (velocity.x != 0){
            //Se aplica friccion
            if (velocity.x > 0){
                velocity.x = Math.max(velocity.x - friction.x * deltaTime, 0);
            }else{
                velocity.x = Math.min(velocity.x + friction.x * deltaTime, 0);
            }
        }
        //Aplica Acceleracion
        velocity.x += acceleration.x * deltaTime;
        //Asegura que la velocidad no exeda la velocidad positiva o negativa 
        //de la velocidad terminal
        velocity.x = MathUtils.clamp(velocity.x, -terminalVelocity.x, terminalVelocity.x);
    }

    protected void updateMotionY(float deltaTime){
        if (velocity.y != 0){
            //Se aplica friccion
            if (velocity.y > 0){
                velocity.y = Math.max(velocity.y - friction.y * deltaTime, 0);
            }else{
                velocity.y = Math.min(velocity.y + friction.y * deltaTime, 0);
            }
        }
        //Aplica Acceleracion
        velocity.y += acceleration.y * deltaTime;
        //Asegura que la velocidad no exeda la velocidad positiva o negativa 
        //de la velocidad terminal
        velocity.y = MathUtils.clamp(velocity.y, -terminalVelocity.y, terminalVelocity.y);
    }

    public void update(float deltaTime){
        //Se establece los parametros Box2D, mientras que no haya un cuerpo definido
        // en Box2D los gameobject van a utilizar nuestras fisicas simples.
        stateTime += deltaTime;
        if (body == null){
            updateMotionX(deltaTime);
            updateMotionY(deltaTime);
            //mover a una nueva posicion
            position.x += velocity.x * deltaTime;
            position.y += velocity.y * deltaTime;
        }else{
            position.set(body.getPosition());
            rotation = body.getAngle() * MathUtils.radiansToDegrees;
        }
    }

    public abstract void render (SpriteBatch  batch);

}

Exception in thread "LWJGL Application" java.lang.NullPointerException at com.packtub.libgdx.canyonbunny.game.objects.BunnyHead.render(BunnyHead.java:218) at com.packtub.libgdx.canyonbunny.game.Level.render(Level.java:182) at com.packtub.libgdx.canyonbunny.game.WorldRenderer.renderWorld(WorldRenderer.java:56) at com.packtub.libgdx.canyonbunny.game.WorldRenderer.render(WorldRenderer.java:193) at com.packtub.libgdx.canyonbunny.screens.GameScreen.render(GameScreen.java:49) at com.packtub.libgdx.canyonbunny.screens.DirectedGame.setScreen(DirectedGame.java:37) at com.packtub.libgdx.canyonbunny.screens.MenuScreen.onPlayClicked(MenuScreen.java:405) at com.packtub.libgdx.canyonbunny.screens.MenuScreen.access$3(MenuScreen.java:403) at com.packtub.libgdx.canyonbunny.screens.MenuScreen$4.changed(MenuScreen.java:301) at com.badlogic.gdx.scenes.scene2d.utils.ChangeListener.handle(ChangeListener.java:28) at com.badlogic.gdx.scenes.scene2d.Actor.notify(Actor.java:181) at com.badlogic.gdx.scenes.scene2d.Actor.fire(Actor.java:146) at com.badlogic.gdx.scenes.scene2d.ui.Button.setChecked(Button.java:116) at com.badlogic.gdx.scenes.scene2d.ui.Button$1.clicked(Button.java:90) at com.badlogic.gdx.scenes.scene2d.utils.ClickListener.touchUp(ClickListener.java:89) at com.badlogic.gdx.scenes.scene2d.InputListener.handle(InputListener.java:57) at com.badlogic.gdx.scenes.scene2d.Stage.touchUp(Stage.java:348) at com.badlogic.gdx.backends.lwjgl.LwjglInput.processEvents(LwjglInput.java:306) at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:200) at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:114)

  • 1
    possible duplicate of [What is a Null Pointer Exception, and how do I fix it?](http://stackoverflow.com/questions/218384/what-is-a-null-pointer-exception-and-how-do-i-fix-it) – tnw Feb 17 '15 at 20:50
  • I am sorry that post didnt help me very much. – user3042518 Feb 18 '15 at 00:28
  • Basically, when you get a NullPointerException, you can see in the error message exactly what line of code has a null object. Then you can quickly find the source of the bug. In your case, it is in line 218 of the BunnyHead class. – Tenfour04 Feb 18 '15 at 15:23

2 Answers2

1

SOLVED

In the method:

private void init() {
    dimension.set(1, 1);
    //regHead = Assets.instance.bunny.head;
    animNormal = Assets.instance.bunny.animNormal;
    animCopterTransform = Assets.instance.bunny.animCopterTransform;
    animCopterTransformBack = Assets.instance.bunny.animCopterTransformBack;
    animCopterRotate = Assets.instance.bunny.animCopterRotate;
    setAnimation(animNormal);
    
    
    //Centra la imagen en el objeto
    origin.set(dimension.x / 2, dimension.y / 2);
    //Pone bordes por colicciones
    bounds.set(0, 0, dimension.x, dimension.y);
    //Se establecen valores fisicos
    terminalVelocity.set(3.0f, 4.0f);
    friction.set(12.0f, 0.0f);
    acceleration.set(0.0f, -25.0f);
    //Ver direccion
    viewDirection = VIEW_DIRECTION.RIGHT;
    //Estado Salto
    jumpState = JUMP_STATE.FALLING;
    timeJumping = 0;
    //Power ups
    hasFeatherPowerup = false;
    timeLeftFeatherPowerup = 0;
    
    //Particula
    dustParticle.load(Gdx.files.internal("particles/dust.pfx"), Gdx.files.internal("particles"));
    
}

I was getting a nullpointerexception because i had failed to assign a start animation, I achived this by stating below animCopterRotate:

setAnimation(animNormal);

This way it worked just fine. Thanks you all for your help, sorry for my english that is not very good.

Community
  • 1
  • 1
0

I can't see that you declare the float variable stateTime. Well the obvious thing is if a NullPointerException occurs is that something is null or is not pointing at anything. So declare float stateTime = 0; . In your render method(before reg.getKeyFrame...) you need to put: stateTime += Gdx.graphics.getDeltaTime(); . So this is probably causing the NullPointerException. I'm giving you my Animator class which might help you:

public class Animator implements ApplicationListener{
protected int FRAME_COLUMNS;
protected int FRAME_ROWS;
protected float ANIMATION_SPEED;

protected float xPos;
protected float yPos;

protected float stateTime;


protected com.badlogic.gdx.graphics.g2d.Animation animation;
protected Texture spritesheet;
protected TextureRegion[] frames;
protected SpriteBatch painter;
protected TextureRegion currentFrame;
protected static GameScreen father;


public static float SPEED_FAST = 0.030f;
public static float SPEED_MEDIUM = 0.050f;
public static float SPEED_SLOW = 0.080f;
public static float SPEED_VERY_SLOW = 0.10f;

public Animator(Texture texture, int FRAME_COLUMNS, int FRAME_ROWS) {
    this.FRAME_COLUMNS = FRAME_COLUMNS;
    this.FRAME_ROWS = FRAME_ROWS;
    painter = new SpriteBatch();

    ANIMATION_SPEED = SPEED_SLOW;

    spritesheet = texture;

    TextureRegion[][] tmp = TextureRegion.split(spritesheet, spritesheet.getWidth() / FRAME_COLUMNS, spritesheet.getHeight() / FRAME_ROWS);

    frames = new TextureRegion[FRAME_COLUMNS * FRAME_ROWS];
    int tempIndex = 0;

     for (int i = 0; i < FRAME_ROWS; i++) {
            for (int j = 0; j < FRAME_COLUMNS; j++) {
                frames[tempIndex++] = tmp[i][j];
            }
        }
        animation = new Animation(ANIMATION_SPEED, frames); 
        stateTime = 0f; 


}



@Override
public void create() {
    // TODO Auto-generated method stub

}


@Override
public void resize(int width, int height) {
    // TODO Auto-generated method stub

}


@Override
public void render() {
        stateTime += Gdx.graphics.getDeltaTime();           
        currentFrame = animation.getKeyFrame(stateTime, true);
        painter.begin();
        painter.draw(currentFrame, xPos, yPos);            
        painter.end();

}


@Override
public void pause() {
    // TODO Auto-generated method stub

}


@Override
public void resume() {
    // TODO Auto-generated method stub

}


@Override
public void dispose() {
    // TODO Auto-generated method stub

}

If you will be using this class make sure to inherit from it(rather than instantiate it directly) and to properly initialize it by using super().

dHoja
  • 206
  • 3
  • 10
  • A float (lowercase) is not an object, so it cannot cause a NPE. – Tenfour04 Feb 17 '15 at 23:18
  • I inherit stateTime from a parent class, because of that i thought it wasnt necesarry to put that, i will edit the post and put that class also so you can see. Thanks you very much for your help nonetheless. – user3042518 Feb 18 '15 at 00:26
  • I tryed what you told me, but it keeps happening. – user3042518 Feb 18 '15 at 13:00
  • By doing step by step in this line: reg = animation.getKeyFrame(stateTime, true); it says that animation is null, where i have made a mistake? – user3042518 Feb 18 '15 at 13:19
  • @user3042518 - Did you instantiate the object by using: *animation = new Animation(ANIMATION_SPEED, frames);* – dHoja Feb 18 '15 at 15:08
  • I'm not saying that the *float* is throwing the NPE, i'm saying that the *getKeyFrame* might return a null value because the float is not defined @Tenfour04 – dHoja Feb 18 '15 at 15:11
  • @dHoja Gotcha, sorry I was reading this issue on my phone so it was hard to decipher the code. But I think since the trace doesn't mention the Animation class itself, the NPE is happening before it can call `getKeyFrame`. – Tenfour04 Feb 18 '15 at 15:19
  • I had previously set all the animations and Instantiate them in the Assets.java in the method AssetBunny, in the method init in CanyonBunny.java i called them, I set the starting animation as "animNormal" with this line: setAnimation(animNormal); this was the mistake i made, i hadnt done this before. Then in the update method of CanyonBunny.java i checked some conditions, depending of them the animation i would set. Was that what you where asking? – user3042518 Feb 18 '15 at 17:53