Most Efficient way to deal with multiple CCSprites? - iphone

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.

Related

Creating a class to run a single action on multiple sprites

I'm trying to rotate numerous sprites (about 48 different ones) around an arbitrary point (using this article: http://indiedevstories.com/2012/08/14/custom-cocos2d-action-rotating-sprite-around-arbitrary-point/ ) and I managed to create the custom category for it, but it only works on a single sprite. I've looked around on the site and tried to use runAction:[action copy] but it makes the copies rotating points crazy numbers for some reason. Then I tried to create a method for the actions and just call the method, but I keep getting errors for that as well. I've tried so many different solutions but no luck.
So my question is, is there a way I can create another class that holds all of my sprites, and then run a single method to run an action on all of the sprites of the class?
Assuming you have an array called spriteArray containing all sprites you wish to rotate, it's as simple as:
for(CCSprite *sprite in spriteArray)
{
CCRotateAroundBy *rotateAround = [CCRotateAroundBy actionWithDuration:1.0 angle:90 rotationPoint:screenCenter];
[sprite runAction:rotateAround];
}

What's the best way to access the current scene (safely) in cocos2d?

I was reading Learn Cocos2d 2; and stumbled across this problem: in a game scene one might have thousands of entities below the scene hierarchy and all these entities might want to talk to the topmost scene object. The author of Learn Cocos2d 2 suggests using a global variable like this
static GameLayer* sharedGameLayer;
+(GameLayer*) sharedGameLayer
{
NSAssert(sharedGameLayer != nil, #"GameScene instance not yet initialized!");
return sharedGameLayer;
}
+(id) scene
{
CCScene *scene = [CCScene node];
GameLayer *layer = [GameLayer node];
[scene addChild: layer];
InputLayer* inputLayer = [InputLayer node];
[scene addChild:inputLayer z:1 tag:GameSceneLayerTagInput];
return scene;
}
Personally I think this approach unreliable: imagine a player entity want to know the touch input from another layer in the scene, so it calls sharedGameLayer and assuming it is the parent that it actually resides in it just works on from there. But how can it be so sure? what if this sharedGameLayer is another instance that someone else has allocated? Of course there can only be one scene at a time in cocos2d, and so this approach just might work most of the time without any problem. But from a design point of view, I still find such approach lacking.
I've thought about two alternatives:
using the proper (old) delegate approach. Assigning the correct delegate/delegator relationship at each level. However, this means thousands lines of possibly duplicate code and is not neat from my perspective
using NSNotification (or KVO?) to broadcast messages from the children level, and let the parent respond accordingly. But does this mean unnecessary overhead for the system
At this point I'm quite lost, can someone tell me about the general idiom that experienced programmers might use?
You may want to read my "Strategies for accessing other nodes in the hierarchy" guide. Plenty of options to choose from with pros and cons.
In most cases, the leaves of your tree of CCNodes, like for exampe the "player entity" you mention, will only persist as long as the containing CCScene is on the CCDirector's scenes stack. Once this scene gets popped, it will receive a dealloc message and thus removeAll(its)Children. This in turn will dealloc these children, including your "player entitity". The last message your "player entity" will receive is a dealloc during which all parent nodes are still alive (although in the process of being destructed), so there will never be a case where children would request an invalid sharedGameLayer. However, if you want to carry over any children to another scene, you will have to take great care in so many ways that this is hardly anything to be encouraged if not absolutely necessary. For example, your specific block of code would have to be improved so that sharedGameLayer will return nil if the scene is no longer active, and your "player entity" might want to check this return value.
The short answer is that the code you show above is safe as long as you are not moving nodes around in the scenes stack. There are many alternative ways for individual nodes to learn about the "graph" of your game, including the parent property of every CCNode, and the runningScene property of the CCDirector.

CCArray with CCSprites in Cocos2D/Kobold2D

I am developing a Cocos2D/Kobold2D game with ARC. I want to store CCSprite references in a CCArray. All the CCSprite objects are added as children to a parent CCNode. Is it a good idea to wrap the CCSprite objects with [NSValue valueWithNonretainedObject:..] for weak references? In all examples I found the CCSprites were stored retained in CCArrays...
There is really no need to use NSValue.
If you add objects to an NSArray or CCArray or any collection, they will be retained. It is your duty to make sure the objects are removed respectively the collection released at the appropriate time.
In many cases you don't actually need to store node references separately. You have the children array, just iterate over it and select the nodes you need to work with, for example by tagging them or by using the userObject property.

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.

Creating pointer Attributes in cocos2d iPhone

I am working on a game. There are balls that fall from the top of the screen, and the player has to catch them, as the are caught they stack ontop of one another. I have a method that creates each new ball and adds it to an Array that i use to move the sprites. Problem is that the after they collide I need them to stop moving, since the array is called on to move them they all move. And if i try to make them stop they all stop. So I was hoping to create a pointer attribute if ther is such a think, for example "sprite.position" I need a new attribute that i can check like a boolean. I was hoping to create a attribute like sprite.hasCollided and if it returns YES then the ball should no longer move. Is this possible or is there a better way to do it?
Thanks
Tanner
I would suggest you create a ball object. And add the boolean as as part of the object.
CCNodes (and, by inheritence, CCSprites) have a userData property, which is a void*. You can use this to relate a custom object to a cocos2d object. Keep in mind if you use the userData option, you will, in most cases, need to allocate any memory when you create/assign the sprite, and release it when you are done.
int* myInt = (int*)malloc(sizeof(int));
*myInt = 0;
sprite.userData = myInt;
//some time later, when you are done with the sprite
free(sprite.userData);
As an improvement on the userData property, you can do what xuanweng suggests and create a ball object containing various game-related properties for the balls, and assign an instance of this to each of your ball CCSprites using the method above.