How can I optimise drawing from large SKTextureAtlas files with preloading? - sprite-kit

I have a game with 4 Texture Atlases. Each atlas is 4096 x 4096 and contains roughly 300 sprites, all ~ 15kb - 50kb. Unoptimised in Texture Packer, they're around 2 - 4MB each, and about half that when optimised.
They contain assets to draw characters, but I'm finding it's very slow to draw a single character (around 24 nodes) ~ 0.5 seconds. This is because of multiple calls to [atlas textureNamed:textureName]
To speed things up, I want to preload the atlases. Ideally I would keep them in memory as I always need them. So my first question is, is this possible with atlases of this size? I've tried calling [SKTextureAtlas preloadTextureAtlases:#[MaleFeatureAtlas] withCompletionHandler...] but I get a crash with no stack trace, just a lost connection to device.
Currently, I have an AtlasManager class, that has static variables that initialise to each texture atlas:
static SKTextureAtlas *MaleFeatureAtlas;
static SKTextureAtlas *MaleItemAtlas;
static SKTextureAtlas *FemaleFeatureAtlas;
static SKTextureAtlas *FemaleItemAtlas;
#implementation AtlasManager
{
}
#pragma mark - Initialisation Methods
+ (void)initialize
{
MaleFeatureAtlas = [SKTextureAtlas atlasNamed:MaleFeatures];
MaleItemAtlas = [SKTextureAtlas atlasNamed:MaleItems];
FemaleFeatureAtlas = [SKTextureAtlas atlasNamed:FemaleFeatures];
FemaleItemAtlas = [SKTextureAtlas atlasNamed:FemaleItems];
}
Each character sprite has an instance of an AtlasManager, but since the SKTextureAtlases are static variables, I figured they would be fast to draw. But the constant calls to [atlas textureNamed:textureName] really slow the drawing down. I am storing an NSDictionary of nodes once drawn, so redrawing is very quick, but the initial draw takes far too long. Rendering 8 characters, with just over 100 nodes in total, takes about 5 seconds.
So, is a singleton approach better than using static variables? And is it wise to preload atlases of this size?

There's a lot of room for differing opinions regarding your questions. Keeping that in mind, I suggest the following:
When creating a texture atlas you should always plan ahead to what is really needed. For example, you have 5 different kinds of enemies but your game's first level only presents enemy #1 and #2. In this case you should create a texture atlas which only contains the required assets for your first level (enemy #1 & #2).
Singleton Pros:
Centralizing all your code into one class.
You only need to have 1 instance of the class.
Prevents having to double load some assets.
Singleton Cons:
Amount of code can get overwhelming if there are a lot of assets to manage.
Subclassing Pros:
Having all your code & assets handled in one class specific to the sprite's needs.
Subclassing Cons:
In certain instances you might have the same animation or image loaded multiple times by different classes. For example, a certain kind of explosion might get utilized by one or more subclass.
I prefer using the singleton approach because I like to centralize my code. Below is a simplified example on how I do this. I also use TexturePacker and use the header file option when creating a texture atlas. My singleton class is called Animations.
In the Animations header file I have this:
-(void)loadPlayer0Atlas;
#property (strong) SKTexture *player0_startLeft;
#property (strong) SKAction *player0_idleLeft;
#property (strong) SKAction *player0_idleRight;
#property (strong) SKAction *player0_walkLeft;
#property (strong) SKAction *player0_walkRight;
In the Animations implementation file:
-(void)loadPlayer0Atlas {
if(self.player0Atlas == nil) {
self.player0Atlas = [SKTextureAtlas atlasNamed:PLAYER0ATLAS_ATLAS_NAME];
[SKTextureAtlas preloadTextureAtlases:[NSArray arrayWithObject:self.player0Atlas] withCompletionHandler:^{
[self loadPlayer0Assets];
}];
} else {
[[NSNotificationCenter defaultCenter]postNotificationName:#"player0AtlasLoaded" object:self];
}
}
-(void)loadPlayer0Assets {
self.player0_startLeft = PLAYER0ATLAS_TEX_PLAYERIDLEL__000;
self.player0_idleLeft = [SKAction repeatActionForever:[SKAction animateWithTextures:PLAYER0ATLAS_ANIM_PLAYERIDLEL timePerFrame:0.2]];
self.player0_idleRight = [SKAction repeatActionForever:[SKAction animateWithTextures:PLAYER0ATLAS_ANIM_PLAYERIDLE timePerFrame:0.2]];
self.player0_walkLeft = [SKAction repeatActionForever:[SKAction animateWithTextures:PLAYER0ATLAS_ANIM_PLAYERWALKL timePerFrame:0.1]];
self.player0_walkRight = [SKAction repeatActionForever:[SKAction animateWithTextures:PLAYER0ATLAS_ANIM_PLAYERWALK timePerFrame:0.1]];
[[NSNotificationCenter defaultCenter]postNotificationName:#"player0AtlasLoaded" object:self];
}
The above code allows me to load up the player's assets by calling the method loadPlayer0Atlas. This method checks if the atlas has already been created. If yes, it posts a NSNotification indicating this. If no, it loads up the atlas, assigns the assets to class properties and then posts the NSNotification.
Back in the class which called loadPlayer0Atlas, you need to register for the NSNotification in either the init or didMoveToView methods.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myMethod)
name:#"player0AtlasLoaded"
object:nil];
Once the notification has been received, the myMethod can proceed with code knowing the player atlas is now loaded.
For good housekeeping remember to remove the calling class from NSNotifications like this:
-(void)willMoveFromView:(SKView *)view {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
To answer your last question on preloading an atlas. Yes it is always wise to preload your atlas because it makes for smoother game play. Having to load assets mid game will potentially cause lags. Just remember to only load the assets you require for the scene you are currently in. Good planning will yield you good results.

Related

Cocos2d: usage of references vs getChild performance

I am unsure on which approach will give me a better performance:
I have a GameScene class in which I tend to allocate a objects and then add them as childs to the class with a tag. This approach is used in many examples I studied but I am not sure if it is the best when I need to frequently access to child objects (e.g. update a score label in the GameScene). In those cases I am wondering if it would be a better approach in terms of performance to declare a pointer to those frequently used object and use it to access the object instead of getting the child by tag.
Is it more efficient to use getChildByTag or accessing those objects via their pointer?
As example I provide a label that I frequently update during the game, e.g. it could be the score label:
#interface GameScene : CCLayer
{
CCLabelTTF *frequentlyUsedLabel;
}
//Implementation
-(id) initWithId:(int)sceneId
{
if ((self = [super init]))
{
//..code
frequentlyUsedLabel = [CCLabelTTF labelWithString:#"Hearths:" fontName:#"Arial" fontSize:20];
[self addChild:frequentlyUsedLabel];
}
}
Write code that is easy to maintain.
Anything worth optimizing is worth measuring.
Time how long it takes to render a frame using both methods. Ask yourself if it matters.
The extra memory overhead is a little more difficult to measure (in particular, Cocos2D might be using a clever allocator that is more efficient if your CCLayer subclass doesn't declare any extra ivars). My take is that 4 bytes is the amount of memory used by a single pixel.
With UIKit, I personally find it much easier to use ivars than to worry about tag uniqueness (especially when reusing code in different apps or using a view in multiple places). This is especially the case with ARC, since you no longer have to worry about getters/setters/retain/release.

CMMotionManager is a global resource? What does this means?

I am trying to initialize the CMMotionManager so start updates and create a reference attitude matrix, and then when i click a button trough the storyboard, i display a different screen (which is in a viewcontroller) and i want to use the reference attitude matrix and other readings from the motion manager but if I do a simple check to see if its ready it says it isnt (even thought it was ready on the previous screen).
So I was researching a little bit and i came across some standford notes that say it is a global resource so that it is ok to either use delegates or classes, that got me thinking.
If i use a delegate then technically the owner of the resource is doing the action for me right? so it doesnt really mean it is global.
What about classes? i tried implementing a MotionManager Class but i still didnt get any readings on the second screen... and i just didnt initialize it again on the new screen because i dont want to have multiple instances runing at the same time.
To solve my problem i also suppose i could initialize it once in the main one, save the attitude, pass it to the next one in a segue transition and just re initialize the motion manager so i only have 1 instance (or close old one on screen will dissapear).
But i do not want this because the user might click right away on the screen and the motion manager does take a little bit of time to initialize (or thats what i noticed).
Thanks in advance for any help you can provide.
EDIT:
Ok i tried the delegate method and i still cant read from the device manager in the second window. What comes to mind is that MAYBE when I am going to the second window xcode is automaticaly releasing my instance of Motion manager.... (CMMotionManager).
Anyone knows how to check this?
I hope I understood you right. Then I suggest using a singleton design pattern encapsulating motion manager access in a specialised class. Some pseudo code:
MotionHandler.h
#interface MotionHandler {
CMMotionManager* motionManager;
+ (MotionHandler*) getInstance;
}
MotionHandler.c:
#interface MotionHandler {
static MotionHandler* instance;
+ (MotionHandler*) getInstance {
if (instance == nil) {
instance = [[self alloc] init];
}
return instance;
}
- (id)init {
if ((self = [super init])) {
motionManager = [[CMMotionManager alloc] init];
// initialise CMMotionManager
}
}
}
So there is one instance of MotionHandler only which manages access to CMMotionManager. You can accesss your CMMotionManager instance from everywhere with MotionHandler.getInstance.motionManager.
If you need access to CoreMotion from several classes, I recommend total encapsulation of CMMotionManager access. That means make it #private and provide methods like getDeviceMotion, setReferenceAttitude, ...
This helps to avoid complications like starting it twice or access to CMDeviceMotion before start and makes it more convenient to debug.

Redrawing GameHUD multiple times causes cocos2d frame rates to drop significantly

I'm currently building a game for the iPhone with cocos2d and have the following problem:
I have a singleton class called GameHUD which displays a HUD in front of the current level scene. Once in a while I want the HUD to be redrawn, so it changes accordingly to the current game status. The problem is the more often I redraw the HUD, the more the frame rate drops.
I'm guessing that I fail to release some ressources, but cannot figure out which ones I have to release. (I'm pretty new to memory management.. I understand that I have to release objects that are created with one of following keywords: "new", "alloc", "copy" or "retain". But cocos2d mainly generates autorelease objects, therefor I mustn't release them manually.. correct me, if I'm wrong ;))
//static, so it can be called from other classes
+(void)redrawGameHUD{
CGSize winSize = [CCDirector sharedDirector].winSize;
//get reference to background-sprite
CCSprite *background = [[[GameHUD class] sharedHUD] towerBackground];
//remove the child from the HUD, if it exists
[[[GameHUD class] sharedHUD] removeChild:background cleanup:YES];
//create sprite containing the background-image
background = [CCSprite spriteWithFile:#"background.png"];
//add background image to HUD
[[[GameHUD class] sharedHUD] addChild:background];
//load images that should be displayed into an array
NSArray *images = [NSArray arrayWithObjects:#"image1.png", #"image2.png", #"image3.png", #"image4.png", nil];
//remove sprites from HUD before drawing them again.
//the "buildable" array contains all those already drawn sprites
for (CCSprite *entity in [[[GameHUD class] sharedHUD] buildable]) {
[[[GameHUD class] sharedHUD] removeChild:entity cleanup:YES];
}
[[[[GameHUD class] sharedHUD] buildable] removeAllObjects];
//loop over sprites, initialize them and add them to the HUD
for(int i = 0; i < images.count; ++i) {
NSString *image = [images objectAtIndex:i];
CCSprite *sprite = [CCSprite spriteWithFile:image];
//add sprite to HUD and memorize them in the "buildable" array
[[[GameHUD class] sharedHUD] addChild:sprite];
[[[[GameHUD class] sharedHUD] buildable] addObject:sprite];
}
}
So every time this method is called, the frame rate drops a little bit and stays down.. Can somebody please tell me, what I am doing wrong? Thanks.
Try not to create and remove sprites at runtime, ie try to avoid doing this frequently:
[CCSprite spriteWithFile:#"background.png"];
This allocates new memory for the sprite, and theres quite a bit of things going on behind the scenes when you create a new sprite. And of course you're releasing the already existing sprites. All of that is unnecessary.
In your redrawGameHUD method I see no indication why you actually want to create the sprites anew. The sprites are using the same images every time. So why not just keep the old ones? Unless you edited the code before you posted it in the questions, there's no need to remove and re-create the HUD sprites.
You also might want to create a texture atlas for all the HUD sprite images. For one, you can then render all HUD sprites with one draw call by using a CCSpriteBatchNode. Secondly, whenever you do want to assign a new texture to an existing sprite, you would simply change the CCSpriteFrame of that sprite instead of throwing away the sprite and re-creating it.
Something else that bothers me, you keep writing this:
[[[GameHUD class] sharedHUD] addChild:sprite];
First, this is the same as writing the following, the message to class is absolutely unnecessary (makes me wonder where you picked that up?):
[[GameHUD sharedHUD] addChild:sprite];
And since you do this multiple times over, you should keep a temporary local copy of GameHUD, this again removes several unnecessary Objective-C messages:
GameHUD* gameHUD = [GameHUD sharedHUD];
// from now on use gameHUD instead of [GameHUD sharedHUD]
[gameHUD addChild:sprite];
This is particularly good advice for loops, because doing this:
for (CCSprite *entity in [[[GameHUD class] sharedHUD] buildable])
will send two extra messages (class and sharedHUD) for every entity in the buildable array. Those extra calls can quickly add up, although they're certainly not enough for the drop in framerate you're experiencing.
You also unnecessarily keep all the HUD sprites in the "buildable" array. Why not use the already existing children array that cocos2d uses? Simply add each HUD sprite that is "buildable" with the same tag, for example 123.
[gameHUD addChild:sprite z:0 tag:123];
If you need to do something with all the "buildable" sprites you can then iterate over the children like this:
CCNode* node;
CCARRAY_FOREACH([gameHUD children], node)
{
if (node.tag == 123)
{
CCSprite* buildable = (CCSprite*)node;
// do stuff with buildable sprite ...
}
}
Again, this avoids the unnecessary adding, retaining, removing and releasing of objects in the buildable array. And you can be sure that you don't accidentally remove sprites from the node hierarchy but not the buildable array, or vice versa.
I'd like to conclude with an assumption: in your code I saw a general tendency that you're doing many extra things unnecessarily. So I'm guessing this is the case throughout the project. You might want to go back and ask yourself (better: find out) what other things there are that you're having the device perform that are rather unnecessary.
I am 90% sure that you are testing this on the iPhone simulator, am I right?
If you are, then keep in mind that you can't profile an OpenGL app properly on the simulator .. The performance is too variable.
Test it on a real device and check the frame rate again.
If not, then you got a problem somewhere else. All the processing done in this method are trivial, unless your images are 5000x5000 px or something..

Subclassing a sprite in cocos2d

A few days ago, I started working with cocos2d. I really like the framework. I would like to create a game with cocos2d and have a probably simple question...
I am making a game with 4 characters, which all have similar characteristics, but have some different attributes like "type" and "points". I'd like to subclass the sprites into one class which handles all their logic, drawing, and animation.
My question though, is how do I call the sprite class with say, a "type" parameter of 1, 2, 3, or 4 and then have the class draw the correct sprite into my scene with all of it's individual logic?
Thanks!
You should have an Enemy class that contains properties of specific enemies and that are not type specific (like position, current health, a CCSprite instance?) and an EnemyType class that contains properties that are shared among all enemies of a specific type (max health, max speed, size, sprite filename). You should load your enemy types prior to loading the level, than instantiate each enemy using the appropriate type in the constructor.
For example if your enemy element in the level file looks like this
<enemy><type>spider</type>...more properties...</enemy>
The code (pseudo) would do something like
EnemyType *enemyType = nil;
if (typeElement.value == "spider")
{
enemyType = spiderType;
}
Enemy *newEnemy = [Enemy enemyWithType:enemyType];
Also the Enemy class should contain the CCSprite that represents it, not subclass it. An enemy is not a sprite, so unless I'm missing something, as i see it, an enemy should not inherit from a sprite. I'd read about when to contain and when to inherit.
EDIT:
Another nice post to read that seems very relevant and could communicate a few other things probably better than me.
Anyway, my intention was not to make you think you should just rethink your entire design. What i'm proposing is "better" MVC-wise, but it doesn't mean it's better for your game. If you spend all your time on "design correctness" you'll never get a game done, especially if you're just learning the cocos2d framework, i was actually making a learning project not too long ago and Steve McConnel himself would come over and slap me if he saw it.
If you're working on a game alone and it's a small project go ahead and subclass away if it's going to be more manageable to you, everything, including "design correctness" needs to be properly quantified (except maybe usage of "goto" statements :) ).
polymorphism in this way can be done a couple of different ways, some better than others.
1) you could try to just override the init or node method and set up your object there.
+(CCSprite *)node
{
MySprite * returnSprite = [super node];
returnSprite.hat = #"fedora";
returnSprite.hatImage = [CCSprite spriteWithImage:...];
}
2) use reflection (psuedocode)
-(void)drawingMethodHere
{
[self.hat drawAtPoint:somePoint];
}
then override -(CCNode *)hat to reflect the type.
you may have to do some combination of them, but plan a little before you start, you will end up saving a lot of time.
You should subclass CCNode instead of subclassing CCSprite.
I think your problem is quite easy. Just create a base class called Character, which has the common logic, properties etc etc. Then you create 4 other classes like, enemy, player and so on and subclass from Character base. Note the character base should be subclassing CCNode.
Now you override the logic to fit your needs in the specific class. Now you will be able to use polymorphism, which is good.
For your sprite I would say create an instance variable of the CCSprite type and then create methods to initialize with an image. Then you will just add that sprite as a child when initializing the object.

Most Efficient way to deal with multiple CCSprites?

I have four different types of objects within my environment(box2d), each type of object having multiple instances of itself, and would like to find the most efficient way to deal with adding and manipulating all the CCSprites. The sprites are all from different files, so would it be best to create each sprite and add it to a data structure (NSMutableArray) or would I use a CCSpriteBatchNode even though each CCSprite file is different (for each type of object)? Thanks.
#interface LevelScene : CCLayer
{
b2World* world;
GLESDebugDraw *m_debugDraw;
CCSpriteBatchNode *ballBatch;
CCSpriteBatchNode *blockBatch;
CCSpriteBatchNode *springBatch;
CCSprite *goal;
}
+(id) scene;
// adds a new sprite at a given coordinate
-(void) addNewBallWithCoords:(CGPoint)p;
// loads the objects (blocks, springs, and the goal), returns the Level Object
-(Level) loadLevel:(int)level;
#end
Well, CCSpriteBatchNode isn't likely your answer, if you are using different textures. From the Cocos2D documentation here:
CCSpriteBatchNode is like a batch
node: if it contains children, it will
draw them in 1 single OpenGL call
(often known as "batch draw"). A
CCSpriteBatchNode can reference one
and only one texture (one image file,
one texture atlas).
So that's out. Ostensibly, you'd be adding these CCSprite objects to a CCLayer (or some other CCNode object), right?
There's no need to create a separate array for managing child objects -- Cocos2D provides you with the children property of any node. If you [myLayer addChild:newSprite], you'll have a reference to it in children.
Does that help? I can expand my answer if you provide me with a little more idea of your use case.