Adding multiple spritesheets in cocos2d - cocos2d-iphone

I have the following code to set up my spritesheets and batch node:
CGSize screenSize = [[CCDirector sharedDirector] winSize];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"soldier-test.plist"];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"soldier-running.plist"];
batchNode = [CCSpriteBatchNode batchNodeWithFile:#"soldier-test.png"];
self.player = [Player spriteWithSpriteFrameName:#"shooting s0000.bmp"];
[batchNode addChild:self.player];
[player setPosition:ccp(screenSize.width/2, screenSize.height/2)];
[self addChild:batchNode];
However, when I try to have player (a subclass of CCSprite) perform an action using frames from the second spritesheet, I get assertion errors related to the texture files. Do I need to combine the sheets into one, or is there a way to span one CCSprite over multiple spritesheets?

A SpriteBatchNode can only have children that all use the same texture. Your player needs to use the texture soldier-test.png if you want to add it to your batchNode.
With a TextureAtlas you can put multiple different textures into one big image.

Related

Which APIs should we use to add spritesheets and sprites to a cocos2d game?

While creating a cocos2d iOS game, there are several options to add spritesheets - CCTextureCache::addImageAsync, CCSpriteFrameCache::addSpriteFramesWithFile, etc. - what is the difference between using these different ways to add a spritesheet?
Similarly, to load a sprite, we can call CCSprite::spriteWithSpriteFrameName or CCSprite::spriteWithFile or CCSpriteBatchNode::batchNodeWithTexture, etc. What is the difference between using these techniques?
Thanks
Anand
Load sprite frames, this also loads the textures:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"file.plist"];
Use sprite frames:
CCSprite* sprite = [CCSprite spriteWithSpriteFrameName:#"frame.png"];
Add the sprite to a batch node:
CCSpriteBatchNode* batchNode = [CCSpriteBatchNode batchNodeWithTexture:sprite.texture];
[batchNode addChild:sprite];
The batchNodeWithFile works too if you use the same image file of the sprite. If the sprite was initialized with a spriteframe, it'll be the texture atlas image (ie "file.png").
addImageAsync is only needed if you want to load your texture on another thread, usually to animate the loading screen. You'll still have to add the sprite frames afterwards.
CCSprite spriteWithFile creates a sprite from a single image file. Those can be batched too but it's better to use a texture atlas with sprite frames.

Cocos2d animations in main loop

Anyone that can give any hint of the smartest way to do a main loop animation? I don't want to use CCAnimation because I want to control the animations frame by frame.
Shall I store the sprite rect (relative to the sprite sheet) for each individual frame in an array, and then look up the suiting rect in each animation step? I tried to find out how this is done in CCAnimation, but I didn't succeed...
How to get the rect for each frame at initialization?
How to set the rect at each animation step?
Do I need to use CCSpriteBatchNode? I guess not, eh?
Cannot crealry understand, why you don't want to use CCAnimation, but anyway, to get answer for your questions you can check creation code of the CCSprite instance. Then, check creation of CCSpriteFrame instance. There you will find the answer for at least your first question.
Actually if you just want to manage animation frames differently from CCAnimate, you can just store array of CCSpriteFrames and show them as you want(in CCAnimate action these frames are just changed one by one in equal time intervals).
And if you do not want to show more than one frame of your animation, there is no difference will you use CCSpriteBatchNode or not. It saves a lot of processor time if you need to draw several parts of one texture, as it draws them in one draw call instead of send draw message to all of these sprites.
As you want animate sprite frame by frame I think using CCSpriteBatchNode would be a better option as it give you frame by frame access of animation.Making plist of sprites using any tool like "Zwoptex" will give an efficient way to animate using CCSpriteBatchNode.
Hope you know the animation using plist file with CCSpriteBatchNode.
I did the following with inspiration from Morions answer:
In the game tick function:
_animationFrames.legFrame = (_animationFrames.legFrame + 1) % _animationFrames.legFrames.count;
[_legs setDisplayFrame: [_animationFrames.legFrames objectAtIndex: _animationFrames.legFrame]];
And in the init function:
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode
batchNodeWithFile:#"Player.png"];
[self addChild:spriteSheet];
_animationFrames.legFrames = [[NSMutableArray array] retain];
for(int i = 0; i <= 15; ++i)
{
[_animationFrames.legFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"Player_legs-%d.png", i]]];
}
_legs = [CCSprite spriteWithSpriteFrameName:#"Player_legs-0.png"];
[_sprite addChild: spriteSheet];
[spriteSheet addChild:_legs z:1];

multiple CCSpritevFramevCache at one time

i am using the CCSpriteFrameCache a lot but cant understand somthing about it.
can i load many .plist to the cache at the start of my game ? or the CCSpriteFrameCache has ONLY one plist at a time ?
by now there is a sprite which is a child of a CCSpriteBatchNode ,that is been created many times during the game , with different images. so every time i create a new sprite i do this:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[NSString stringWithFormat:#"cand%i.plist",stage]];
candySheet = [CCSpriteBatchNode batchNodeWithFile:[NSString stringWithFormat:#"cand%i.png",stage]];
[self addChild:candySheet];
sprite1 = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:#"cand%i.png",1]];
is it ok ?
now lets say i have 2 spriteSheets, and 2 .plist, and i want to load both of them on my init , and add 2 sprites, each to be a child of one CCSpriteBatchNode and render images for each sprite from his own spriteSheet and Plist. but i get error then that :
CCSprite is not using the same texture id
so, i understand that each time i have to load to the cache the plist that i need at that specific time ????
thanks.
Someone asked a similar thing earlier.
You cannot make sprites children of the same CCSpriteBatchNode if they are not from the same spritesheet.
You need to create a new CCSpriteBatchNode for each spritesheet you use (by spritesheet I mean the combined image file and .plist file)
The CCSpriteFrameCache is a single cache shared across all your scenes and classes. When you call this method:
[CCSpriteFrameCache sharedSpriteFrameCache]
You are not making a new CCSpriteFrameCache object everytime, there is just ONE instance. You store all your loaded spritesheets in this single cache. So you could load 2 spritesheets into the cache like so:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"sheet1.plist"]; [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"sheet2.plist"];
You then need to create a CCSpriteBatchNode for EACH spritesheet, you cannot have more than one sheet in a batch node:
CCSpriteBatchNode *spriteSheet1 = [CCSpriteBatchNode batchNodeWithFile:#"sheet1.pvr.ccz"];
CCSpriteBatchNode *spriteSheet2 = [CCSpriteBatchNode batchNodeWithFile:#"sheet2.pvr.ccz"];
You can then add both of these batch nodes to a layer if you wish. Sprites added to batch nodes must be from the spritesheet that batch node is using.
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"some.plist"];
The above line takes quite a chunk of time even if the sprite frames are already loaded/cached. So make sure to avoid doing this unless you're absolutely certain that these sprite frames haven't been cached before, like at the beginning of the game or after purging the sprite frame cache.

Animate Sprite From Tile Map

I would like to animate a sprite at a specific location on an isometric tilemap. I can animate a sprite on a given layer, but not when its a sprite from a tilemap. For example the following works just fine:
// make a frame cache
CCSpriteFrameCache *frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
[frameCache addSpriteFramesWithFile:#"spellanim.plist" textureFile:#"spellanim.pvr.ccz"];
// create a sprite
CCSprite *effectSprite = [CCSprite spriteWithSpriteFrameName:#"spell_strength__33.png"];
// set sprite at center of screen
CGSize screenSize = [[CCDirector sharedDirector] winSize];
effectSprite.position = CGPointMake(screenSize.width / 2, screenSize.height / 2);
// create animation using an animation helper (since animationWithName:delay:frames: will be deprecated)
CCAnimation *animation = [CCAnimation animationWithFrame:#"spell_strength__" frameCount:13 delay:0.3f startAt:33];
CCAnimate *animate = [CCAnimate actionWithAnimation:animation];
// run animation on sprite
[effectSprite runAction:animate];
// add sprite as a child of the layer
[self addChild:effectSprite];
Now the following does not work, I assume it has to do with how tile maps work (I get an assertion failure in CCSprite setTexture:):
// add one to x to offset the spell animation from the player
CGPoint tileCoord = CGPointMake(player.entityTileCoordinate.x + 1, player.entityTileCoordinate.y);
// get the effects layer from the tile map
CCTMXTiledMap *tileMap = (CCTMXTiledMap *)[[TileMapLayer sharedTileMapLayer] getChildByTag:TileMapNode];
CCTMXLayer *effectsLayer = [tileMap layerNamed:#"Effects"];
// get a sprite from the effects layer
CCSprite *effectSprite = [effectsLayer tileAt:CGPointMake(0, 0)];
// move the sprite to the desired location (this works just fine)
CGPoint pointPixel = [effectsLayer positionAt:tileCoord];
[effectSprite runAction:[CCMoveTo actionWithDuration:0.0f position:pointPixel]];
// now animate the sprite
CCSpriteFrameCache *frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
[frameCache addSpriteFramesWithFile:#"spellanim.plist" textureFile:#"spellanim.pvr.ccz"];
CCAnimation *animation = [CCAnimation animationWithFrame:#"spell_strength__" frameCount:13 delay:0.3f startAt:33];
CCAnimate *animate = [CCAnimate actionWithAnimation:animation];
[effectSprite runAction:animate];
My guess it is because the animation sprites aren't a part of the tile set for that layer of the tile map. Is there a way to dynamically add these animation sprites to some cache used to draw on that layer (basically modify the tile set at runtime)? Can I later then remove these sprites from that modified tile set? Is there still a 1024x1024 limitation when you modify a tileset at runtime?
At the end of the day I really want to be able to have an animated sprite move from one tile to another on the tile map but I'm just not sure how to do that in the most efficient way. It seems really clunky to have an effects layer on the tile map and a tile set with all spell animations (especially if you can't fit them in 1024x1024) as assembling an animation would be chaining together tile GID updates as the effect moves across the tile map.
I know I can do what I want when the layer isn't a part of a tilemap - I can animate and move a sprite using screen coordinates, but when what I know are tile coordinates, translating those to screen coordinates (if the tile is even visible on the screen) has evaded my understanding so far. How do you determine what tiles the screen can actually 'see'? What then is the pixel coordinate on the screen of a visible tile?
I appreciate any thoughts on how to go about this process.
what you guess is the real cuase of the problem, tile maps are created using spritebatchnode. spritebatchnode is just like a layer with higher performance that you can only add sprites to it but there is a single restriction all the sprites in a spritebatchnode must share their textures. so when you are trying to change a tile to show an animation you are trying to draw something from diffrent texture alongside other tiles (which have default tile texture) and it causes crashes or malfunctioning. i didn't test it myself but i think if you try putting all your frames in a same texture you put other tiles, the problem would be solved.

create a texture from a spritesheet cocos2d

Hey all
Basically all i want is to create CCTexture2D objects from a spritesheet. I can make individual sprites from
charSpriteCur = [CCSprite spriteWithTexture:charSheet.texture rect:CGRectMake(136, 0, 136, 223)];
but i want to get individual textures from a spritesheet so that i can use
[mySprite setTexture:tex];
to change the sprite as required. I dont need it to be an animated sprite i just want to be able to change its texture when i want using a spritesheet.
any ideas with this or what is the best approach?
thanks
g
I don't think this is possible. When I need to do this I instead remove the CCSprite node (which I have setup as a property in my class) and then make a new one. Here is an example:
[self removeChild:[self mySprite] cleanup:YES];
[self setMySprite:[CCSprite spriteWithSpriteFrameName:#"image.png"]];
[mySprite setAnchorPoint:ccp(0,1)];
[mySprite setPosition:ccp(623,872)];
[self addChild:mySprite z:5];