yookunka

  • 3 янв 2017
  • Регистрация: 23 июн 2016
    Nate написал

    Or you could try updating to the dev branch, though it's still in a state of flux as we work through the massive issue #621 and a few other AnimationState issues. Almost done though!

    Awesome! I don't need this to be fixed right now, so I'll wait for the release. Thank you for your time :happy:

    I found the exact reason why it happened. Yeah, as you said that SimpleTest1 works fine even though I called addAnimation sequentially with a same animation, but if you change an animation during the end event of AnimationStateAdapter, it throws the exception.

    Here is a code snippet of modified version of SimpleTest1

    state.setAnimation(0, "run", false);
    state.addAnimation(0, "idle", true, 0);
    state.addAnimation(0, "idle", true, 0);  <
    
    ---
    
     This line, actually any animation can cause a problem
    
    state.addListener(new AnimationState.AnimationStateAdapter() {
       @Override
       public void end(int trackIndex) {
          if(!firstTime){
             firstTime = true;
             Gdx.app.log("end animation", "");
             state.setAnimation(0, "jump", true);  <
    
    ---
    
     if you change an animation here, NPE will be thrown
          }
       }
    });

    Nate написал

    Wait, you got an NPE but never posted the stacktrace?

    Sorry man, my bad :$

    Here is a stacktrace

    Exception in thread "LWJGL Application" java.lang.NullPointerException
       at com.esotericsoftware.spine.AnimationStateData$Key.hashCode(AnimationStateData.java:87)
       at com.badlogic.gdx.utils.ObjectFloatMap.get(ObjectFloatMap.java:279)
       at com.esotericsoftware.spine.AnimationStateData.getMix(AnimationStateData.java:72)
       at com.esotericsoftware.spine.AnimationState.setCurrent(AnimationState.java:191)
       at com.esotericsoftware.spine.AnimationState.update(AnimationState.java:74)
       at com.esotericsoftware.spine.SimpleTest1.render(SimpleTest1.java:96)
       at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:223)
       at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:124)
    Pharan написал

    @[удалено] hey, don't accuse yookunka of using Unity.

    hahaha. Yeah, I was like "what relates to Unity here?" when Nate asked you 😃

    Pharan написал

    That's this line throwing the null reference exception, right? spine-runtimes/AnimationStateData.java at master · EsotericSoftware/spine-runtimes · GitHub

    Yes, it is.

    Pharan написал

    Do you always call setAnimation/addAnimation with a string?

    Yes, I always set animations with a string.

    Pharan написал

    or do you sometimes use it with an animation reference?

    No, I don't.

    Pharan написал

    Are you somehow manipulating TrackEntry objects so their animations change or become null?

    No, I don't do that at all.

    Can you reproduce the same exception if you call addAnimation twice with a same animation like my code?


    So there is no advice for this problem? :wonder:

    Ok, I figured out why it happens :whew: I don't actually code like the following but it happens in some situations anyhow in my code.

    animationState.setAnimation(0, "attack", loop);
    animationState.addAnimation(0, "idle", loop, delay);
    animationState.addAnimation(0, "idle", loop, delay);  <== if I add the same animation sequentially, the NPE will be thrown

    If I code like this, setCurrent being called from update throws the NPE. So I should avoid this situation, right? Or could it be a bug? I can avoid this situation by tweaking my code 😉

    No, the value was "<none>" (with a small letter).

    Hi,

    I'm using Spine libgdx runtime and spine version is 3.4.02. I get a NullPointerException in setCurrent method in AnimationState from time to time. I can't really reproduce this exception all the time but sometimes it happens somehow. I'm wondering it's because my animation setting has a problem or there's a bug.

    When the exception is thrown, current.animation has an animation but entry is "<none>" and entry.animation is null. I don't know how this "<none>" value is set.

    private void setCurrent (int index, TrackEntry entry) {
    TrackEntry current = expandToIndex(index);
    if (current != null) {
       TrackEntry previous = current.previous;
       current.previous = null;
    
       if (current.listener != null) current.listener.end(index);
       for (int i = 0, n = listeners.size; i < n; i++)
          listeners.get(i).end(index);
    
       try {
          entry.mixDuration = data.getMix(current.animation, entry.animation);  <==== This line throws NullPointerException
       }
       catch (NullPointerException npe){
          Gdx.app.log("error", npe.toString());
       }
       if (entry.mixDuration > 0) {
          entry.mixTime = 0;

    Actually the direct cause of the exception is the a2 in the following code is null. Could you take a look at what's going on here? I can actually suppress the exception with try catch clause so it's not a big problem, though it would be nice to know why :$

    static class Key {
       Animation a1, a2;
    
       public int hashCode () {
          return 31 * (31 + a1.hashCode()) + a2.hashCode();  <=== this line throws the exception and a2 is null
       }
    
       public boolean equals (Object obj) {
          if (this == obj) return true;
          if (obj == null) return false;
          Key other = (Key)obj;
          if (a1 == null) {
             if (other.a1 != null) return false;
          } else if (!a1.equals(other.a1)) return false;
          if (a2 == null) {
             if (other.a2 != null) return false;
          } else if (!a2.equals(other.a2)) return false;
          return true;
       }
    }

    I'm using this "applyPrevious" from the following snippet.
    One Frame Attachment Animations
    Could it be the cause?

    • Изменено

    Thank you Nate, that solution worked very well. I'll use this code for the time being until you guys implement #12 in the future 😉

    • Изменено

    I'm using 3.3.07.

    Set a color to a path, save Spine, reopen the same file, the set color was cleared :o

    Yes, your picture is what I'm talking about. Thanks. Your hand drawing is great by the way 🙂

    a) Set the slot attachment to null AFTER the transition duration is done. This will cause the shield disappear a bit later.
    Or
    b) Place keys to null the slot on all the animations that could follow the "guard" animation. This will cause the shield to disappear immediately.

    Well, I thought I would need to implement either way before asking at first, but both ways are too cumbersome to implement so I wanted to know whether there is other solution or not. Especially b) way is too hard to maintain animations. What if I add a new attachment? I need to go through all the animations to turn off. It's not acceptable at all.

    a) is also hard because how can I know "the transition duration is done"? I need somehow to check the transition is done constantly whenever animation changed.

    Can't you just ignore previous attachment "on" when you apply for changing animation? Most likely you can take care of "on or off" in your current animation when you change to a new animation programmatically.


    @[удалено]
    Sorry, I didn't see what you wrote at the end. Seems like I bothered many people due to posting to the different topics, my bad 🙁 So, should I open a new topic?

    I guess your libgdx version isn't the latest. Update to the latest then it should be alright since I had the same error

    Thank you for taking your time, but no, you misunderstood my situation. I believe the issue BinaryCat and I talking about is the same. I made the explanation picture and hope you understand it now.

    The current default behavior of the Spine runtimes is that it does nothing you don't tell it to do

    I do understand this behavior, that's why I keep saying that the shield should be off because I tell it to do so before changing to a new animation! Otherwise I need to make the shield off at key 0 in the idle animation in the above explanation.

    And above all, this issue brought by updating to the latest runtime recently and reverting the code which I posted a few comments back works well.

    The attachment should remain off. Attachment keys outside of the mixing time SHOULD NOT have a say on if a slot is turned on or off.

    Yeah, I agree with BinaryCats as I have the same problem.

    @[удалено]

    Thank you for your detailed explanation. I already posted and described my situation at the following thread, but @BinaryCats pointed me this thread then I got confused after reading through all the comments 😃

    http://esotericsoftware.com/forum/Libgdx-Attachment-Bug-Fix-Proposal-6614

    Could you take a look at my comment in the above thread and give some advise please? :$

    We recently changed back to always setting the attachment rather than using lastTime. This allows 2 tracks to key attachment changes and the higher track wins since it is applied last. If we checked lastTime to trigger the attachment change then the most recent track to set the attachment wins, which doesn't make sense as the higher track wins for all other types of keys.

    I'm sorry I don't get your comment well. So do I need to turn an attachment off at a beginning of every animation if the animation doesn't need it? This isn't ideal at all because I need to modify all the animation if I add a new attachment. I want attachment's current state (on or off) to remain unless a higher track changes it when animation changes.

    • Изменено

    I still have the same problem even though I updated the latest runtime.

    My situation is that I have two animations, guard and idle. At the beginning of the guard animation, I make a shield image visible in Spine and when there is animation changing, I make the shield image invisible by programming by setting null to the shield image attachment with Spine runtime.

    Here is my code simplified version:

    // set the guard animation
    animationState.setAnimation(0, "guard", loop);
    
    // some time later...
    skeleton.findSlot("guard-shield").setAttachment(null);
    
    // then change to idle immediately
    animationState.setAnimation(0, "idle", loop);
    
    // then update the animation
    animationState.update(delta);
    animationState.apply(skeleton);  // this line makes "guard-shield" on, but I don't want it to be so...
    

    So, I reverted to the old code in the animation.java file which works fine.

    public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
    float[] frames = this.frames;
    if (time < frames[0]) {
       if (lastTime > time) apply(skeleton, lastTime, Integer.MAX_VALUE, null, 0);
       return;
    } else if (lastTime > time) //
       lastTime = -1;
    
    int frame = (time >= frames[frames.length - 1] ? frames.length : binarySearch(frames, time)) - 1;
    if (frames[frame] < lastTime) return;
    
    String attachmentName = attachmentNames[frame];
    skeleton.slots.get(slotIndex)
       .setAttachment(attachmentName == null ? null : skeleton.getAttachment(slotIndex, attachmentName));
    }

    For my project this works ok, but isn't it good?

    Same here. I ended up coming here after countless retry, but seems like there is no work around yet. hmm. :S