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)
 
     
    