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.
Related
I'm very new to cocos2d development.
What I am trying to implement is some sprite nodes with physics bodies. As far as I understand, you're supposed to add them to the scene like this ("physics" here is the name of CCPhysicsNode declared earlier):
[physics addChild:node];
instead of
[self addChild:node];
The second one is pretty self-explanatory, I never had any trouble with it. But with the first the position of collision shapes does not match the position of the actual sprite (as seen in debug drawing, and by offset I mean greatly offset, like 2x the actual position). This is how I declare and add the node:
CCSprite *sprite = [CCSprite spriteWithImageNamed:#"sprite.png"];
sprite.position = position;
sprite.physicsBody = [CCPhysicsBody bodyWithRect:[sprite boundingBox] cornerRadius:0];
sprite.physicsBody.collisionType = #"SomeCollisionType";
sprite.name = #"Name";
[physics addChild:sprite];
What am I doing wrong? Please explain me how to make the positions match. TIA.
I can clip using native coding within iOS however, as I wish to port across to Android using SpriteBuilder I want to clip 2 CCSprites using Cocos2D.
I am looking to do the following :
I have seen libraries which support only Cocos2D 2 however I am using the latest version and these no longer seem to work.
How would I achieve this affect?
For anyone looking for a similar fix the following is native to Cocos2D.
//Get Screen size
CGSize winSize = [[CCDirector sharedDirector] viewSize];
//Set Clipping Sprite
CCSprite *heroClip = [CCSprite spriteWithImageNamed:#"stamina/MenuHappinessWhite.png"];
heroClip.position = ccp(winSize.width/2, winSize.height/2); // Middle of screen
//Set Sprite below Clipping
CCSprite *heroUnder = [CCSprite spriteWithImageNamed:#"stamina/MenuLevel.png"];
heroUnder.position = ccp(winSize.width/2, winSize.height/2);
heroUnder.scaleY = 0.5f;
// Create Clipping Node
CCClippingNode *scissor = [CCClippingNode clippingNodeWithStencil:heroClip];
[scissor setContentSize:self.contentSize];
[scissor setPositionType:CCPositionTypeNormalized];
[scissor setAlphaThreshold:0.0];
//[scissor setInverted:YES];
[self addChild:scissor];
// Add nodes to Clipping Node
[scissor addChild:heroUnder];
and add this to app delegate
//Load Clipping Mask
[cocos2dSetup setObject:#GL_DEPTH24_STENCIL8_OES forKey:CCSetupDepthFormat];
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.
i wanted to use CCRenderTexture in my project because i have a lot of CCLabelBMFont that are mostly static. Adding them all to my Scene caused some performance issues (Yes you can use BatchNotes etc. but it didnt really help). So I rendered them into a single Texture which increased the performance significantly! But the problem is that the rendered texture and the directly rendered node look different. I have no idea why!
I created a cocos2d-sample project and and created this:
CCSprite* testImage = [CCSprite spriteWithFile:#"N.png"];
testImage.position = ccp(100,100);
CCRenderTexture *rt = [CCRenderTexture renderTextureWithWidth:786 height:1024];
[rt beginWithClear:0 g:0 b:0 a:0];
[testImage visit];
[rt end];
CCSprite* renderedSprite = [CCSprite spriteWithTexture:rt.sprite.texture];
renderedSprite.position = ccp(386,512);
// Flip because CCRenderTexture is flipped
renderedSprite.flipY = YES;
// Add normal node an the rendered sprite
testImage.position = ccp(130,100);
[self addChild:testImage];
[self addChild:renderedSprite];
The results look like this:
How can this be? How can i make them look the same?
The N on the right is the sprite added the "normal" way and it is displayed correctly. The N on the left is the texture.
Edit:
i found this tutorial which hints that the blend functions are different. so can i am looking for the correct function to make them look the same.
I found the solution!
I used this example to create the sprite but when i looked into the cocos2d-manual again i saw, that you can add the CCRenderTexture directly to your scene. Thats what i did and it solved the problem!
So the correct code should be:
CCSprite* testImage = [CCSprite spriteWithFile:#"N.png"];
testImage.position = ccp(100,100);
CCRenderTexture *rt = [CCRenderTexture renderTextureWithWidth:786 height:1024];
[rt begin];
[testImage visit];
[rt end];
rt.position = ccp(386,512);
// Add normal node an the rendered sprite
testImage.position = ccp(130,100);
[self addChild:testImage];
[self addChild:rt];
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.