cocos2d-iphoneccspriteatlassprites

Cocos2d: 6 doubts on usage of CCSpriteBatchNode


I am wondering how to optimize the usage of CCSpriteBatchNode. In other words I understand that:

What I am not 100% sure and I would like your answer is:


Other considerations on 3

In order to address my assumption in 3., and reduce the number of CCSpriteBatchNode instances, my solution would follow what suggested by @Suboptimus in this answer. I like the suggested approach of initializing classes that want to share the same batch node with the MainScene class, rather than having them to access the MainScene via self.parent.parent.(...).parent.finallysharedbatchNode

Other people would instead suggest to refer to the MainScene via using self.parent.....parent and casting it correctly.

Is this the best approach also in Software Engineering terms?

I prefer making it clear where the sprites are added by using an explicit reference to the MainScene class.. this should help if I am working in team or if I change the class hierarchy. But the downside of this is that I "need" to store a reference to it if I want to add subsequently sprites to the batch node, resulting in more code to maintain.

The reason I am asking this question is that if find a slight clash between my traditional "Software Engineering" mind and the "Cocos2d parent-node" hierarchy approach. I am new to game programming and I'd like to understand which approach is the one that experienced game developers that work in large teams use :). H


Solution

    1. Correct, but a "draw call" is not equivalent to executing the draw method. A draw call is a change in the OpenGL state that requires performing an expensive operation to reset the statemachine. Binding a new texture or changing transform fit the bill.
    2. Correct.
    3. Correct.
    4. No need to do that. Animations run on sprites. So only the sprite needs to be batched. If one animation frame is not from the same texture atlas, CCSpriteBatchNode will complain when the animation tries to use such a frame.
    5. It's preferable. Add/Remove of sprites in a CCSpriteBatchNode is more expensive than add/remove of any other node. Because the sprite batch node's quads need to be updated. Though it probably only makes any difference if you have many child nodes and add/remove frequently.
    6. No and no. See my answer here.

    Other considerations on 3:

    If your scene hierarchy is not too deep, you can go with self.parent or maybe self.parent.parent on occasion but only where the parent-parent relationship is practically fixed (ie from a sprite batched sprite bypassing the sprite batch node in order to get to the underlying "true" parent). But I wouldn't recommend going any deeper. See my answer here for techniques for both self.parent and avoiding retain cycles.

    The problem with self.parent.parent.(…).parent is that this completely breaks if you need to change the parent child relationship, for example by adding or removing a parent node in the hierarchy. This will then crash badly with EXC_BAD_ACCESS and it's hard to debug because you will have to check each parent and parent's parent to see what kind of object it really is. Accessing parents over 3 or more levels of the hierarchy I wouldn't consider bad practice. It's a terrible practice.

    Personally, for access to commonly used nodes like shared sprite batches, I prefer the solution where the "MainScene" becomes a temporary Singleton class for the time while it is active. Then you can do the following from any child node:

    CCSpriteBatchNode* mainBatch = [MainScene sharedMainScene].spriteBatchNode;
    

    To create this temporary singleton:

    static MainScene* instance;
    -(id) init
    {
        self = [super init];
        if (self)
        {
            instance = self;
        }
        return self;
    }
    -(void) dealloc
    {
        instance = nil;
    }
    -(MainScene*) sharedMainScene
    {
        NSAssert(instance, @"MainScene is not initialized!");
        return instance;
    }
    

    The difference to a real singleton is that it doesn't initialize the instance if it doesn't exist yet. Hence the NSAssert in sharedMainScene. You should only access the scene instance while it is already running, ie it's only to be used by child nodes of that particular scene.

    This semi-singleton allows access to all of the scene's properties. You can also send messages that the scene could relay to other nodes. Or enqueue physics objects in the scene that need to be removed, but can't be removed during collision handling itself.

    And if that singleton bothers you, there's always the possibility to get the running scene from the director:

    MainScene* mainScene = (MainScene*)[CCDirector sharedDirector].runningScene;
    

    You should be careful casting to the MainScene only if the runningScene is really a MainScene object. An isKindOfClass: check or assert is in order.