Updating a CCTexture2D from a BatchNode - cocos2d-iphone

After I completed my testing, I have moved my images over to a spritesheet.
I loaded up the batchnode with the appropriate files and my images load just fine.
But I am running into an issue of swapping the textures out. When the images were individual files, there was no problem. Now it seems the CCTexture2d doesn’t like my sprite sheet.
I have the objects stored in a multidimensional array, so I can run through them quickly and update their image.
Here is what I did when it worked:
CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:#"alt-image.png"];
[((MyFunObject*)[[myFunObject2DArr objectAtIndex:j]objectAtIndex:i])->img setTexture: tex];
Here is what I am doing now:
CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:[CCSprite spriteWithSpriteFrameName:#"alt-image.png"]];
[((MyFunObject*)[[myFunObject2DArr objectAtIndex:j]objectAtIndex:i])->img setTexture: tex];
MyFunObject is a subclass of CCSprite and has an CCSprite img property that get set. I run through the array and find like objects and replace their image with a new image “alt-image.png”.
Seems simple, but outside of a sprite sheet this worked flawlessly.
Can someone tell me what I am doing wrong?

UPDATE:
HERE IS THE SOLUTION FOR ANYONE IN THE FUTURE WHO HAS THIS ISSUE
You can't change the sprites 'texture' when the texture in question is spritesheet. (Slapped my head on that one)
You can only change the rectangle that is viewed.
Here is how you do it:
[((MyFunObject*)[[myFunObject2DArr objectAtIndex:j]objectAtIndex:i])->img setTextureRect:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: #"alt-image.png" ].rect];
Notice I changed setTexture to setTextureRect, then I called the SpriteFrame's rectangle as an argument.
Enjoy.

Related

Strange artefacts appear on CCSprite

I have a CCLayer class imported onto a game layer
strangely all the the sprites have "artefacts" that seem to appear from nowhere since I have checked and re-exported all of the files
Is some setting or something else that could cause this to happen?
I'm new at this
but I have checked so far:
set to PixelFormat_RGBA8888
PVRImagesHavePremultipliedAlpha:YES
png's are clear from artefact (28bit with transparency)
Textures are made with texture packer with "pre-multiplied"
The Background is a CCLayer
The Mine is a CCLayer
both are added to the game layer (cclayer also) as "addChild"
backgroundManager = [[BackGround alloc] init];
[self addChild:backgroundManager z:0];
myShip = [[Ship alloc]init];
[self addChild:myShip z:5];
Yes it was the settings in texture packer
after a few changes in the settings they now seem to load fine.
with no artifacts
use pre multiple
trim not crop
and give a little more inner padding
hope it helps someone else (since it was driving me a little crazy)

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

Get CGImageRef from CCSprite, CCTexture2D or even directly from CCSpriteFrameCache?

I have written some code that takes a UIImage.CGImageRef and puts it into a context so that I can analyze it. This all works great. BUT, I now wish to implement this process in a Cocos2D app.
All of my graphics for the app are done as sprite sheets using Texture Packer so I am looking for any way to get the CGImageRef that I require out of the sprite sheet.
My theory is that you can init a CCSprite with a CGImageRef so why can't I simply get it back out again?
Maybe I am missing something simple like does Cocos2D have a CGImageRef equivalent?
you can try smth like this
CCRenderTexture *renderer = [CCRenderTexture renderTextureWithWidth:size.width height:size.height];
[renderer begin];
CCSpriteBatchNode *spriteSheet = [m_sprite batchNode];
if (spriteSheet != nil)
[spriteSheet visit];
else
[m_sprite visit];
[renderer end];
NSData* uidata = [renderer getUIImageAsDataFromBuffer:kCCTexture2DPixelFormat_RGBA8888];
UIImage* uiimage = [UIImage imageWithData:uidata];
CGImageRef image = [uiimage CGImage];
You can get image data from OpenGL via glReadPixels. It needs some adjustments, but it would be wrong solution in performance aspect. OpenGL is designed as one-way conveyer (you pass data and don't read it back). Depending on your concrete task you may preload image data when loading textures from file.
As example we use alpha channel of textures for per-pixel touch test. So, when we load image we store its bitmask of alpha threshold in memory.
no
cctexture direct from [CCTextureCache sharedTextureCache] read from photo file
ccspriteframe direct from [CCSpriteFrameCache sharedSpriteFrameCache] read from ".plist" file

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]