【问题标题】:Isuee with animated sprite动画精灵的问题
【发布时间】:2015-04-18 16:49:28
【问题描述】:

我在学习libgdx游戏开发书的最后,最后一章是关于动画精灵(兔子)。当它应该加载游戏屏幕时发生错误,我真的无法弄清楚我在哪里做错了什么我确定动画兔子是什么,我一步一步地走了,这就是错误发生的地方:

reg = animation.getKeyFrame(stateTime, true);

如果您能给我一个提示,我将不胜感激。

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);

}

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

【问题讨论】:

  • 很抱歉这篇文章对我没有太大帮助。
  • 基本上,当你得到一个 NullPointerException 时,你可以在错误信息中确切地看到哪一行代码有一个空对象。然后你可以快速找到bug的来源。在您的情况下,它位于 BunnyHead 类的第 218 行。

标签: java android animation libgdx


【解决方案1】:

已解决

在方法中:

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"));
    
}

我得到了一个空指针异常,因为我没有分配一个开始动画,我通过下面的 animCopterRotate 声明来实现这一点:

setAnimation(animNormal);

这种方式效果很好。谢谢大家的帮助,对不起我的英语不太好。

【讨论】:

    【解决方案2】:

    我看不到您声明了浮点变量 stateTime。那么显而易见的事情是,如果发生 NullPointerException 是某物为空或没有指向任何东西。所以声明 float stateTime = 0; 。在您的渲染方法中(在 reg.getKeyFrame... 之前),您需要输入:stateTime += Gdx.graphics.getDeltaTime(); .所以这可能会导致 NullPointerException。我正在为您提供我的 Animator 课程,这可能会对您有所帮助:

    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
    
    }
    

    如果您将使用这个类,请确保从它继承(而不是直接实例化它)并使用 super() 正确初始化它。

    【讨论】:

    • 浮点数(小写)不是对象,因此不会导致 NPE。
    • 我从父类继承 stateTime,因此我认为没有必要放置它,我将编辑帖子并放置该类,以便您查看。尽管如此,还是非常感谢您的帮助。
    • 我试过你告诉我的,但它一直在发生。
    • 通过在这一行一步一步做: reg = animation.getKeyFrame(stateTime, true);它说动画是空的,我在哪里犯了错误?
    • @user3042518 - 您是否使用以下方法实例化对象:animation = new Animation(ANIMATION_SPEED, frames);
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 2011-12-01
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多