Search code examples
javalibgdxoverlap2d

libgdx animated sprite no method error


Whenever I upload a sprite atlas for animation purposes into a scene in overlaps2d version 0.1.2-snapshot. My app crashes with the following line (slightly redacted):

java.lang.NoSuchMethodError: No virtual method getKeyFrame(F)Lcom/badlogic/gdx/graphics/g2d/TextureRegion; in class Lcom/badlogic/gdx/graphics/g2d/Animation; or its super classes (declaration of 'com.badlogic.gdx.graphics.g2d.Animation' appears in /data/data/xxx.xxx.xxx/files/instant-run/dex/slice-gdx-1.9.5_xxx-classes.dex)

And it points to this line in my code:

sceneLoader.getEngine().update(Gdx.graphics.getDeltaTime());

Could this be a issue with a version mismatch as overlap2d hasn't been updated in over a year, but libgdx was just updated about a month ago? The crash only happens with an animated image, otherwise app runs fine. I looked at the libgdx file the error refers to and this is what it looks like:

package com.badlogic.gdx.graphics.g2d;

import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.Array;

public class Animation<T> {

/** Defines possible playback modes for an {@link Animation}. */
public enum PlayMode {
    NORMAL,
    REVERSED,
    LOOP,
    LOOP_REVERSED,
    LOOP_PINGPONG,
    LOOP_RANDOM,
}

/** Length must not be modified without updating {@link #animationDuration}. See {@link #setKeyFrames(T[])}. */
T[] keyFrames;
private float frameDuration;
private float animationDuration;
private int lastFrameNumber;
private float lastStateTime;

private PlayMode playMode = PlayMode.NORMAL;

/** Constructor, storing the frame duration and key frames.
 * 
 * @param frameDuration the time between frames in seconds.
 * @param keyFrames the objects representing the frames. */
public Animation (float frameDuration, Array<? extends T> keyFrames) {
    this.frameDuration = frameDuration;
    T[] frames = (T[]) new Object[keyFrames.size];
    for (int i = 0, n = keyFrames.size; i < n; i++) {
        frames[i] = keyFrames.get(i);
    }
    setKeyFrames(frames);
}

/** Constructor, storing the frame duration and key frames.
 * 
 * @param frameDuration the time between frames in seconds.
 * @param keyFrames the objects representing the frames. */
public Animation (float frameDuration, Array<? extends T> keyFrames, PlayMode playMode) {
    this(frameDuration, keyFrames);
    setPlayMode(playMode);
}

/** Constructor, storing the frame duration and key frames.
 * 
 * @param frameDuration the time between frames in seconds.
 * @param keyFrames the objects representing the frames. */
public Animation (float frameDuration, T... keyFrames) {
    this.frameDuration = frameDuration;
    setKeyFrames(keyFrames);
}

/** Returns a frame based on the so called state time. This is the amount of seconds an object has spent in the
 * state this Animation instance represents, e.g. running, jumping and so on. The mode specifies whether the animation is
 * looping or not.
 * 
 * @param stateTime the time spent in the state represented by this animation.
 * @param looping whether the animation is looping or not.
 * @return the frame of animation for the given state time. */
public T getKeyFrame (float stateTime, boolean looping) {
    // we set the play mode by overriding the previous mode based on looping
    // parameter value
    PlayMode oldPlayMode = playMode;
    if (looping && (playMode == PlayMode.NORMAL || playMode == PlayMode.REVERSED)) {
        if (playMode == PlayMode.NORMAL)
            playMode = PlayMode.LOOP;
        else
            playMode = PlayMode.LOOP_REVERSED;
    } else if (!looping && !(playMode == PlayMode.NORMAL || playMode == PlayMode.REVERSED)) {
        if (playMode == PlayMode.LOOP_REVERSED)
            playMode = PlayMode.REVERSED;
        else
            playMode = PlayMode.LOOP;
    }

    T frame = getKeyFrame(stateTime);
    playMode = oldPlayMode;
    return frame;
}

/** Returns a frame based on the so called state time. This is the amount of seconds an object has spent in the
 * state this Animation instance represents, e.g. running, jumping and so on using the mode specified by
 * {@link #setPlayMode(PlayMode)} method.
 * 
 * @param stateTime
 * @return the frame of animation for the given state time. */
public T getKeyFrame (float stateTime) {
    int frameNumber = getKeyFrameIndex(stateTime);
    return keyFrames[frameNumber];
}

/** Returns the current frame number.
 * @param stateTime
 * @return current frame number */
public int getKeyFrameIndex (float stateTime) {
    if (keyFrames.length == 1) return 0;

    int frameNumber = (int)(stateTime / frameDuration);
    switch (playMode) {
    case NORMAL:
        frameNumber = Math.min(keyFrames.length - 1, frameNumber);
        break;
    case LOOP:
        frameNumber = frameNumber % keyFrames.length;
        break;
    case LOOP_PINGPONG:
        frameNumber = frameNumber % ((keyFrames.length * 2) - 2);
        if (frameNumber >= keyFrames.length) frameNumber = keyFrames.length - 2 - (frameNumber - keyFrames.length);
        break;
    case LOOP_RANDOM:
        int lastFrameNumber = (int) ((lastStateTime) / frameDuration);
        if (lastFrameNumber != frameNumber) {
            frameNumber = MathUtils.random(keyFrames.length - 1);
        } else {
            frameNumber = this.lastFrameNumber;
        }
        break;
    case REVERSED:
        frameNumber = Math.max(keyFrames.length - frameNumber - 1, 0);
        break;
    case LOOP_REVERSED:
        frameNumber = frameNumber % keyFrames.length;
        frameNumber = keyFrames.length - frameNumber - 1;
        break;
    }

    lastFrameNumber = frameNumber;
    lastStateTime = stateTime;

    return frameNumber;
}

/** Returns the keyframes[] array where all the frames of the animation are stored.
 * @return The keyframes[] field. */
public T[] getKeyFrames () {
    return keyFrames;
}

protected void setKeyFrames (T... keyFrames) {
    this.keyFrames = keyFrames;
    this.animationDuration = keyFrames.length * frameDuration;
}

/** Returns the animation play mode. */
public PlayMode getPlayMode () {
    return playMode;
}

/** Sets the animation play mode.
 * 
 * @param playMode The animation {@link PlayMode} to use. */
public void setPlayMode (PlayMode playMode) {
    this.playMode = playMode;
}

/** Whether the animation would be finished if played without looping (PlayMode#NORMAL), given the state time.
 * @param stateTime
 * @return whether the animation is finished. */
public boolean isAnimationFinished (float stateTime) {
    int frameNumber = (int)(stateTime / frameDuration);
    return keyFrames.length - 1 < frameNumber;
}

/** Sets duration a frame will be displayed.
 * @param frameDuration in seconds */
public void setFrameDuration (float frameDuration) {
    this.frameDuration = frameDuration;
    this.animationDuration = keyFrames.length * frameDuration;
}

/** @return the duration of a frame in seconds */
public float getFrameDuration () {
    return frameDuration;
}

/** @return the duration of the entire animation, number of frames times frame duration, in seconds */
public float getAnimationDuration () {
    return animationDuration;
}

From what i understand getting the key frame is what retrieves the frame out of the image atlas and changes it based on time to give the illusion of movement.


Solution

  • Use the previous version of libgdx, i.e., 1.9.4. In libgdx version 1.9.5 Animation class having some change, that is not updated with overlap2d snapshot version, so you are facing the problem.

    Downgrade version in build.gradle of root project. Hopefully, it may be helpful.

    Thanks.