So here's what I'm trying to do:
Create a sprite from the atlas I read into CCSpriteFrameCache (working fine.)
Add an animation to the sprite - I can also get this working fine but...
The animation is created by loading up a number of CCSpriteFrames, which in turn are reading in CCTextures from a shared CCTextureCache as their assets. It works, but I'm not convinced it's the best way. Seems like I should be loading those CCSpriteFrames with elements of the atlassprite I already loaded in CCSpriteFrameCache.
Question is: is there a method or way of loading those CCSpriteFrames with CCSprites named in the frames of CCSpriteFrameCache?
Open to the idea that I'm going about this wrong. Thanks for any tips.
Code here:
CCAnimation *thingAnimation = [CCAnimation animationWithName:#"wiggle" delay:0.1f];
//this works but I want to get the CCTexture from CCSpriteFrameCache....
aTexture = [[CCTextureCache sharedTextureCache] addImage:#"moon.png"];
//because CCSpriteFrame will only accept a CCTexture
frame1 = [CCSpriteFrame frameWithTexture:aTexture rect:CGRectMake(0, 0, aTexture.pixelsWide, aTexture.pixelsHigh) offset:ccp(0,-40)];
[thingAnimation addFrame:frame1];
I suppose if you knew this already, you think it was a pretty stupid question. I found the answer here:
http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:animation
Basically to do animation from an atlas and a plist, you need to be able to work with CCSpriteSheet, CCSprite, CCSpriteFrame, CCSpriteFrameCache and CCAnimation.
You create the CCSpriteSheet first. Then you create the CCSpriteFrameCache from a plist. Then you can create CCSprites and CCSpriteFrames at will; the CCSpriteFrames feed into the CCAnimation.
That's my take, anyhow. Any better way, lemme know.
Related
I'm developing a game for iPad. When I run the game on ipad and ride scene presence for the ipad automatically closes the application for lack of memory.
With TexturePacker converted images to pvr, but going from scene to scene'm not able to release memory.
[CCTextureCache sharedTextureCache] removeUnusedTextures];
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
I am using these lines but not being enough.
Any idea?
Please consider:
Use instruments leak tool - try making snapshots at the same specific moment (for instance when you are ready to run your scene) then check what was allocated between them.
Try printing memory used by your application in specific parts of your program to find out where the memory is leaking.
Try to find out which dealloc's are not called when you remove your scene then go deeper.
Try calling general functions instead of unused ones:
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrames];
[[CCTextureCache sharedTextureCache] removeAllTextures];
After you remove unused textures, use :
[CCAnimationCache purgeSharedAnimationCache];
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
[[CCDirector sharedDirector] purgeCachedData];
CCLOG(#"%#<applicationDidReceiveMemoryWarning> : after purge", self.class);
[[CCTextureCache sharedTextureCache] dumpCachedTextureInfo];
This will list the textures that are still retained in the cache, and the amount of memory they consume. Any texture that is still present when you expect it to be disposed, is probably there because somewhere along the lines (of code ;) ), you retained them 'somehow' (for example, adding them to an array).
I add my sprite frames to CCSpriteFrameCache. Then I create a CCSpriteBatchNode with my desired image file.
This is what I don't quite understand:
When I make a CCSprite, if I want to take advantage of the CCSpriteBatchNode, I need to initialize the CCSprite with [CCSprite spriteWithBatchNode: rect:]? But if that's the case, I don't see how am I taking advantage of CCSpriteFrameCache to get the frames, since now I would be manually making the rect.
So I guess I use [CCSprite spriteWithSpriteFrameName:] and then I add this sprite to the batch node. But I am still unsure.
You should use:
CCSprite *sp = [CCSprite spriteWithSpriteFrameName:#"monster.png"];
The .plist that you specified in the SpriteFrameCache will take care of the frames for you.
Then you create the sprite and add to the batch.
If you create the batchnode with a file called "myArt.png", you CAN ONLY add a sprite to it that is contained inside "myArt.png".
Hope it helps!
According to what I've learned of cocos2d. SpriteFrameCache and SpriteBatchNode have the same result but are used differently and can notice a slight performance difference if your game is very big...
CCSpriteFrameCache loads your frames according to when they are called by their named according to the plist file it was given. The atlas associated with the plist has to be added to the project as well or else the frames will be called but nothing will be found to be drawn. The Plist is like the address of where the image is located inside the image atlas.
The good part of CCSpriteFrameCache is that the code is neater, and smaller than CCSpriteBatchNode method, at the cost that for every call of that frame, it goes to that specific atlas and draws it.
CCSpriteBatchNode, on the other hand, loads the atlas and loads it in one draw call. This is efficient because it reduces the amount of times the draw has to be done per need in the game. The only difficulty here is that you need to do math for the rectangles of each sprite in the atlas. This is because lets say your atlas is of 2 actions of a character, the atlas image file has a size of 1024x1024, and each sprite has a size of 128x128. so you would do the math to get each rectangle for the whole jump action for example.(This is where .plist come in handy to avoid doing such math)
The code gets complicated as you can see but it will only do one call, making it performance-wise your best call.
Another way to use CCSpriteBatchNode is to have different static sprites and you would just do one draw call for those multiple static images or sprites.
If you need example code just ask, I would be more than happy to provide it.
Update: Adding Link for SpriteBatchNode and an Example of my own.
SpriteBatchNode:
Example using SpriteBatchNode with Ray Wenderlich
I believe in this guy, and I have learned alot of Cocos2d from his tutorials. I would suggest you to read other of his tutorials.
In a nutshell, CCSpriteBatchNode is the exact same process we did below with the CCSpriteFrameCache the ONLY difference and its that you add the Sprite Child Node to the CCSpriteBatchNode and not the Layer, BUT you do Add the CCSpriteBatchNode to the Layer.
This is the hard concept that new comers to Cocos2d get entangled at.
SpriteFrameCache:
The SpriteFrameCache I couldn't find a good example so here is one simple one.
//By doing this your sprites are now in the cache ready to be used
//by their names declared in the .plist file.
-(void) loadingSprites:(NSString*) plistName {
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:plistName];
}
-(id)initGameLayer {
//CCSprite accepts CCSpriteFrame and your img is now ready to be displayed.
//However is still not drawn yet.
CCSprite * mySprite = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:framename];
//set a position if desired
//20 pixels to the right and 0 pixels to the top.
mySprite.position = CGPointMake(20,0);
//Now the Image has been drawn, making 1 draw call.
[self addChild:mySprite];
}
It is noteworthy to point out that CCSpriteBatchNode makes just 1 drawcall, HOWEVER all the sprites being added to the batchnode have to be part of the same SpriteAtlas.
And using SpriteFrameCache only its easier and simpler, but for every child added to the layer it means +1 draw call is being done.(This is the downside, performance)
So if you add 10 Sprites to the layer with SpriteFrameCache you will have 10 drawcalls.
However if you implement the SpriteBatchNode and add those 10 Sprites in the CCSpriteBatchNode instead and just add that CCSpriteBatchNode to the layer, you will have the same 10 sprites added but only ONE draw call will be done. Hence the Performance difference(for the best) will be significant in larger games.
Hope it helps, Cheers!
I have a "ball" sprite that changes it's texture when a certain scenario occurs. I change it's texture like this:
[ball setTexture:[[CCTextureCache sharedTextureCache] addImage:#"red.png"]];
This does work; it changes the ball sprite to use the red.png image. How would I handle this if I have about 20 balls that need to switch to using this sprite? Would I run through each ball and "addImage"?
If I could, I would like to load the texture once, save it in a variable (called like "redTexture"), and then be able to assign it to any of the ball objects.
Any advice on how to approach this would be a huge help, thank you!
If you were to put that statement inside, say, a for loop, you'd be adding the red.png image to the shared texture cache over and over again, which I doubt is what you want.
Let's back up and re-write things a bit, starting by adding the red.png image to the shared texture cache, on a line by itself:
[[CCTextureCache sharedTextureCache] addImage:#"red.png"];
You'd subsequently get the same texture again simply by calling [CCTextureCache sharedTextureCache]. Until you add another image to the shared texture cache, that is.
CCTextureCache is a singleton, and its docs don't suggest there's a way to make a copy of the shared texture cache (which would be ideal for preserving your redTexture). That being the case, just create a variable and point it at [CCTextureCache sharedTextureCache]; just be careful not to add any other images to it before you're done with it:
CCTextureCache *redTexture = [CCTextureCache sharedTextureCache];
Now let's assume you already have an array (or mutable array) called ballArray, that contains 20 ball objects. You could loop through them like this:
for (YourBallObject *ball in ballArray)
{
[ball setTexture:redTexture];
}
Or even better, you could do this:
[ballArray makeObjectsPerformSelector:#selector(setTexture:) withObject:redTexture];
Good luck in your endeavors.
In my application I needed something like a particle system so I did the following:
While the application initializes I load a UIImage
laserImage = [UIImage imageNamed:#"laser.png"];
UIImage *laserImage is declared in the Interface of my Controller. Now every time I need a new particle this code makes one:
// add new Laserimage
UIImageView *newLaser = [[UIImageView alloc] initWithImage:laserImage];
[newLaser setTag:[model.lasers count]-9];
[newLaser setBounds:CGRectMake(0, 0, 17, 1)];
[newLaser setOpaque:YES];
[self.view addSubview:newLaser];
[newLaser release];
Please notice that the images are only 17px * 1px small and model.lasers is a internal array to do all the calculating seperated from graphical output. So in my main drawing loop I set all the UIImageView's positions to the calculated positions in my model.lasers array:
for (int i = 0; i < [model.lasers count]; i++) {
[[self.view viewWithTag:i+10] setCenter:[[model.lasers objectAtIndex:i] pos]];
}
I incremented the tags by 10 because the default is 0 and I don't want to move all the views with the default tag.
So the animation looks fine with about 10 - 20 images but really gets slow when working with about 60 images. So my question is: Is there any way to optimize this without starting over in OpenGl ES?
As jeff7 and FenderMostro said, you're using the high-level API (UIKit), and you'd have better performance using the lower APIs, either CoreAnimation or OpenGL. (cocos2d is built on top of OpenGL)
Your best option would be to use CALayers instead of UIImageViews, get a CGImageRef from your UIImage and set it as the contents for these layers.
Also, you might want to keep a pool of CALayers and reuse them by hiding/showing as necessary. 60 CALayers of 17*1 pixels is not much, I've been doing it with hundreds of them without needing extra optimization.
This way, the images will already be decompressed and available in video memory. When using UIKit, everything goes through the CPU, not to mention the creation of UIViews which are pretty heavy objects.
Seems like you're trying to code a game by using the UIKit API, which is not really very suitable for this kind of purpose. You are expending the device's resources whenever you allocate a UIView, which incurs slowdowns because object creation is costly. You might be able to obtain the performance you want by dropping to CoreAnimation though, which is really good at drawing hundreds of images in a limited time frame, although it would still be much better if you used OpenGL or an engine like Cocos2d.
The UIImageView is made to display single OR multiple images. So, instead of creating every time a UIImageView, you should consider creating a new image and add it to the UIImageView instead.
See here.
I'd recommend starting over using OpenGL ES, there is an excellent framework called cocos2d for iPhone that can make this type of programming very easy and fast. From a quick look at your code, you're lasers can be remodeled as CCSprite which is an easy way to move images around a scene among many other things.
I just developed a game using Cocos2D,it is so easy to move an object on the screen using Cocos such as MoveTo. But what about in Cocoa? How can I make an object move from point A to point B easily? Could anyone help? I really appreciate any helps. This is the code i used to make an object move in Cocos. How can I convert this to Cocoa, I mean the way to make a thing move in Cocoa.
Sprite *logo = [Sprite spriteWithFile:#"logo.png"];
logo.position = CGPointMake (240 , -10);
[self addChild:logo];
id actionTo = [MoveTo actionWithDuration:1.0f position:ccp(240, 55)];
[sexyTrainning runAction:actionTo];
Depends on what kind of object you want to move.
Using Core Animation you could move a CALayer along an arbitrary path using the path property of CAKeyframeAnimation
More examples here.