CCNode in Cocos2d-x? - cocos2d-iphone

In cocos2d-iPhone we have a CCNode which:
has a position
has a size (wether we like it or not, it has)
an anchor point
This is very useful to define areas on screen where that area has children that belong together. Like a group of buttons. We can easily hide/unhide everything just by hiding that parent CCNode.
What is the equivalent in cocos2d-x? I see that Node does not have a size. Layer seems like its going to be deprecated... and according to the docs, a Sprite is something that moves. Where is the logical stuff?

Implement that function by yourself:
void setVisibilityWithChildren(CCNode* parent, bool bVisible) {
if (0 == parent) return;
CCNode* node = NULL;
CCARRAY_FOREACH(parent->getChildren(), node)
{
setVisibilityWithChildren(node, bVisible);
if (node) node->setVisible(bVisible);
}
}

In cocos2d-x, CCNode has the same attributes as it in cocos2d. Including size, position, anchor point and so on.
I don't know what it like in cocos2d, but in cocos2d-x CCNode is something without graph. You may consider that it is invisible.
If you wish to see where and what the size it is. You may use CCLayerColor instead. All of the functions you use are the same as CCNode, besides an extra step setColor()
CCNode => CCLayer => CCLayerRGBA => CCLayerColor (this is the inheritance tree of CCLayerColor in cocos2d-x 2.2.6)

Related

Trouble removing sprites from parent node

I'm making an endless running game in which users dodge obstacles, and I'm working on producing the obstacles right now. The plan I had where I'm spawning these obstacles is as follows:
obstacle->setPosition(CCPointMake(this->getContentSize().width, this->getContentSize().height*.75));
obstacle->setScale(.5);
this->addChild(obstacle);
_obstacles->addObject(obstacle);
obstacle->runAction(CCMoveBy::create(2.0, CCPointMake(-(this->getContentSize().width + obstacle->getContentSize().width/2), 0)));
obstacle->removeFromParent();
I set the position, set it's scale, add it to the scene, run an action on it so that it moves across the screen from right to left, add it to an array called _obstacles to be used elsewhere, and then I remove it from the scene so as to save memory. However, the problem is that once I try implement this, the obstacle doesn't show up at all as if it's nowhere to be seen. When I don't call obstacle->removeFromParent() it shows up and performs the action. What am I doing wrong here? If I don't call removeFromParent(), what do I call? Is there a problem in my code not related to removeFromParent()?
The reason that obstacle doesn't apeear at all is that it is removed as it start moving. You just have to create a sequence of move action and function call with obstacle as parameter and than remove this obstacle in that function , so that obstacle will be removed after moving out of screen.
CCCallFuncN *myCallFunc = CCCallFuncN::create(this, callfuncN_selector(CLASS_NAME::removeObstacles));
obstacle->runAction(CCSequence::create(CCMoveBy::create(2.0, CCPointMake(-(this->getContentSize().width + obstacle->getContentSize().width/2), 0)),myCallFunc,NULL));
Method to remove obstacle from array and from parent view
void CLASS_NAME::removeObstacles(CCObject* pSender){
// Type cast pSender to obstacle type e.g if obstacle is of CCSprite type.
CCSprite *tempObstacle = (CCSprite *)pSender;
_obstacle.pop_back(tempObstacle);
tempObstacle->removeFromParent();
}
Don't forget to replace CLASS_NAME by your class name
I would need more info on what removeFromParent is doing and how you use _obstacles
I'm taking a guess on how the parent is structured. I would guess that it has a list of children and when the parent is updated/render, it calls those functions for its children. If removeFromParent removes the object from its parent list, then that would explain why it is not being render or used in the scene. If you have a setup like this, then you should only call removeFromParent before destroying the parent object

Cocos2d: the best way to compose an object that will contain several Sprites

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.

Tile Maps and vertexZ order for sprites

Here is the setup. I have a orthogonal tile map made with Tiled. There are 5 layers. The bottom 4 comprise the background, while the top layer is the foreground that I will refer to as the “tree” layer. I have a hero sprite that I have added as a child of the tile map at the same node zorder as the tree layer.
The issue that invariably comes up with this scenario is that I want my hero to be in front of trees that he is “below”, but behind those trees that he is “above”. Below and above are determined by the Y coordinate of the hero versus the Y coordinate of any given tree.
The Cocos2d programming guide contains a section for tile maps, and in that section it specifically discusses how to achieve the affect I have described. This information can be found here.
http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:tiled_maps
The solution is pretty simple. Enable the depth buffer in my code. Add a cc_vertexz property with a value of -1000 to each layer that the hero will be above. Then add a cc_vertexz property to the tree layer with a value of “automatic” as well as a cc_alpha_func property with a value of .5. Then as your hero moves re-adjust its sprite’s vertexz value based on its position within the map.
I did all this and it worked great. However, a problem appeared as soon as I added other “enemy” sprites to this map. As with the hero sprite these sprites were added to the map with the same node zorder as the tree layer, and they too have their vertexz values changed based on their Y coordinate within the map. It should be noted that the hero sprite is added after the enemy sprites. In addition, both the hero and enemy sprites are animated and their textures are not part of the tile map textures. Based on the Cocos2d documentation this should not matter.
The problem is this. If my hero is below an enemy sprite on the map then everything looks and works fine. However, if my hero is above an enemy sprite on the map then as the enemy passes over the hero sprite the background comes through rather than the hero sprite. The best way to describe it is to say that as soon as the enemy sprite begins to pass over the hero sprite, the transparent areas of the enemy sprite begin to fill with the background and the hero sprite is partially obscured until then enemy sprite moves off the hero. It is like the hero sprite is not there.
I know this is related to the order that the sprites are added to the map, because if I change this and have the hero sprite added to the map before the enemy sprites, then the effect reverses in that the background comes through the transparent areas of the hero sprite rather than the enemy sprites. It is almost like the vertexz property is not absolutely determining the order that things are drawn.
Looking into this it appears to be a blending issue and I found what seems to be the solution, which is to subclass the CCSprite class and override the draw method with the following:
-(void) draw {
glEnable(GL_ALPHA_TEST);
glAlphaFunc( GL_GREATER, 0 );
[super draw];
glDisable(GL_ALPHA_TEST);
}
The problem however is that this code is not applicable to the version of Cocos2d that I use, which is Cocos2d 2.1. In addition this appears to be handled by the CCTMXLayer drawing function if automatic vertexz is enabled. As such I do not know if subclassing CCSprite would just be redundant. See below for how CCTMXLayer handles this:
// CCTMXLayer drawing function -(void) draw
{
if( useAutomaticVertexZ_ ) {
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, alphaFuncValue_);
}
[super draw];
if( useAutomaticVertexZ_ )
glDisable(GL_ALPHA_TEST);
}
I am not new to Cocos2d, but I am very new to anything related to OpenGL. So in short, is the solution just to subclass CCSprite and find the Cocos2d 2.1 equivalent to the above code snippet, or am I simply doing something wrong? I suspect the latter.
Any help would be very much appreciated. Thanks!
This has been resolved. The solution was to use the following on my sprites or sprite sheets as the case may be:
mySprite.shaderProgram = [[CCShaderCache sharedShaderCache]programForKey:kCCShader_PositionTextureColorAlphaTest];
This essentially takes the place of sub-classing CCSprite and overriding the draw method, which is the prescribed method for Cocos2d 1.x, whereas I am using Cocos2d 2.x.

Game object with composition and CCSpriteBatchNode

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.

Can I layer a CCSprite between two CCTMXLayers?

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.