Cocos2d-JS using mulitpacked sprite

Hi,
I wanted to ask on how to use a sprite packed into multiple sprite sheets (multipack in texturePacker) I tried this but it doesn’t work:

var bgCrumbleLayer = cc.Layer.extend({
    crumbleSheet_0:null,
    crumbleSheet_1:null,
    crumbleSheet_2:null,
    crumbleSheet_3:null,
    crumbleAction:null,
    crumbleSprite:null,

    ctor:function () {
        this._super();
        this.init();
    },

    init:function () {
        this._super();

        var winSize = cc.winSize;

        // create sprite sheet
        cc.spriteFrameCache.addSpriteFrames(res.crumble_0_plist);
        cc.spriteFrameCache.addSpriteFrames(res.crumble_1_plist);
        cc.spriteFrameCache.addSpriteFrames(res.crumble_2_plist);
        cc.spriteFrameCache.addSpriteFrames(res.crumble_3_plist);
        this.crumbleSheet_0 = new cc.SpriteBatchNode(res.crumble_0_png);
        this.crumbleSheet_1 = new cc.SpriteBatchNode(res.crumble_1_png);
        this.crumbleSheet_2 = new cc.SpriteBatchNode(res.crumble_2_png);
        this.crumbleSheet_3 = new cc.SpriteBatchNode(res.crumble_3_png);
        this.addChild(this.crumbleSheet_0);
        this.addChild(this.crumbleSheet_1);
        this.addChild(this.crumbleSheet_2);
        this.addChild(this.crumbleSheet_3);

        // init runningAction
        var crumbleFrames = [];
        for (var i = 0; i < 104; i++) {
            //var str = "crumble_" + (i < 10 ? ("0" + i) : i < 100 ? ("0" + i) : i) + ".png";
            if(i < 10){
                str = "crumble_00" + i + ".png";
            }else if(i < 100){
                str = "crumble_0" + i + ".png";
            }else {
                str = "crumble_" + i + ".png";
            }
            var frame = cc.spriteFrameCache.getSpriteFrame(str);
            crumbleFrames.push(frame);
        }

        var crumbleAnimation = new cc.Animation(crumbleFrames, 0.1);
        this.crumbleAction = new cc.repeat(new cc.Animate(crumbleAnimation), 1);
        this.crumbleSprite = new cc.Sprite("#crumble_000.jpg");
        this.crumbleSprite.attr({
            x: winSize.width/2,
            y: winSize.height/2
        });

        this.crumbleSheet_0.addChild(this.crumbleSprite);
        this.crumbleSheet_1.addChild(this.crumbleSprite);
        this.crumbleSheet_2.addChild(this.crumbleSprite);
        this.crumbleSheet_3.addChild(this.crumbleSprite);

        this.crumbleSprite.runAction(this.crumbleAction);
    }
});

Can you please advise?

I wouldn’t use a SpriteBatchNode in this case then. If you add all the sprite frames to the cache and use a regular Sprite added as a child to the scene it should use the correct texture from whichever sprite sheet the frame is associated. The engine will probably batch all sprites using the same texture in any given frame automatically.

Thank you for your reply, I really appreciate it. :smile:
I tried what you said but I couldn’t implement it so it works, can you please help with more details, or bits of code? thank you

Hmmm, I’d be surprised if the following didn’t work. Maybe you can’t use cc.Animate that has frames from different textures?

var bgCrumbleLayer = cc.Layer.extend({    
    crumbleAction:null,
    crumbleSprite:null,
    ctor:function () {
        this._super();
        this.init();
    },
    init:function () {
        this._super();
        var winSize = cc.winSize;
        // create sprite sheet
        cc.spriteFrameCache.addSpriteFrames(res.crumble_0_plist);
        cc.spriteFrameCache.addSpriteFrames(res.crumble_1_plist);
        cc.spriteFrameCache.addSpriteFrames(res.crumble_2_plist);
        cc.spriteFrameCache.addSpriteFrames(res.crumble_3_plist);
        // init runningAction
        var crumbleFrames = [];
        for (var i = 0; i < 104; i++) {
            //var str = "crumble_" + (i < 10 ? ("0" + i) : i < 100 ? ("0" + i) : i) + ".png";
            if(i < 10){
                str = "crumble_00" + i + ".png";
            }else if(i < 100){
                str = "crumble_0" + i + ".png";
            }else {
                str = "crumble_" + i + ".png";
            }
            var frame = cc.spriteFrameCache.getSpriteFrame(str);
            crumbleFrames.push(frame);
        }
        var crumbleAnimation = new cc.Animation(crumbleFrames, 0.1);
        this.crumbleAction = new cc.repeat(new cc.Animate(crumbleAnimation), 1);
        this.crumbleSprite = new cc.Sprite("#crumble_000.jpg");
        this.crumbleSprite.attr({
            x: winSize.width/2,
            y: winSize.height/2
        });
        this.addChild(this.crumbleSprite);
        this.addChild(this.crumbleSprite);
        this.addChild(this.crumbleSprite);
        this.addChild(this.crumbleSprite);
        this.crumbleSprite.runAction(this.crumbleAction);
    }
});

I removed the four instances of:

this.addChild(this.crumbleSprite);

because it throws back “child already added” or something similar.
after removing it, there is no error but the animation doesn’t run…
It is a bit confusing :frowning:

Oh, yeah, sorry I didn’t catch that when I replied. You can only add a node (or sprite) as a child to one parent node. In this case I think what you wanted was to just call this.addChild(this.crumbleSprite); once, instead of 4 times.

Yes I did that, kept only one instance of the add child, but no animation. :confused:
Thank you for all your help btw :slight_smile:

I’m not 100% familiar with differences from c++ version. Maybe the sprite frames aren’t getting added to the cache or aren’t valid from getSpriteFrame? The animation is long, so you shouldn’t miss it, but might as well loop forever during testing. If you see it, but not animating then new cc.Sprite is obviously working correctly. Debug step through may help.

/// Confirm you're getting valid sprite frames
if(frame != null) 
  crumbleFrames.push(frame);
else
  console.log("FRAME " + str + " IS NULL");
/// ...
console.log(crumbleFrames.length);
/// Loop the animation just to make sure it's not running quickly once.
this.crumbleAction = new cc.repeatForever(new cc.Animate(crumbleAnimation));

Edit: also it’s always smart to try another test, unless you have some already. Try making a 2-frame animation using individual images to confirm animation is working. Try scheduling an update method and try to change the frame manually using SpriteFrameCache to confirm the plist packed texture frames are getting loaded, added, and used correctly.

The result of the log shows that all the frames are nulls, which explains why the animation doesn’t work. I have animation that work, and when using each plist individually they work fine. Making them work in sequence is quite challenging…

I tried to to initialize every plist separately, but I couldn’t run the sequence, and also, this method results on a quite messy code:

var bgCrumbleLayer = cc.Layer.extend({
    crumbleSheet_0:null,
    crumbleSheet_1:null,
    crumbleSheet_2:null,
    crumbleSheet_3:null,

    crumbleAction_0:null,
    crumbleAction_1:null,
    crumbleAction_2:null,
    crumbleAction_3:null,

    crumbleSprite_0:null,
    crumbleSprite_1:null,
    crumbleSprite_2:null,
    crumbleSprite_3:null,

    ctor:function () {
        this._super();
        this.init();
    },

    init:function () {
        this._super();

        var winSize = cc.winSize;

        cc.spriteFrameCache.addSpriteFrames(res.crumble_0_plist);
        this.crumbleSheet_0 = new cc.SpriteBatchNode(res.crumble_0_png);
        this.addChild(this.crumbleSheet_0);


        // init crumble Action
        var crumbleFrames0 = [];
        for (var i = 0; i < 28; i++) {
            var str = "crumble_0" + (i < 10 ? ("0" + i) : i) + ".jpg";
            var frame = cc.spriteFrameCache.getSpriteFrame(str);
            if(frame != null)
                crumbleFrames0.push(frame);
            else
                cc.log("FRAME " + str + " IS NULL");
        }

        cc.log(crumbleFrames0.length);

        var crumbleAnimation0 = new cc.Animation(crumbleFrames0, 0.1);
        this.crumbleAction_0 = new cc.repeat(new cc.Animate(crumbleAnimation0), 10);
        this.crumbleSprite_0 = new cc.Sprite("#crumble_000.jpg");
        this.crumbleSprite_0.attr({
            x: winSize.width/2,
            y: winSize.height/2,
            anchorX: 0.5,
            anchorY: 0.5,
            scale: 1.02
        });

        this.crumbleSheet_0.addChild(this.crumbleSprite_0);

        cc.spriteFrameCache.addSpriteFrames(res.crumble_1_plist);
        this.crumbleSheet_1 = new cc.SpriteBatchNode(res.crumble_1_png);
        this.addChild(this.crumbleSheet_1);


        // init crumble Action
        var crumbleFrames1 = [];
        for (var i = 28; i < 56; i++) {
            var str = "crumble_0" + (i < 10 ? ("0" + i) : i) + ".jpg";
            var frame = cc.spriteFrameCache.getSpriteFrame(str);
            if(frame != null)
                crumbleFrames1.push(frame);
            else
                cc.log("FRAME " + str + " IS NULL");
        }

        cc.log(crumbleFrames1.length);

        var crumbleAnimation1 = new cc.Animation(crumbleFrames1, 0.1);
        this.crumbleAction_1 = new cc.repeat(new cc.Animate(crumbleAnimation1), 10);
        this.crumbleSprite_1 = new cc.Sprite("#crumble_028.jpg");
        this.crumbleSprite_1.attr({
            x: winSize.width/2,
            y: winSize.height/2,
            anchorX: 0.5,
            anchorY: 0.5,
            scale: 1.02
        });

        this.crumbleSheet_1.addChild(this.crumbleSprite_1);

        cc.spriteFrameCache.addSpriteFrames(res.crumble_2_plist);
        this.crumbleSheet_2 = new cc.SpriteBatchNode(res.crumble_2_png);
        this.addChild(this.crumbleSheet_2);


        // init crumble Action
        var crumbleFrames2 = [];
        for (var i = 56; i < 84; i++) {
            var str = "crumble_0" + (i < 10 ? ("0" + i) : i) + ".jpg";
            var frame = cc.spriteFrameCache.getSpriteFrame(str);
            if(frame != null)
                crumbleFrames2.push(frame);
            else
                cc.log("FRAME " + str + " IS NULL");
        }

        cc.log(crumbleFrames2.length);

        var crumbleAnimation2 = new cc.Animation(crumbleFrames2, 0.1);
        this.crumbleAction_2 = new cc.repeat(new cc.Animate(crumbleAnimation2), 10);
        this.crumbleSprite_2 = new cc.Sprite("#crumble_056.jpg");
        this.crumbleSprite_2.attr({
            x: winSize.width/2,
            y: winSize.height/2,
            anchorX: 0.5,
            anchorY: 0.5,
            scale: 1.02
        });

        this.crumbleSheet_2.addChild(this.crumbleSprite_2);

        cc.spriteFrameCache.addSpriteFrames(res.crumble_3_plist);
        this.crumbleSheet_3 = new cc.SpriteBatchNode(res.crumble_3_png);
        this.addChild(this.crumbleSheet_3);


        // init crumble Action
        var crumbleFrames3 = [];
        for (var i = 84; i < 104; i++) {
            var str = "crumble_" + (i < 100 ? ("0" + i) : i) + ".jpg";
            var frame = cc.spriteFrameCache.getSpriteFrame(str);
            if(frame != null)
                crumbleFrames3.push(frame);
            else
                cc.log("FRAME " + str + " IS NULL");
        }

        cc.log(crumbleFrames3.length);

        var crumbleAnimation3 = new cc.Animation(crumbleFrames3, 0.1);
        this.crumbleAction_3 = new cc.repeat(new cc.Animate(crumbleAnimation3), 10);
        this.crumbleSprite_3 = new cc.Sprite("#crumble_084.jpg");
        this.crumbleSprite_3.attr({
            x: winSize.width/2,
            y: winSize.height/2,
            anchorX: 0.5,
            anchorY: 0.5,
            scale: 1.02
        });

        this.crumbleSheet_3.addChild(this.crumbleSprite_3);

        var stitchedAnimation = new cc.sequence(this.crumbleAction_0, this.crumbleAction_1, this.crumbleAction_2, this.crumbleAction_3);


        this.crumbleSprite_0.runAction(stitchedAnimation);
    }
});

Null frames mean that something isn’t loaded correctly. Maybe wrong file paths or URL paths. Maybe wrong frame names within each of the textures.

You also cannot run all 4 animation actions on a sprite if it exists inside a spriteBatchNode. Add the sprites as children of the layer, don’t create the sprite batch nodes.

// verify this is working (does plist reference texture PNG correctly)
cc.spriteFrameCache.addSpriteFrames(res.crumble_0_plist);
// alternatively specify texture file
// cc.spriteFrameCache.addSpriteFrames(res.crumble_0_plist, res.crumble_0_png);
// if this returns null, either the name is wrong or previous line failed
cc.spriteFrameCache.getSpriteFrame("crumble_000.png");

Well, oddly enough, every plist with its png work fine individually, but when trying to stitch them they throw back nulls :confused:

Is there a way to just run an animation without packing the images before hand?

It’s not supported as well as it is for packed textures. But I believe you can do one of the following to get sprite frames that you can add to an animation.

// create sprite frame from file (assuming 64x42 px image)
var frame = new cc.SpriteFrame("crumble_000.jpg", cc.rect(0,0,64,64)); 
// create sprite frame from sprite
var s = new cc.Sprite("crumble_000.jpg");
var frame = s.getSpriteFrame();