Problem with CCSpriteFrameCache and sprite textures - ios

I have recently switched from using separate resource files to using a texture atlas. I experimented with replacing a few [CCSprite spriteWithFile] with [CCSprite spriteWithSpriteFrameName]. This works fine except one thing. The CCSprite's texture dimensions are incorrect. Here is my code :
CGSize screenSize = [[CCDirector sharedDirector]winSize];
CCSprite * leftArrow = [CCSprite spriteWithSpriteFrameName:#"smallLeftArrow.png"];
CGSize arrowSize = [leftArrow texture].contentSizeInPixels;
CCSprite * selectedLA = [CCSprite spriteWithSpriteFrameName:#"smallLeftArrow.png"];
selectedLA.opacity = 100;
CCMenuItem * leftArrowItem = [CCMenuItemSprite itemFromNormalSprite:leftArrow selectedSprite:selectedLA target:[HelloWorldLayer sharedHelloWorldLayer] selector:#selector(doLeft)];
leftArrowItem.position = ccp(- arrowSize.width, arrowSize.height);
CCLOG(#"arrow.width = %f arrow.height %f",arrowSize.width, arrowSize.height);
This is the output on the debugger:
2011-08-29 18:27:04.239 Zero Gravity Combat[454:307] arrow.width = 1024.000000 arrow.height 1024.000000
The size of the entire texture atlas is 1024 X 1024. I am using texture packer to create the texture atlas. Is there a way to fix this or do I need to create a texture manually to determine dimensions?

You obviously want to get the size of the arrow sprite. Thats simple: leftArrow.contentSize.
What you've done is getting the size of the used texture, which is of size 1024x1024. The size of the texture is not always the same size as the sprite's size.

Related

(Spritekit) iPad resolution not 1024x768?

I'm trying to set a background texture and I want it to cover the entire screen.
I prepared the background files exactly to size in photoshop before hand. There are 2 files in my project:
background.png - 1024x768px
background#2x.png - 2048x1536px
I am running the following code:
SKTexture *backgroundTexture = [SKTexture textureWithImageNamed:#"background"];
SKSpriteNode *background = [SKSpriteNode spriteNodeWithTexture:backgroundTexture];
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
background.size = CGSizeMake(750, 550);
[self addChild:background];
And it is giving me this result http://d.pr/i/Ej2m - notice that the entire screen is almost filled and the background size is background.size = CGSizeMake(750, 550). Why is this?
You manually changed the sprite's size:
background.size = CGSizeMake(750, 550);
So it will display in a smaller region (750x550) than its original size (1024x768). Allow me to say: d'uh! ;)

""CCSprite is not using the same texture id"" workaround?

I am animating a whole body through SpriteSheets with CCSpriteBatchNode and CCSpriteFrameCache. Now user can add his own pic to that body which when i try to addChild to Spritesheet crashes with error "CCSprite is not using the same texture id"
Now i know the face CCSprite was not in that cache/texture(it was created through texturepacker) and the crash was normal but i wanted to know if there was a workaround to this as i have to add a face to that body through user interaction and animate that body. And by far using spritesheets is the best option for animation. anyone??
In this case what you can do is You take picture of user , then you make texture from user's image .
Then Add that texture to the CCTextureCache . Now you have texture of user image. Now you can use that texture in animation.
Make Texture from Sprite(You can make sprite from user image)
CCSprite *spr = nil;//your sprite
CCRenderTexture* renderTexture = [CCRenderTexture renderTextureWithWidth:spr.contentSize.width height:spr.contentSize.height];
spr.anchorPoint = ccp(0, 0);
spr.position = ccp(0, 0);
[renderTexture addChild:spr];
[renderTexture begin];
[spr draw]; // or [spr visit];
[renderTexture end];
CCTexture2D *result = renderTexture.sprite.texture;
Add that Texture in to Texture Cache.
[[CCTextureCache sharedTextureCache] addTexture]
When you create a CCSpriteBatchNode it is linked to a single texture. This is the point of a CCSpriteBatchNode: to draw different sprites that use the same texture to reduce OpenGL draw calls and increase efficiency.
The easiest workaround (if you have not reached a performance-critical point) would be to use a regular CCLayer instead of a CCSpriteBatchNode.
If you still want to add different CCSprites (say, the body, the limbs and the head of your character) to the same CCSpriteBatchNode, the you need to build a single sprite sheet (or texture pack) which contains all the body parts that you need to add to the CCSpriteBatchNode. This single sprite sheet will be the only one that the CCSpriteBatchNode will use. You won't be able to add CCSprites that are not using this sprite sheet.
You can not Manually add CCSprite to SpriteSheets. Because When you create animated Sprite using texturepacker then i hope that you know it also created with .plist file and it loas image from it with its size.
When you add manually CCSprite then it is nor found from SpriteFramesWithFile. may be you got error.
another way for add animated CCSprite without use of texturepacker
CCSprite *dog = [CCSprite spriteWithFile:#"dog1.gif"];
dog.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:dog z:2];
NSMutableArray *animFrames = [NSMutableArray array];
for( int i=1;i<=5;i++)
{
NSString* file = [NSString stringWithFormat:#"dog%d.gif", i];
CCTexture2D* texture = [[CCTextureCache sharedTextureCache] addImage:file];
CGSize texSize = texture.contentSize;
CGRect texRect = CGRectMake(0, 0, texSize.width, texSize.height);
CCSpriteFrame* frame = [CCSpriteFrame frameWithTexture:texture rect:texRect];
[animFrames addObject:frame];
}
CCAnimation * animation = [CCAnimation animationWithSpriteFrames:animFrames];
animation.delayPerUnit = 0.07f;
animation.restoreOriginalFrame = YES;
CCAnimate *animAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:animation]];
[dog runAction:animAction];
Above code is just describe that how can you add animated CCSprite without use of texturepacker
Here you can also change array of Images so you may be add manually image also.

CCSprite texture size

I am a bit confused about changing the texture of a CCSprite.
I have:
aTexture[NUM_WALLS+11] = [[CCTexture2D alloc]initWithImage:[UIImage imageNamed:#"shop1.png"]];
[aSprite setTexture:aTexture[NUM_WALLS+11]];
and
aTexture[NUM_WALLS+9] = [[CCTexture2D alloc]initWithImage:[UIImage imageNamed:#"bush2.png"]];
[aSprite setTexture:aTexture[NUM_WALLS+9]];
The two images have two different sizes. However, the sprite does NOT change size when I change the texture. Instead, the image scales to the size of the sprite. I thought the sprite was supposed to change size.
Can someone please clarify?
The image files may have a different size, but the textures may use the same size since they usually scale up to the next nearest power of two (with NPOT texture support being disabled by default in cocos2d).
Meaning: if one image is 150x150 and the other is 250x250 their textures will both have 256x256 dimensions. If you load a sprite from an image file, cocos2d adjusts the actual part of the texture that is drawn to the size of the image (contentSize). If you change the texture, cocos2d will simply use the size of the texture regardless of the size of the image stored in the texture - because that information is lost after a texture has been created.
In that case you will have to manually call setTextureRect: on the sprite to draw only the area of the actual image in the texture.
The better solution is to create a texture atlas with both textures and then just change the sprite frame displayed by the sprite. It's a lot easier and saves memory, too.
For those who still want to use setTexture.
I found that this here works:
CCTexture2D* tex2d = [[CCTexture2D alloc] initWithImage:[UIImage imageNamed:_texture]];
CGSize texSize = [tex2d contentSize];
[sprite setTexture:tex2d];
[sprite setTextureRect:CGRectMake(0, 0, texSize.width, texSize.height)];
the order of setTexture and setTextureRect is important. Otherwise it wouldn't work.
as far as i know, CCSprite use it's contentSize property and position to calculate array of vertices to be drawn using OpenGL. In such way, sprite will not change it's vizible size until you change it's contentSize property.
CCTexture2D* tex2d = [[CCTexture2D alloc] initWithImage:[UIImage
imageNamed:_texture]]; CGSize texSize = [tex2d contentSize]; [sprite
setTexture:tex2d]; [sprite setTextureRect:CGRectMake(0, 0,
texSize.width, texSize.height)];
WARNING
This code stores a texture but it may cause a memory leak because the dealloc function is never called.
You can check it by subclassing CCTexture2D class. At least alloc-init always works this wrong way for CCSprite objects

CCSpriteBatchNode with large transparent backgrounds (cocos2d)

I've written a bunch of classes for creating a parallax effect background which contains the ground, hills and clouds. Each object is duplicated once and this allows me to create an infinite loop with the ground etc. Problem is, using CCSpriteBatchNode and 12 x PNG 32bit textures with approx. size 600px by 200px slows down my FPS from 60 to 30. I have read that using CCSpriteBatchNode shows big improvements in performance, but I can't seem to replicate them.
http://www.learn-cocos2d.com/2011/09/cocos2d-spritebatch-performance-test/
How can I speed things up?
EDIT:
Running it on my iPhone gives me 60 FPS, but it shows 30 FPS in the simulator :S
Here's the code, if it helps, there's nothing syntactically wrong with it:
batch = [CCSpriteBatchNode batchNodeWithFile:#"parallax.png"];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"parallax.plist"];
[self addChild:batch];
float screenHeight = [CCUtil screenSize].height;
float screenWidth = [CCUtil screenSize].width;
CCSprite *sky = [CCSprite spriteWithSpriteFrameName:#"sky.png"];
sky.anchorPoint = ccp(0,0);
[batch addChild:sky];
CGPoint speedCloudSlow = ccp(0.08f, 0.002f);
CGPoint speedCloudFast = ccp(0.15f, 0.006f);
CCSprite *clouds1 = [CCSprite spriteWithSpriteFrameName:#"clouds1.png"];
CCSprite *clouds2 = [CCSprite spriteWithSpriteFrameName:#"clouds2.png"];
CCSprite *clouds3 = [CCSprite spriteWithSpriteFrameName:#"clouds3.png"];
CCSprite *clouds4 = [CCSprite spriteWithSpriteFrameName:#"clouds4.png"];
[batch addChild:clouds1];
[batch addChild:clouds2];
[batch addChild:clouds3];
[batch addChild:clouds4];
CCSprite *land1 = [CCSprite spriteWithSpriteFrameName:#"land.png"];
CCSprite *land2 = [CCSprite spriteWithSpriteFrameName:#"land.png"];
CCSprite *landBrown1 = [CCSprite spriteWithSpriteFrameName:#"land_brown.png"];
CCSprite *landBrown2 = [CCSprite spriteWithSpriteFrameName:#"land_brown.png"];
CCSprite *landDark1 = [CCSprite spriteWithSpriteFrameName:#"land_dark.png"];
CCSprite *landDark2 = [CCSprite spriteWithSpriteFrameName:#"land_dark.png"];
CCSprite *ground1 = [CCSprite spriteWithSpriteFrameName:#"ground.png"];
CCSprite *ground2 = [CCSprite spriteWithSpriteFrameName:#"ground.png"];
[batch addChild:land1];
[batch addChild:land2];
[batch addChild:landBrown1];
[batch addChild:landBrown2];
[batch addChild:landDark1];
[batch addChild:landDark2];
[batch addChild:ground1];
[batch addChild:ground2];
Running it on my iPhone gives me 60 FPS
Perfect, so you don't actually have a problem!
FYI, whatever framerate you see in the Simulator, you should not concern yourself with that. There's no point wasting time to even think about Simulator performance. It is irrelevant, can not be compared, and bears no meaning to your application's real world performance.

Cocos2d Sprite Continuous

I am trying to create a background to flow with the game. However the image isn't Continuous. There is a space between each of the image loads. I want the image to continue to loop.
Here is the method to create the sprite
CCSprite *sprite = [CCSprite spriteWithFile:#"Image.png" rect:CGRectMake(0, 0, 960, 640)];
ccTexParams tp = {GL_NEAREST, GL_NEAREST, GL_REPEAT, GL_REPEAT};
[sprite.texture setTexParameters:&tp];
sprite.anchorPoint = ccp(1.0f/8.0f, 0);
sprite.position = ccp(screenW/8, 0);
Method to update the position of the sprite.
- (void) setOffsetX:(float)offsetX {
if (_offsetX != offsetX) {
_offsetX = offsetX;
CGSize size = _sprite.textureRect.size;
_sprite.textureRect = CGRectMake(_offsetX, 0, size.width, size.height);
}
}
Any help please
Your image width needs to be a power of two. i.e. the width has to be 64, 128, 256, 512, etc if you want it to repeat
The gap you are seeing is where OpenGL has padded empty space to your texture to make it power of two.
After trying it a few times, the best way is to ensure that the sprite dimensions are a power of 2. This will ensure that you can scale the layer and all remains fine. If you don't plan on scaling the layer, then you can use any size sprites and use this:
[[CCDirector sharedDirector] setProjection:CCDirectorProjection2D];
http://ak.net84.net/iphone/gap-between-sprites-when-moving-in-cocos2d/

Resources