create a texture from a spritesheet cocos2d - cocos2d-iphone

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];

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];

Adding multiple spritesheets in cocos2d

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.

Cocos2d show only a part of a CCSprite

Is there any possibility to show only a part of an CCSprite?
It seams that contentSize property doesn't have a good result.
I think you might have to create a new sprite for this. The general pseudo code is this.
CCTexture2D *origTexture = originalSprite->getTexture();
CGRect rect = {0, 0, 20, 20};
CCSprite *destSprite = CCSprite::spriteWithTexture(origTexture, CGRect);
Both doc_180's and James' answers work by creating new CCSprite using a portion of the texture, but if you are using clipping method, you will get CCSprite that uses the full texture but have the ability to only draw a portion of it on screen. One advantage of this method is you are able to modify how big or small the portion that you want shown or hidden on the fly rather than having to re-create the CCSprite again and again (or replacing the texture again and again).
So, to use the clipping method, simply download the ClippingNode class from here, and add the CCSprite you want clipped to that ClippingNode. Then you call one of its methods to specify which region to limit the drawing to. I'm currently using it to create a progress bar so I know for sure it works great.
Get the [sprite displayedFrame], change the frame of that, and create a new sprite with that spriteframe: CCSprite *sprite2 = [CCSprite spriteWithSpriteFrame:frame]

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.