DragonBones / DragonBonesJS

DragonBones TypeScript / JavaScript Runtime
MIT License
759 stars 320 forks source link

BUG: playing armature animation does not play child/nested armatures #28

Open bitbay opened 7 years ago

bitbay commented 7 years ago

So this one is weird... used to work "as expected" in dragonbones v2.4. I attach this simple animation arrow_nested.zip It contains:

Now, "outer_animation" once placed on screen it plays its nested animation, but when i call it's .play() method nothing happens. It seems that the first time it is played on construction (even without telling it to play) by a call from BaseFactory.buildArmature >> Armature.advanceTime >> Slot._update, and later on i can´t get it to play by playing the parent/main armature...

Any ideas how to solve this use case? Although it is a simplified example, i have some animations that make heavy use of this "feature"/"bug" with v2.4, but would LOVE to update to the latest dragonbones and PIXI.

akdcl commented 7 years ago

Hi, Armature will play child/nested armature animation. It seems not played in this case, because in the skeleton.json the "rotation"(animation) in "spinning_arrow"(armature) loop is 1, that means the animation only play once. If you want the animation infinite loop, you cound set "loop" to 0.

bitbay commented 7 years ago

Thanks for the quick response @akdcl! I do not want it to loop endlessly, i want it to play every time i call ".play()" on the animation.

I hope this "pseudo" code helps to explain the issue :

let anims = [
    { armature: "outer_spin", anim: "outer" },  // DOES NOT PLAY
    { anim: "outer" },  // DOES NOT PLAY
    { armature: "spinning_arrow", anim: "rotation" },  // PLAYS OK
    { anim: "rotation" },  // PLAYS OK
    { armature: "outer_spin", anim: "outer" },  // PLAYS OK (?!)
    { anim: "outer" }  // DOES NOT PLAY
]
let armature = null;

let nextItem = ()=>{
    if( anims.length ) {
        // get the next item...
        let current = anims.shift();

        // if "armature" is defined, switch any previously instantiated one...
        if( current.armature ) {
            if( armature ) {
                 pixiStage.removeChild( armature.display );
                 dragonBones.WorldClock.clock.remove( armature );
            }

            armature = dragonBones.PixiFactory.factory.buildArmature( current.armature );
            dragonBones.WorldClock.clock.add( armature );
            pixiStage.addChild( armature.display );
        }

        // play animation on the aramature...
        armature.animation.play( current.anim );

        setTimeout( nextItemBound, 2000 );
    } else {
        pixiStage.removeChild( armature.display );
        dragonBones.WorldClock.clock.remove( armature );
    }
};

let nextItemBound = nextItem.bind( this );
setInterval( nextItemBound, 2000 );

What i would expect is to see the animation playing 6 times, but it only plays 3 times.

bitbay commented 7 years ago

Just tried out the "DragonBonesDesignPanel" plugin of Flash, and i can confirm the same behavior.

  1. import the zip file to the Panel
  2. the "outer_spin" skeleton should be selected, and it doesn't play
  3. clicking the play button does not play the animation neither
  4. select the "spinning_arrow" skeleton, it starts to play immediately
  5. click as many times as You want on the play button, the animation plays each time
  6. select "outer_spin" again, this time it plays once
  7. clicking the play button does not play the animation again (although the icon changes to "pause")

I hope this helps in explaining/reproducing the bug.

akdcl commented 7 years ago

Hi, You could code like this:

outer_spin.getSlot("layer").childArmature.animation.play("rotation"); // Control child armature animation.

Armature animation is independent of. Like this:

hero.animation.play("run");
hero.getSlot("gun").childArmature.animation.play("fire");
hero.getSlot("hair").childArmature.animation.play("flowing");
bitbay commented 7 years ago

Thanks again @akdcl. As You can imagine this use case is oversimplified, not the real one i have to face. Like this the posted "workaround" of the bug probably is useful here, but not with a complex animation with multiple nested armatures - sometimes i don't even know the slots name, nor the animation it has inside... i just need to play the upmost armature and was hoping to be able to play ALL its children as well. Hence the description "playing armature animation does not play child/nested armatures".

Should i close the issue with "won't fix" or do You consider this as "not-a-bug", maybe hope for the team to look into it?

akdcl commented 7 years ago

Hi, You could play all child armature animation like this.

function playAllChildAnimation (armature: Armature)
{
    armature.getSlots().forEach((slot: Slot) => {
        let childArmature = slot.childArmature;
        if (childArmature) {
            childArmature.animation.play();
            playAllChildAnimation(childArmature);
        }
    });
}

armature.play("animationName");
playAllChildAnimation(armature);

I think the issue is not a bug, because api should be flexible enough to suit the needs :)

akdcl commented 7 years ago

Another solution, edit this file in DragonBones Pro: Edit the "outer_spin"(armature) -> "outer"(animation), add a keyframe on the "layer"(slot) timeline, enable the "play" attributes of the frame, and select an animation name to play. (Make "spinning_arrow" in the frame to play "rotation" animation).

bitbay commented 7 years ago

The first alternative (code) is exactly what i would expect from the base library - doing so from outside feels "hackish" (workaround a bug).

armature.play("animationName");
playAllAnimation (armature);

The second solution got my hopes up, but it just gives partial solution. Did what You proposed, edited and exported the data again, now i'm down to 2 fails out of 6.

let anims = [
    { armature: "outer_spin", anim: "outer" },  // PLAYS OK
    { anim: "outer" },  // DOES NOT PLAY
    { armature: "spinning_arrow", anim: "rotation" },  // PLAYS OK
    { anim: "rotation" },  // PLAYS OK
    { armature: "outer_spin", anim: "outer" },  // PLAYS OK
    { anim: "outer" }  // DOES NOT PLAY
]

Consecutive calls on armature.play() (without playAllAnimation) fail to play the nested armatures. Thanks for the suggestions, will keep looking for alternatives!

akdcl commented 7 years ago

For the second solution, i think it is a bug, it will be fixed :)