I have 2 CCLayers that needs to communicate with each other in separate .m files
Level1.m (CCScene with Level1 CCLayer) - Holds tiled map and player sprite
HUDLayer.m (links to top of Level1.m) - Holds all buttons
How can I get the following code in HUDLayer.m to talk to player sprite in Level1.m?
- (void)MoveUpSelected {
CCMoveTo* moveup = [CCMoveBy actionWithDuration:1 position:ccp(0,-100)];
CCSequence* sequence = [CCSequence actions: moveup, nil];
[Player runAction:sequence];
}
Please help I've been stuck on this for days. At least if someone can point me in the right direction. Thanks!
I would advice you to use you scene object to control communication between its layers.
You can create a HUD protocol and set the scene as its delegate. And for each HUD event the scene will react accordingly by accessing the proper layer (stored as its member).
This way you won't have to make this layer coupling.
To access another layer, you need a reference to it. There are many ways to do that. In your case just add one property for each layer to the CCScene class. The layers can then access the scene via their parent:
CCLayer* otherLayer = [(YourActualSceneClass*)self.parent otherLayer];
It is very important that you do not store a reference to the other layer in either layer, or if you do, make sure to make it a weak reference, or nil them in the cleanup method. Otherwise you created a retain cycle.
You'll find more info on accessing other nodes here.
Related
I need to create an object that contains several sprites (2 for simplicity: the game object and its' shadow).
I need to compose them into the same class, so having the following questions:
what is the best ancestor class to the described one?
I used CCNode for this purpose. I overridden its draw method in the following way:
- (void)draw {
[super draw];
[_item draw];
[_itemShadow draw];
}
and suddenly found that need to change all other CCNode methods in the same way. For ex. change position, visible, etc. in order to change these properties in both Sprites my custom container aggregates:
#interface NBChecker : CCNode {
CCSprite *_item;
CCSprite *_itemShadow;
}
#end
There is the other way I see - to make both sprites parent property pointing to self. This should synchronise positions, visibility, etc. for these sprites.
Who has better ideas?
Would like to use aggregation and not sure I'm right in my thoughts.
Thanks!
Scrap the draw code. All you need to do is to add the two sprites as children of the CCNode subclass. Write your logic code in the CCNode subclass and have two ivars for each sprite for easier access (like you already have).
You don't need to draw the sprites yourself as long as they are part of the scene graph.
The sprites will move and rotate relative to their parent automatically. Move the node to move the sprites in unison, etc.
I don't get why this code is incorrect...
DeadPanelLayer* deadPanelLayer = [DeadPanelLayer node];
[(CCScene*)self.parent addChild:deadPanelLayer z:2];
DeadPanel is a layer that I want to add on the Scene running from another layer
any idea why my layer is not loaded?
Edit: the game is not crashing, but the new layer does't show up. I tried to even move the objects on the scene and layer just to make sure it is not displaying due zindex hierarchic
but still...
I tried to add a method on Scene to add the layer in case it is called from parent:
[(GameScene*)self.parent showDeadPanel];
and even get current scene from Director
[[CCDirector sharedDirector] runningScene]
and it doesn't work either
I don't know if this is possible, but I would like to create one big texture atlas and use it on all classes of the application.
Can one CCSpriteBatchNode be used for multiple classes?
Suppose I create this on the main class
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"atlasGeral.plist"];
self.batchNodeGeneral = [CCSpriteBatchNode batchNodeWithFile:#"atlasGeral.png"];
[self addChild:self.batchNodeGeneral];
and I have another class creating CCLayers on the main class, that is initialized, before using CCSpriteBatchNode, like this:
-(id) init
{
if( (self=[super init])) {
self.bg = [CCSprite spriteWithFile: #"cCircularBG.png"];
[self addChild:self.bg];
self.dr = [CCSprite spriteWithFile: #"cCircularDR.png"];
[self addChild:self.dr];
}
return self; // self is a CCLayer
}
can this be optimized using the self.batchNodeGeneral from the main class? My idea is to replace these two sprites and others with something like [CCSprite spriteWithSpriteFrameName:...
thanks
I'm not entirely sure I follow, but I'm pretty sure the answer is yes.
CCSpriteBatchNode doesn't have anything to do with classes, it has to do with assets. The important restriction on the use of batch nodes is that every sprite in the batch needs to reference the same texture atlas. So it is perfectly fine to have one batch node for your entire application and have every gameplay class add its own sprites to that batch. This can turn into a practical issue if your texture atlas becomes larger than the maximum texture size on your target hardware (see iOS device specs for details), but if you still want to have global batches and lots of assets it's not too hard to create a batch pool indexed by texture ID, create one batch node per atlas, and whenever you create a new sprite add it to the appropriate batch.
Honestly, I feel like the whole batch node thing is a terrible kludge on Cocos2D's part that could be made almost completely transparent to developers while still retaining its efficiency, but maybe this opinion is not entirely fair since I haven't dug around in the rendering code to understand their motivations. I guess it would mess with expectations of how depth sorting works, etc., but still I don't understand why batching objects for render is made the programmer's responsibility, it should be done by the engine.
Edit to add possible solution:
-(id) initWithMainClass:(MainClass*)mc
{
if( (self=[super init])) {
self.bg = [CCSprite spriteWithSpriteFrameName: #"cCircularBG.png"];
[mc.batchNodeGeneral addChild:self.bg];
self.dr = [CCSprite spriteWithSpriteFrameName: #"cCircularDR.png"];
[mc.batchNodeGeneral addChild:self.dr];
}
return self; // self is a CCLayer
}
`
So when you initialize one of the other classes, you pass the main class instance as a parameter so you can access its fields. Or make the main/manager class a singleton or find another architecture suitable to your needs.
I'm currently developing a game with cocos2d and box2d on iPhone.
I read a lot of articles concerning game code organization, game objects structure etc.
I first started developing my game objects by inheriting from a base class itself inheriting from CCSprite.
I had a CCSpriteBatchNode to draw all the game items, which the player can interact with, in one draw call. This was easy because my Item class indirectly inherit from CCSprite so I could easily add my items to the CCSpriteBatchNode. And on top of that I could rely on cocos2d to retain my objects.
After I read the articles, I understood the need to refactor my code with a more composition oriented style rather than the inheritance style.
So I went with a GameObject base class inherited from NSObject and having properties such as one or more CCSprite, one b2Body etc.
The problem I'm facing now is that I can't directly add my GameObject to the CCSpriteBatchNode anymore. I first thought I could easily fix the problem by adding the sprite property of the GameObject to the CCSpriteBatchNode. It's ok but who retains the object owning the CCSprite ? How can I easily access the original object from the CCSprite (are userData/Object ok) ?
Should I create an array retaining my items ?
I'd like to know how you would use a CCSpriteBatchNode with such a game object structure ?
There is already a thread about that which is unanswered and I'd really like to hear about the subject. Not a straight answer but some elements to go further.
Thanks.
Personally I don't recommend using NSObject as the base class for cocos2d classes anymore. Simply because you lose some cocos2d convenience features such as scheduling and you can easily shoot yourself in the foot, memory-management wise.
What you want is a setup where the scene has one or more sprite batch nodes. You can consider them layers for your sprites. Your actual game objects (consider them to be MVC controllers) deriving from CCNode can be added anywhere, typically directly to the scene.
scene
+ batch node 1
+ sprite 1
+ sprite 2
+ sprite n
+ batch node 2
+ sprite 1
+ sprite 2
+ sprite n
+ game node 1
+ game node 2
+ game node 3
+ game node n
The thing to keep in mind is that each game node has one or more sprites as instance variables, but they're not childs of the node. So for example the game node 1 class might look like this:
game node 1 class
CCSprite* sprite1; // instance variable
CCSprite* sprite2; // instance variable
Now when you init game node 1 and its sprites, you add the sprites to the appropriate sprite batch node. Typically you will want to access your scene like a singleton to get access to its sprite batch properties.
sprite1 = [CCSprite spriteWithSpriteFrameName:#"doodle.png"];
[[scene sharedScene].batchNode1 addChild:sprite1];
sprite2 = [CCSprite spriteWithSpriteFrameName:#"splash.png"];
[[scene sharedScene].batchNode2 addChild:sprite2];
Note that you do not need to retain the sprites as long as they are children of the sprite batch node. The addChild method retains it for you. Actually, it just adds it into an array which does the retaining. Also, if you're still thinking about "retain", by all means start using ARC.
You do not need to figure out how to access the sprite in the sprite batch. It's available as an instance variable of your game node class, and if you wish, you can also make it publicly available to other classes as a property.
The game node class obviously runs all the object's game logic, including positioning and otherwise modifying the sprite's properties. The only thing you need to take care of in the game node class is that you remove the sprites from the batch node, otherwise it may be leaking. To do so, override the cleanup method:
-(void) cleanup
{
[sprite1 removeFromParentAndCleanup:YES];
sprite1 = nil;
[sprite2 removeFromParentAndCleanup:YES];
sprite2 = nil;
[super cleanup];
}
It's important to set the variables to nil because there may be code running after cleanup, including the code in the dealloc method. You could also remove the sprites in dealloc but depending on your setup, dealloc may not even get called because of the references to the sprite the game node class is still holding. Therefore it is generally safer to use the cleanup method if the sprites are not children (or grandchildren) of the game node class itself.
I am making a top-down shooter that makes extensive use of TMX maps created with the "Tiled" application. Within my TMX map, I have a "Background" layer with floor tiles, which appears beneath my characters (CCSprites.)
I have another layer in the TMX file called "Foreground" which I would like to appear "above" my CCSprites, giving the illusion of them walking underneath various objects.
I tried using the vertexZ property of the CCNode class to do this:
CCTMXLayer *backgroundLayer = ...
CCSprite *spriteNode = ...
CCTMXLayer *foregroundLayer = ...
[backgroundLayer setVertexZ:1];
[spriteNode setVertexZ:2];
[foregroundLayer setVertexZ:3];
...but it turns out vertexZ actually alters the node's visual appearance within the openGL view. It effectively causes a CCNode to appear larger, or closer to the user when it has a higher vertexZ value. I don't want that- all I want is a sort of layers-of-an-impossibly-thin-cake effect, without any visual differences between the layers.
So I thought I would try altering the zOrder property of the nodes, like this:
[[backgroundLayer parent] reOrderChild:backgroundLayer z:1];
[[spriteNode parent] reOrderChild:backgroundLayer z:2];
[[foregroundLayer parent] reOrderChild:backgroundLayer z:3];
But I realized there's a fundamental problem with what I'm doing here, since my spriteNode is a direct child of the CCScene, but the background and foreground nodes are both children of my CCTMXTiledMap, which itself is a child of the CCScene.
So I'm basically trying to slip a CCSprite between two layers of the map, which, from the CCScene's perspective, are really just two parts of the same layer.
It seems I could create an additional instance of CCTMXTiledMap just to hold the foreground layer, but that also seems like overkill. My other thought was to create CCSprites to serve the same purpose, but it seems like there's got to be a better way.
Yes, I have used Tiled once very lightly and I do believe there is an option to add an Object Layer into your TMXTiledMap (Tiled -> Layer -> Add Object Layer...), then once imported into your build you can link up a CCSprite with the corresponding Object Layer you have created. I would post your question on the cocos2d forum, as people there are more experienced and equipped to answer this with examples.