How can I display the bounding box for a CCNode in cocos2d? - cocos2d-iphone

As a follow-on question to my previous question about displaying the anchor point, I subclassed CCSprite and changed its draw method as follows:
[super draw];
ccDrawColor4F(0, 1, 0, 1);
ccDrawCircle(self.anchorPointInPoints, 20, 0, 8, YES);
This works great. For extra credit, I added the following to display its bounding box:
CGRect bb = self.boundingBox;
CGPoint vertices[4] = {
[self convertToNodeSpace:ccp(bb.origin.x, bb.origin.y)],
[self convertToNodeSpace:ccp(bb.origin.x + bb.size.width, bb.origin.y)],
[self convertToNodeSpace:ccp(bb.origin.x + bb.size.width, bb.origin.y + bb.size.height)],
[self convertToNodeSpace:ccp(bb.origin.x, bb.origin.y + bb.size.height)],
};
ccDrawPoly(vertices, 4, YES);
This also works great, until I reparent a sprite:
CGPoint oldPosition = [sprite convertToWorldSpace:sprite.position];
[sprite removeFromParentAndCleanup:NO];
[parentSprite addChild:sprite];
sprite.position = [sprite convertToNodeSpace:oldPosition];
The sprite's now in the proper position and its anchor point draws where it should, but the bounding box draws in the wrong place. What am I doing wrong?

Bounding box of a node is relative to its parent. Drawing done in draw method is in node's local space. convertToNodeSpace: converts coordinates from world space to local space, not from parent's space.
When you reparent node to a parent with different origin while maintaining same "world" position of this node, origin of its bounding box changes.
Your mistake is that you treat your sprite's bounding box as if its coordinates were in world space.
Second, you don't need to do the convert-to-x-space dance to draw bounding box of a sprite. Open ccConfig.h file in cocos2d folder of your project and change
#define CC_SPRITE_DEBUG_DRAW 0
line to
#define CC_SPRITE_DEBUG_DRAW 1
Third, coordinates of sprite.position point are relative to its parent, not to the sprite. When you call [node convertToWorldSpace:aPoint], it will treat aPoint as if it were in node's local space. If you want to get world coordinates of a node's position, you should call convertToWorldSpace: on node's parent: [node.parent convertToWorldSpace:node.position].

I normally draw AABBs with:
Rect aabb = someNode->getBoundingBox();
DrawNode* drawNode = DrawNode::create();
drawNode->drawRect(aabb.origin, aabb.origin + aabb.size, Color4F(1, 0, 0, 1));
parentNode->addChild(drawNode, 100);
Note: If the Rect aabb size is (0,0) then the rectangle will not be drawn.

Related

Positionning particle in node

Using cocos2d, I'm trying to replace a sprite (item in my code) with a particle system. This code is placed in my board class. This one works:
// Draw the particles
CCParticleSystem *particles = [[CCParticleSystem alloc] initWithDictionary:_popParticles];
particles.position = ccpSub(item.position,ccp(160,160));
particles.autoRemoveOnFinish = TRUE;
[self addChild:particles];
This one doesn't:
// Draw the particles
CCParticleSystem *particles = [[CCParticleSystem alloc] initWithDictionary:_popParticles];
particles.position = item.position;
particles.autoRemoveOnFinish = TRUE;
[self addChild:particles];
I tried player with this but without success:
particles.positionType = CCPositionTypeMake(CCPositionUnitUIPoints, CCPositionUnitUIPoints, CCPositionReferenceCornerBottomLeft);
My board is a 320x320 points CCSprite with anchor point set at 0.5, 0.5
When I log my item.position value, I get something relative to the bottom left corner of my board (from 30,30 to 290,290)
Is using ccpSub the correct way ?
When you destroy a node you also destroy all of it's children nodes, you said you add your particle to 'item' then you destroy that 'item', which means you have no particle anymore.

CCSprite anchorPoint not working as expected?

Let me start by explaining what I am trying to do. I have a full screen animation with about 73 frames and with these images being so large I am not able to use sprite sheets so I am just adding them to an animation as seperate spriteframes. My goal is to have the animation play through and then have it disappear from left to right. The way that I am achieving this look is by updating the width of the (textureRect) of the frames and eventually making the textureRect width 0. So I have set up the CCSprite that I am going to run the animation on.
-(id) init {
if( (self=[super init])) {
self.transSprite = [CCSprite spriteWithFile:#"transition0.pvr.ccz"];
if (CC_CONTENT_SCALE_FACTOR() == 1) {
//iPhone 3Gs
self.transSprite.scaleX = .563;
self.transSprite.scaleY = .665;
self.transSprite.position = ccp(self.transSprite.contentSize.height - 3, -1);
self.transSprite.anchorPoint = ccp(1, 0);
[self addChild:self.transSprite z:5];
}
else if (kiPhone5) {
//iPhone 5
self.transSprite.scale = self.transSprite.scale * 1.335f;
self.transSprite.position = ccp(569, -1);
self.transSprite.anchorPoint = ccp(1, 0);
[self addChild:self.transSprite z:5];
}
else {
//iPhone 4
self.transSprite.scaleX = 1.126;
self.transSprite.scaleY = 1.33;
self.transSprite.position = ccp(481, -1);
self.transSprite.anchorPoint = ccp(1, 0);
[self addChild:self.transSprite z:5];
}
}
return self;
}
So the reason for checking what device is running, is because I did not make 2 versions of the animation frames HD and SD. Instead I just made them a good in between size and then I check to see what device is running and scale the sprite accordingly. So here is my problem I am setting the Sprites anchorPoint to the bottom right hand corner of the screen so that when I change the width of the textureRect it will decrease in size from left to right. Everything seems to be working great with this idea except for when I change the width of the textureRect of each sprite frame.
- (void) rectUpdate3Gs {
for (CCAnimationFrame *frame in transition.frames) {
frame.spriteFrame.rect = CGRectMake(0, 0, 856, 484);
}
}
- (void) retinaRectUpdate {
for (CCAnimationFrame *frame in transition.frames) {
frame.spriteFrame. rect = CGRectMake(0, 0, 428, 242);
}
}
So when I run the animation and start to change the width of the textureRect it decreases from both the left and the right side, it is like the anchorPoint is being ignored. Do the spriteFrames have there own anchorPoint or what is happening.
Here is an illustration of what is happening.This is the CCSprite set up for the full texture and with the anchorPoint in the bottom right corner.
self.transSprite.scaleX = .563;
self.transSprite.scaleY = .665;
self.transSprite.position = ccp(self.transSprite.contentSize.height - 3, -1);
[self addChild:self.transSprite z:5];
[self.transSprite setTextureRect:CGRectMake(0, 0, 856, 484)];
self.transSprite.anchorPoint = ccp(1, 0);
Now this is setting the textureRect of the CCSprite a little smaller.
This is the way that I want it to work, is to subtract the width of the texture from the left to right. But when I change the width of the animation frames to match this subtracted width this is what I get.
I am at a loss as to why this is happening. Do the animation frames not respect the anchor point of the CCSprite they are running on?
Scaling issue aside, there's 2 things you can do. First, and probably easiest, is to simply add a CCLayerColor on top of the animation and scale that down accordingly. This way you don't need to modify every animation frame's rect.
Second option would be to use a clipping node, found here:
Here

Clip a CCSprite added to CCSpriteBatchNode

I'm clipping my sprite with this code:
//At my CCSprite subclass m.
-(void)visit
{
CGPoint worldOrg = [self convertToWorldSpace:ccp(0, 0)];
CGPoint dest = [self convertToWorldSpace:ccp(self.contentSize.width, self.contentSize.height)];
CGPoint dims = ccpSub(dest, worldOrg);
glEnable(GL_SCISSOR_TEST);
glScissor(worldOrg.x, worldOrg.y, dims.x, dims.y);
#define SHOW_CLIPPED_AREA 1
#if SHOW_CLIPPED_AREA
//Draws a red rectangle showing clipped area
ccDrawSolidRect(ccp(0, 0), ccp(1024, 1024), ccc4f(64, 0, 0, 128));
#endif
[super visit];
glDisable(GL_SCISSOR_TEST);
}
Then just create the sprite as usual, adjust the sprite.contentSize property to whatever I need:
CCSprite aSprite = [CCSprite spriteWith...];
aSprite.contentSize = CGSizeMake(20,20);
//Add it to my layer
[self addChild:aSprite];
And it works as expected!
Problem...
When adding it to a CCSpriteBatchNode, it wont clip the sprite... it shows the sprite but without clipping it.
Can someone please help me out with this, I've googled everywhere with no answer to this.
I've also used the ClippingNode class from Steffen Itterheim, but I'm also having issues adding it to a CCSpriteBatchNode.
Any help will be appreciated.
Clipping or any custom drawing won't work with sprite-batched sprites.
The CCSpriteBatchNode will not call visit (nor draw) methods on their children because the batch node takes over rendering of the children. Therefore any code you write in draw or visit methods of a CCSprite will have no effect when you sprite-batch the sprite.

How to draw line in topmost z-oder

I want to draw line connect two sprites, but i can not do that in this case:
//add Map Background
map = [CCTMXTiledMap tiledMapWithTMXFile:#"orthogonal-test-zorder.tmx"];
[self addChild:map z:1 tag:kTagTileMap];
//overite draw function
- (void)draw {
glColor4f(0.8, 1.0, 0.76, 1.0);
glEnable(GL_LINE_SMOOTH);
ccDrawLine( ccp(0, 0), ccp(150, 150) );
}
the line is not shown on screen, if i remove map background , it's shown.
I don't know Cocos2D so this is just wild guessing, but the z parameter of [self addChild:map z:1 tag:kTagTileMap]; seems to be a z offset. Since 1 would be in front of 0 (the likely default), your map would render in front of your line. Try [self addChild:map z:-1 tag:kTagTileMap];
I've encounted same problem 2 days ago.
My solution is to make a seperate MyLineLayer which only draw the line. Then you can set the z-index of the CCLayer to the top.
I think that using an enveloped MyLineLayer would be better than make the z-index of the tile map smaller.

Cocos2d iPhone - Sprite cliping/mask/frame

how can i clip/crop/mask or just set the frame of a CCSprite in Cocos2D?
Something similar to:
setting the frame for UIView, with clipping subviews = TRUE
My CCSprite Main Sprite have multiple Child Sprite added to it.
I only want Mask part of that Main Sprite Sprite visible.
Is there a way to clip or use a mask for CCSprite?
I could cut the background and layer that on top, leaving only that visible area, but is that the only way?!
here's a sample image demonstrating what I'm trying to achieve:
(source: dnamique.com)
I ended up using GL_SCISSOR.
in MainSprite I impemented:
- (void) visit
{
if (!self.visible) {
return;
}
glEnable(GL_SCISSOR_TEST);
glScissor(x, y, width, height);
[super visit];
glDisable(GL_SCISSOR_TEST);
}
This will clip or mask the specified area.
The only tricky bit is that in Landscape mode Cocos2D has 0,0 at the bottom-left side of the screen, while OpenGL has it at the bottom-right corner as it doesn't consider the orientation of the screen.
In other words, for OpenGL consider you have a rotated portrait Screen.
I wrote a ClippingNode class which does exactly that. You can add other nodes (sprites, labels, etc.) to the ClippingNode and they will only be drawn in the region specified by the ClippingNode. It also takes device rotation into account.
Internally it uses GL_SCISSOR_TEST like in Bach's answer.
http://www.learn-cocos2d.com/2011/01/cocos2d-gem-clippingnode/
I tried using Steffen Itterheim's ClippingNode, but was unable to get to work in a sufficiently robust
enough fashion for my needs.
Believe it or not, the below code works fairly well and should be code complete. It handles device orientation changes, anchorPoint, position, scale (scaleX, scaleY). For cocos2d v2, you may just need to
comment out the glPushMatrix and glPopMatrix calls..
To use, simply set the position and contentSize properties and add the child/children you want clipped to this ClippingNode instance. The contentSize property is used to define the dimensions of the clipping region.
example of usage:
ClippingNode *clipNode = [[ClippingNode alloc] init];
clipNode.anchorPoint = ccp(0.5f, 0);
clipNode.position = ccp(100, 25);
clipNode.contentSize = CGSizeMake(120, 120);
// add clipNode to your node hierarchy.
[parentNode addChild:clipNode];
// add one or more children to your clipNode:
[clipNode addChild:child1];
// ClippingNode.h
// CC0 - (public domain. Use in anyway you see fit.)
// No warranty of any kind is expressed or implied.
//
// by UChin Kim.
//
// the caller can simply set the regular cocos2d
// properties: position and contentSize to define the clipping region implicitly (i.e. the
// position and contentSize of the ClippingNode is the clipping region to be used).
// as an added bonus, position seems to work as expected (relative to parent node, instead of
// requiring absolute positioning).
//
// also, anchorPoint and scale properties seem to work as expected as well..
// no special code is neeed to handle device orientation changes correctly..
//
// To visually see exactly what is being clipped, set the following #define
// #define SHOW_CLIPPED_REGION_IN_LIGHT_RED 1
//
#import "cocos2d.h"
#interface ClippingNode : CCNode
#end
//
// ClippingNode.m
//
#import "ClippingNode.h"
#implementation ClippingNode
-(void) visit
{
CGPoint worldOrg = [self convertToWorldSpace:ccp(0, 0)];
CGPoint dest = [self convertToWorldSpace:ccp(self.contentSize.width, self.contentSize.height)];
CGPoint dims = ccpSub(dest, worldOrg);
glPushMatrix();
glEnable(GL_SCISSOR_TEST);
glScissor(worldOrg.x, worldOrg.y, dims.x, dims.y);
#if SHOW_CLIPPED_REGION_IN_LIGHT_RED
glColor4ub(64, 0, 0, 128);
ccDrawSolidRect(ccp(0, 0), ccp(1024, 1024));
#endif
[super visit];
glDisable(GL_SCISSOR_TEST);
glPopMatrix();
}
#end