How to use openGL line drawing in cocos2d - cocos2d-iphone

I like to draw a line, but cocos2d inside ccDrawLine serrate, how to draw a blurring of the line, who can help me?
ccDrawLine( ccp(StartP.x, StartP.y), ccp(EndP.x, EndP.y) );

I did not use ccDrawLine but I created a line with a Sprite and I animated it (see code below). In this way I was able to use custom line images made (but nevermind. that's how I did).
If you want to stick to primitives I guess you should set the opacity of the line primitive (see this post which explains how) and then you could create a sequence of action that set the opacity level (e.g. start with opacity at 100%, then 75%, then back to 100% like I did with my images but using the method in the link above) to get the blurring effect..
Code using images:
CCSprite * string = [self getChildByTag:tag];
[string setOpacity:100];
NSMutableArray* frames = [[NSMutableArray alloc]initWithCapacity:3];
NSString*lineFrame = [NSString stringWithString:#"line0.png"];
CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:lineFrame];
[frames addObject:frame];
lineFrame = [NSString stringWithString:#"line1.png"];
frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:lineFrame];
[frames addObject:frame];
lineFrame = [NSString stringWithString:#"line0.png"];
frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:lineFrame];
[frames addObject:frame];
CCAnimation* anim = [CCAnimation animationWithFrames:frames delay:0.1f];
CCAnimate* animate = [CCAnimate actionWithAnimation:anim];
//CCRepeatForever* repeat = [CCRepeatForever actionWithAction:animate];
[string runAction:animate];
[string setOpacity:75];
Hope that this helps..

#import "cocos2d.h"
#interface CClineSprite : CCLayer
{
CCRenderTexture * renderTarget;
NSMutableArray * pathArray;
CCSprite * pathBrush;
CGPoint prePosition;
}
-(void)setLinePosition:(CGPoint)position;
-(void)setLineOpacity:(GLubyte) anOpacity;
-(void)setLineScale:(float) scale;
-(void)setLineColor:(ccColor3B) color;
#end
#import "CClineSprite.h"
#implementation CClineSprite
- (id) init
{
self = [super init];
if (self)
{
CGSize s = [[CCDirector sharedDirector] winSize];
renderTarget = [CCRenderTexture renderTextureWithWidth:s.width height:s.height];
[renderTarget setPosition:ccp(s.width/2, s.height/2)];
[self addChild:renderTarget z:1];
pathBrush = [CCSprite spriteWithFile:#"dots.png"];
pathBrush.color = ccWHITE;
[pathBrush setOpacity:100];
[pathBrush setScale:0.5];
pathArray = [[NSMutableArray alloc]init];
}
return self;
}
-(void)setLineOpacity:(GLubyte) anOpacity
{
[pathBrush setOpacity:anOpacity];
}
-(void)setLineScale:(float) scale
{
[pathBrush setScale:scale];
}
-(void)setLineColor:(ccColor3B) color
{
[pathBrush setColor:color];
}
-(void)setLinePosition:(CGPoint)position
{
[pathArray addObject:[NSValue valueWithCGPoint:position]];
[self renderPath];
}
- (void) renderPath
{
[renderTarget clear:0 g:0 b:0 a:0];
[renderTarget begin];
for (int i = 0; i < pathArray.count-1;i++)
{
CGPoint pt1;
CGPoint pt2;
[[pathArray objectAtIndex:i] getValue: &pt1];
[[pathArray objectAtIndex:i + 1] getValue: &pt2];
float distance = ccpDistance(pt1, pt2);
if (distance > 1)
{
int d = (int)distance;
for (int i = 0; i < d; i += 10)
{
float difx = pt2.x - pt1.x;
float dify = pt2.y - pt1.y;
float delta = (float)i / distance;
[pathBrush setPosition:ccp(pt1.x + (difx * delta), pt1.y + (dify * delta))];
[pathBrush visit];
}
}
}
[renderTarget end];
}
#end
USAGE:
CClineSprite* streak = [CClineSprite node];
[self addChild:streak z:999];
[streak setLinePosition:associatedTurtleObject.position];
[streak setLineScale:0.4];
To dynamically update or extend the line just use
[streak setLinePosition:ccp(x,y)];
Hope this will give you more flexibility for use

Related

How to move a CCSprite inside CCNode In cocos2d

I have a Bird which is a (CCSprite) and i want it to be flying in the Top of screen off course, So i add CCNode and add the bird as child on it but it's still flying outside of the CCNode!
MY CODE IS:
#implementation GamePlay {
CCSprite *myBird;
CCNode *theFlyingArea;
}
-(id)init {
if(self=[super init]) {
theFlyingArea = [CCNodeColor nodeWithColor:[CCColor colorWithRed:0.2f green:0.2f blue:0.2f alpha:0.4f] width:viewSize.width height:viewSize.height*60/100];
theFlyingArea.position = ccp(0, 0);
theFlyingArea.positionType = CCPositionTypeMake(CCPositionUnitPoints, CCPositionUnitPoints, CCPositionReferenceCornerTopLeft);
theFlyingArea.anchorPoint = ccp(0, 1);
[self addChild:theFlyingArea z:1];
float RandomY = arc4random() % (int)theFlyingArea.contentSize.height;
// Create MY BIRD
myBird = [CCSprite spriteWithImageNamed:#"myBird.png"];
myBird.scale = myBird.scale / 2;
myBird.position = ccp(-10, RandomY);
[theFlyingArea addChild:myBird z:2];
}
return self;
}
- (void)update:(CCTime)delta {
id moveTo = [CCActionMoveTo actionWithDuration:100.f position:ccp(((float)rand() / RAND_MAX) * theFlyingArea.contentSize.width , ((float)rand() / RAND_MAX) * theFlyingArea.contentSize.height)];
[myBird runAction:moveTo];
}
#end
Thank you very much.

'Signature not found for selector - does it have the following form? -(void) name: (ccTime) dt' - cocos2d

I keep getting the same crash report "'Signature not found for selector - does it have the following form? -(void) name: (ccTime) dt'".
This is getting pretty annoying now, i only wanted to make the Backgound scrolling infinitely.
Here is my code:
-(id) init {
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
self.isTouchEnabled = YES;
self.isAccelerometerEnabled = YES;
CGSize screenSize = [CCDirector sharedDirector].winSize;
/*CCSprite *Player = [CCSprite spriteWithFile:#"Icon.png"];
Player.position = ccp(screenSize.width/2 -110, screenSize.height/2);
Player.scale = 0.7;
[self addChild:Player];
[self scheduleUpdate];*/
CCLOG(#"Screen width %0.2f screen height %0.2f",screenSize.width,screenSize.height);
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
bool doSleep = true;
world = new b2World(gravity, doSleep);
world->SetContinuousPhysics(true);
// Debug Draw functions
/*m_debugDraw = new GLESDebugDraw( PTM_RATIO );
world->SetDebugDraw(m_debugDraw);
uint32 flags = 0;
flags += b2DebugDraw::e_shapeBit;
//flags += b2DebugDraw::e_jointBit;
//flags += b2DebugDraw::e_aabbBit;
//flags += b2DebugDraw::e_pairBit;
//flags += b2DebugDraw::e_centerOfMassBit;
m_debugDraw->SetFlags(flags);*/
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); // bottom-left corner
// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
b2Body* groundBody = world->CreateBody(&groundBodyDef);
// Define the ground box shape.
b2PolygonShape groundBox;
// bottom
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
// top
groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox,0);
//Set up sprite
BG1 = [CCSprite spriteWithFile:#"SkyAndClouds.png"];
BG1.anchorPoint = ccp(0,0);
BG1.position = ccp(0, 0);
BG1.scale = 0.5;
[self addChild:BG1];
BG2 = [CCSprite spriteWithFile:#"SkyAndClouds.png"];
BG2.anchorPoint = ccp(0,0);
BG2.position = ccp([BG1 boundingBox].size.width-1,0);
[self addChild:BG2];
[self schedule: #selector(AnimatedBG:) interval:0];
CCSpriteBatchNode *batch = [CCSpriteBatchNode batchNodeWithFile:#"Stick.png" capacity:150];
[self addChild:batch z:0 tag:kTagBatchNode];
[self addNewSpriteWithCoords:ccp(screenSize.width/2-110, screenSize.height/2)];
[self addZombieWithCoords:ccp(screenSize.width/2+240, screenSize.height/2)];
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Stick, you Better Run!" fontName:#"Helvetica" fontSize:16];
[self addChild:label z:0];
[label setColor:ccc3(0,0,255)];
label.position = ccp( screenSize.width/2, screenSize.height-17);
[self schedule: #selector(tick:)];
}
return self;
}
-(void)AnimatedBG {
BG1.position = ccp(BG1.position.x-1,BG1.position.y);
}
Your AnimatedBG method should be defined as:
-(void) AnimatedBG:(ccTime)dt {
}
You should also make sure you have a definition for your scheduled tick: method:
-(void) tick:(ccTime)dt {
}

EXC_BAD_ACCESS while using box2d GetUserData and SetUserData

I've been straggling with this problem for a few days now and I'm desperate for your help. As I've been following Ray Wenderlich's tutorials and I still get EXC_BAD_ACCESS error when I'm trying to getUserData from b2Body inside a ContactListener
so WHContactListener.h:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"
class WHContactListener : public b2ContactListener
{
private:
void BeginContact(b2Contact* contact);
void EndContact(b2Contact* contact);
};
WHContactListener.mm
#import "WHContactListener.h"
#import "cocos2d.h"
#import "ExSprite.h"
void WHContactListener::BeginContact(b2Contact *contact)
{
b2Body *bodyA = contact->GetFixtureA()->GetBody();
b2Body *bodyB = contact->GetFixtureB()->GetBody();
/*The problem occurs here, while it returns a non-null value it just crashes when I implement any method here, such as NSLog */
ExSprite *spriteB = (ExSprite*)bodyB->GetUserData();
NSLog(#"output %#", spriteB);
}
void WHContactListener::EndContact(b2Contact *contact)
{
}
Sprite class ExSprite.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"
#interface ExSprite : CCSprite {
BOOL _exploded;
b2Body *_explBody;
}
#property (nonatomic, readwrite) BOOL exploded;
#property (nonatomic, assign) b2Body *explBody;
-(void)setPhysicsBody:(b2Body*)body;
#end
ExSprite.mm
#import "ExSprite.h"
#import "HelloWorldLayer.h"
#pragma mark - ExSprite
#implementation ExSprite
#synthesize exploded = _exploded;
#synthesize explBody = _explBody;
-(void)setPhysicsBody:(b2Body *)body
{
_explBody = body;
_explBody->SetUserData(self);
}
// this method will only get called if the sprite is batched.
// return YES if the physics values (angles, position ) changed
// If you return NO, then nodeToParentTransform won't be called.
-(BOOL) dirty
{
return YES;
}
// returns the transform matrix according the Chipmunk Body values
-(CGAffineTransform) nodeToParentTransform
{
b2Vec2 pos = _explBody->GetPosition();
float x = pos.x * PTM_RATIO;
float y = pos.y * PTM_RATIO;
if ( ignoreAnchorPointForPosition_ ) {
x += anchorPointInPoints_.x;
y += anchorPointInPoints_.y;
}
// Make matrix
float radians = _explBody->GetAngle();
float c = cosf(radians);
float s = sinf(radians);
if( ! CGPointEqualToPoint(anchorPointInPoints_, CGPointZero) ){
x += c*-anchorPointInPoints_.x + -s*-anchorPointInPoints_.y;
y += s*-anchorPointInPoints_.x + c*-anchorPointInPoints_.y;
}
// Rot, Translate Matrix
transform_ = CGAffineTransformMake( c, s,
-s, c,
x, y );
return transform_;
}
-(void) dealloc
{
//
[super dealloc];
}
#end
Finally the layer that I'm using HelloWorld.h
#import <GameKit/GameKit.h>
// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
#import "Box2D.h"
#import "GLES-Render.h"
#import "WHContactListener.h"
//Pixel to metres ratio. Box2D uses metres as the unit for measurement.
//This ratio defines how many pixels correspond to 1 Box2D "metre"
//Box2D is optimized for objects of 1x1 metre therefore it makes sense
//to define the ratio so that your most common object type is 1x1 metre.
#define PTM_RATIO 32
// HelloWorldLayer
#interface HelloWorldLayer : CCLayer <GKAchievementViewControllerDelegate, GKLeaderboardViewControllerDelegate>
{
CCTexture2D *spriteTexture_; // weak ref
b2World* world; // strong ref
GLESDebugDraw *m_debugDraw; // strong ref
WHContactListener* contactListener;
CCRenderTexture *target;
CCSprite *brush;
b2Body *groundBody;
NSMutableArray *shapeVert;
}
#property (nonatomic, readwrite)b2Body *groundBody;
#property (nonatomic, assign)NSMutableArray *shapeVert;
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
#end
and HelloWorld.mm
#import "HelloWorldLayer.h"
// Needed to obtain the Navigation Controller
#import "AppDelegate.h"
#import "PhysicsSprite.h"
#import "ExSprite.h"
enum {
kTagParentNode = 1,
};
#pragma mark - HelloWorldLayer
#interface HelloWorldLayer()
-(void) initPhysics;
-(void) addNewSpriteAtPosition:(CGPoint)p;
#end
#implementation HelloWorldLayer
#synthesize groundBody = groundBody_;
#synthesize shapeVert = shapeVert_;
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(id) init
{
if( (self=[super init])) {
// enable events
self.isTouchEnabled = YES;
self.isAccelerometerEnabled = YES;
CGSize s = [CCDirector sharedDirector].winSize;
// init physics
[self initPhysics];
target = [CCRenderTexture renderTextureWithWidth:s.width height:s.height pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
[target retain];
[target setPosition:ccp(s.width/2, s.height/2)];
[self addChild:target z:0];
brush = [CCSprite spriteWithFile:#"bird.png"];
[brush retain];
[self scheduleUpdate];
}
return self;
}
-(void) dealloc
{
delete world;
world = NULL;
delete contactListener;
contactListener = NULL;
delete m_debugDraw;
m_debugDraw = NULL;
[super dealloc];
}
-(void) initPhysics
{
CGSize s = [[CCDirector sharedDirector] winSize];
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
world = new b2World(gravity);
// Do we want to let bodies sleep?
world->SetAllowSleeping(true);
world->SetContinuousPhysics(true);
contactListener = new WHContactListener();
world->SetContactListener(contactListener);
m_debugDraw = new GLESDebugDraw( PTM_RATIO );
world->SetDebugDraw(m_debugDraw);
uint32 flags = 0;
flags += b2Draw::e_shapeBit;
// flags += b2Draw::e_jointBit;
// flags += b2Draw::e_aabbBit;
// flags += b2Draw::e_pairBit;
// flags += b2Draw::e_centerOfMassBit;
m_debugDraw->SetFlags(flags);
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(s.width/2/PTM_RATIO, s.height/2/PTM_RATIO); // bottom-left corner
//SET USERDATA
//groundBodyDef.userData = groundBody;
// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
groundBody = world->CreateBody(&groundBodyDef);
// Define the ground box shape.
b2EdgeShape groundBox;
float32 x1 = 2*cosf(0.0f * b2_pi/180);
float32 y1 = 2*sinf(0.0f * b2_pi/180);
for(int32 i=1; i<=18; i++){
float32 x2 = 2*cosf((i*20) * b2_pi / 180);
float32 y2 = 2*sinf((i*20) * b2_pi / 180);
groundBox.Set(b2Vec2(x1,y1), b2Vec2(x2,y2));
groundBody->CreateFixture(&groundBox, 0);
x1 = x2;
y1 = y2;
}
}
-(void) draw
{
//
// IMPORTANT:
// This is only for debug purposes
// It is recommend to disable it
//
[super draw];
ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );
kmGLPushMatrix();
world->DrawDebugData();
kmGLPopMatrix();
}
-(void) addNewSpriteAtPosition:(CGPoint)p
{
//CCLOG(#"Add sprite %0.2f x %02.f",p.x,p.y);
CCNode *parent = [self getChildByTag:kTagParentNode];
//We have a 64x64 sprite sheet with 4 different 32x32 images. The following code is
//just randomly picking one of the images
int idx = (CCRANDOM_0_1() > .5 ? 0:1);
int idy = (CCRANDOM_0_1() > .5 ? 0:1);
//PhysicsSprite *sprite = [PhysicsSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32 * idx,32 * idy,32,32)];
ExSprite *sprite = [ExSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32*idx, 32*idy, 32, 32)];
[parent addChild:sprite];
sprite.position = ccp( p.x, p.y);
// Define the dynamic body.
//Set up a 1m squared box in the physics world
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
//USERDATA set
//bodyDef.userData = sprite;
b2Body *body = world->CreateBody(&bodyDef);
// Define another box shape for our dynamic body.
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box
// Define the dynamic body fixture.
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.restitution = 0.0f;
//fixtureDef.density = rand() * 20.0f;
fixtureDef.friction = 0.3f;
body->CreateFixture(&fixtureDef);
[sprite setPhysicsBody:body];
//NSLog(#"here %#", body->GetUserData());
}
-(void) update: (ccTime) dt
{
//It is recommended that a fixed time step is used with Box2D for stability
//of the simulation, however, we are using a variable time step here.
//You need to make an informed choice, the following URL is useful
//http://gafferongames.com/game-physics/fix-your-timestep/
int32 velocityIterations = 8;
int32 positionIterations = 1;
// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
world->Step(dt, velocityIterations, positionIterations);
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//Add a new body/atlas sprite at the touched location
for( UITouch *touch in touches ) {
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
[self addNewSpriteAtPosition: location];
}
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
/*UITouch *touch = (UITouch*)touches.anyObject;
CGPoint start = [touch locationInView:[touch view]];
start = [[CCDirector sharedDirector] convertToGL:(start)];
CGPoint end = [touch previousLocationInView:[touch view]];
end = [[CCDirector sharedDirector] convertToGL:end];
[target begin];
float distance = ccpDistance(start, end);
for(int i =0; i < distance; i++){
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float)i / distance;
[brush setPosition:ccp(start.x + (difx*delta), start.y + (dify * delta))];
[brush visit];
}
[target end];
*/
}
#pragma mark GameKit delegate
-(void) achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
#end
Any help will be appreciated on the matter.
I've found the problem. It seems that I've left CCNode undeclared, and when I attach body with the UserData to the unavailable CCNode, it then crashes when I retrieve it in the contactlistener class

Outline (stroke) for non-rectangular CCNode in cocos2d

I need to create an outline like this dynamically:
Not for a CCSprite, but for multiple animated CCSprites united in one CCNode. I'm thinking about:
copying CCNode's content to a texture (like canvasBitmapData.draw(sourceDisplayObject) in AS3)
creating CCSprite with the resulting texture
tinting the sprite to outline color and scaling it up a bit
placing the sprite behind other sprites in the node
I have no idea how to perform step 1. And maybe it is faster to draw "true stroke" around the texture's opaque pixels instead of tint-scale in step 3?
I totally forgot to post an answer for this question. Here's the code for a very smooth stroke. It's not fast but worked great for a couple of big sprites on the first iPad.
The idea is to draw tiny colored and blurred balls around the sprite and place them onto their own texture. It can be used both for CCNode and CCSprite. The code also shifts anchor points because the resulting sprites will have a bit larger width and height.
Resulting outline (body and 2 hands, about 0.3s on iPad1):
White balls examples:
5f: http://i.stack.imgur.com/e9kos.png
10f: http://i.stack.imgur.com/S5goU.png
20f: http://i.stack.imgur.com/qk7GL.png
CCNode category, for Cocos2d-iPhone 2.1:
#implementation CCNode (Outline)
- (CCSprite*) outline
{
return [self outlineRect:CGRectMake(0, 0, self.contentSize.width, self.contentSize.height)];
}
- (CCSprite*) outlineRect:(CGRect)rect
{
NSInteger gap = dscale(4);
CGPoint positionShift = ccp(gap - rect.origin.x, gap - rect.origin.y);
CGSize canvasSize = CGSizeMake(rect.size.width + gap * 2, rect.size.height + gap * 2);
CCRenderTexture* renderedSpriteTexture = [self renderTextureFrom:self shiftedFor:positionShift onCanvasSized:canvasSize];
CGSize textureSize = renderedSpriteTexture.sprite.contentSize;
CGSize textureSizeInPixels = renderedSpriteTexture.sprite.texture.contentSizeInPixels;
NSInteger bitsPerComponent = 8;
NSInteger bytesPerPixel = (bitsPerComponent * 4) / 8;
NSInteger bytesPerRow = bytesPerPixel * textureSizeInPixels.width;
NSInteger myDataLength = bytesPerRow * textureSizeInPixels.height;
NSMutableData* buffer = [[NSMutableData alloc] initWithCapacity:myDataLength];
Byte* bytes = (Byte*)[buffer mutableBytes];
[renderedSpriteTexture begin];
glReadPixels(0, 0, textureSizeInPixels.width, textureSizeInPixels.height, GL_RGBA, GL_UNSIGNED_BYTE, bytes);
[renderedSpriteTexture end];
//SEE ATTACHMENT TO GET THE FILES
NSString* spriteFrameName;
if (IS_IPAD) spriteFrameName = (CC_CONTENT_SCALE_FACTOR() == 1) ? #"10f.png" : #"20f.png";
else spriteFrameName = (CC_CONTENT_SCALE_FACTOR() == 1) ? #"5f.png" : #"10f.png";
CCSprite* circle = [CCSprite spriteWithSpriteFrameName:spriteFrameName];
circle.anchorPoint = ccp(0.48, 0.48);
float retinaScale = (CC_CONTENT_SCALE_FACTOR() == 1) ? 1.0 : 0.5;
CCRenderTexture* strokeTexture = [CCRenderTexture renderTextureWithWidth:textureSize.width height:textureSize.height pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
[strokeTexture beginWithClear:0 g:0 b:0 a:0];
for (NSInteger x = 0; x < textureSizeInPixels.width; x++)
{
for (NSInteger y = 0; y < textureSizeInPixels.height; y++)
{
NSInteger idx = y * bytesPerRow + x * bytesPerPixel + 3;
NSInteger w = 1;
if (bytes[idx] <= 254)
{
BOOL shouldBeStroked = NO;
for (NSInteger nx = -w; nx <= w; nx++)
{
for (NSInteger ny = -w; ny <= w; ny++)
{
if (x + nx < 0 || y + ny < 0 || x + nx >= textureSizeInPixels.width || y + ny >= textureSizeInPixels.height)
continue;
if (bytes[idx + nx * bytesPerPixel + ny * bytesPerRow] == 255)
{
shouldBeStroked = YES;
break;
}
}
}
if (shouldBeStroked == YES)
{
circle.position = ccp(x * retinaScale, y * retinaScale);
[circle visit];
}
}
}
}
[strokeTexture end];
CCSprite* resultSprite = [CCSprite spriteWithTexture:strokeTexture.sprite.texture];
[resultSprite.texture setAntiAliasTexParameters];
resultSprite.flipY = YES;
if ([self isKindOfClass:[CCSprite class]]) {
CGPoint oldAnchorInPixels = ccp(roundf(self.contentSize.width * self.anchorPoint.x), roundf(self.contentSize.height * self.anchorPoint.y));
resultSprite.anchorPoint = ccp((oldAnchorInPixels.x + gap) / resultSprite.contentSize.width, (oldAnchorInPixels.y + gap) / resultSprite.contentSize.height);
resultSprite.position = self.position;
} else { //CCNode
resultSprite.anchorPoint = CGPointZero;
resultSprite.position = ccpAdd(self.position, ccp(rect.origin.x - gap, rect.origin.y - gap));
}
return resultSprite;
}
- (CCRenderTexture*) renderTextureFrom:(CCNode*)node shiftedFor:(CGPoint)posShift onCanvasSized:(CGSize)size
{
SoftAssertion(!CGSizeEqualToSize(size, CGSizeZero), #"node has zero size");
BOOL isSprite = [node isMemberOfClass:[CCSprite class]];
CGPoint apSave = node.anchorPoint;
CGPoint posSave = node.position;
BOOL wasVisible = node.visible;
CCRenderTexture* rtx = [CCRenderTexture renderTextureWithWidth:size.width
height:size.height
pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
[rtx beginWithClear:0 g:0 b:0 a:0];
node.anchorPoint = CGPointZero;
node.position = posShift;
node.visible = YES;
if (isSprite) [node visit];
else [[self cloneCCNode:node] visit];
node.anchorPoint = apSave;
node.position = posSave;
node.visible = wasVisible;
[rtx end];
return rtx;
}
- (CCNode*) cloneCCNode:(CCNode*)source
{
CCNode* clone = [CCNode node];
void (^copyCCNodeProperties)(CCNode*, CCNode*) = ^(CCNode* source, CCNode* clone)
{
clone.visible = source.visible;
clone.rotation = source.rotation;
clone.position = source.position;
clone.anchorPoint = source.anchorPoint;
clone.zOrder = source.zOrder;
clone.tag = source.tag;
};
for (CCNode* srcSubnode in source.children) {
CCNode* subNode;
if ([srcSubnode isMemberOfClass:[CCSprite class]]) {
CCSprite* srcSprite = (CCSprite*)srcSubnode;
subNode = [CCSprite spriteWithTexture:srcSprite.texture];
CCSprite* subSprite = (CCSprite*)subNode;
subSprite.flipX = srcSprite.flipX;
subSprite.flipY = srcSprite.flipY;
subSprite.displayFrame = srcSprite.displayFrame;
subSprite.opacity = srcSprite.opacity;
}
else if ([srcSubnode isMemberOfClass:[CCLabelTTF class]]) {
CCLabelTTF* srcLabel = (CCLabelTTF*)srcSubnode;
subNode = [CCLabelTTF labelWithString:srcLabel.string fontName:srcLabel.fontName fontSize:srcLabel.fontSize dimensions:srcLabel.dimensions hAlignment:srcLabel.horizontalAlignment vAlignment:srcLabel.verticalAlignment];
CCSprite* subLabel = (CCSprite*)subNode;
subLabel.flipX = srcLabel.flipX;
subLabel.flipY = srcLabel.flipY;
subLabel.color = srcLabel.color;
}
else {
subNode = [self cloneCCNode:srcSubnode];
}
copyCCNodeProperties(srcSubnode, subNode);
[clone addChild:subNode];
}
copyCCNodeProperties(source, clone);
return clone;
}
I have a general purpose function I built up from various sources (that I'm ashamed to say I can't reference here). What it does is take a CCSprite, create a stroke that you can put behind it and return in a CCRenderTexture. If the sprite passed in has children (as yours might) I see no reason why it wouldn't do what you want, but I haven't tried.
Here it is in case it works:
#implementation Cocosutil
+(CCRenderTexture*) createStrokeForSprite:(CCSprite*)sprite size:(float)size color:(ccColor3B)cor
{
CCRenderTexture* rt = [CCRenderTexture renderTextureWithWidth:sprite.texture.contentSize.width+size*2 height:sprite.texture.contentSize.height+size*2];
CGPoint originalPos = [sprite position];
ccColor3B originalColor = [sprite color];
BOOL originalVisibility = [sprite visible];
[sprite setColor:cor];
[sprite setVisible:YES];
ccBlendFunc originalBlend = [sprite blendFunc];
[sprite setBlendFunc:(ccBlendFunc) { GL_SRC_ALPHA, GL_ONE }];
CGPoint bottomLeft = ccp(sprite.texture.contentSize.width * sprite.anchorPoint.x + size, sprite.texture.contentSize.height * sprite.anchorPoint.y + size);
CGPoint positionOffset = ccp(sprite.texture.contentSize.width * sprite.anchorPoint.x - sprite.texture.contentSize.width/2,sprite.texture.contentSize.height * sprite.anchorPoint.y - sprite.texture.contentSize.height/2);
CGPoint position = ccpSub(originalPos, positionOffset);
[rt begin];
for (int i=0; i<360; i+=30)
{
[sprite setPosition:ccp(bottomLeft.x + sin(CC_DEGREES_TO_RADIANS(i))*size, bottomLeft.y + cos(CC_DEGREES_TO_RADIANS(i))*size)];
[sprite visit];
}
[rt end];
[sprite setPosition:originalPos];
[sprite setColor:originalColor];
[sprite setBlendFunc:originalBlend];
[sprite setVisible:originalVisibility];
[rt setPosition:position];
return rt;
}
#end
and here is code where I use it:
- (id) initWithSprite:(CCSprite*)sprite color:(ccColor3B)color strokeSize:(float)strokeSize strokeColor:(ccColor3B)strokeColor {
self = [super init];
if (self != nil) {
strokeColor_ = strokeColor;
strokeSize_ = strokeSize;
CCRenderTexture *stroke = [CocosUtil createStrokeForSprite:sprite size:strokeSize color:strokeColor];
[self addChild:stroke z:kZStroke tag:kStroke];
[self addChild:sprite z:kZLabel tag:kLabel];
[self setContentSize:[sprite contentSize]];
}
return self;
}

Cocos2d Sprites Collision

I have multiple sprites placed onto a background sprite like this:
//my background
CCSprite *bg = [CCSprite spriteWithFile:#"imageName.png"];
[self addchild:bg];
And then I add my items onto bg
//this is how i add my items
CCSprite *items = [CCSprite spriteWithFile:#"itemName.png"];
[bg addchild:items];
Oh and not forgetting my car sprite
//my car
CCSprite *car = [CCSprite spriteWithFile:#"car.png"];
[self addchild:car];
I use a loop to add multiple sprites onto the bg.
Now the question is how do I detect whether the car collided with the multiple sprites that I have placed onto the bg?
I've tried using CGRectIntersectsRect and it doesn't work.
I've tried using the pythagoras theorem method and once again it doesn't work.
There was a method which involved adding the items sprites into a NSMutableArray and it doesn't work either.
Can anyone suggest a method whereby I can try?
Additional code:
-(void) initializeCarAndItems
{
car = [CCSprite spriteWithFile:#"android.png"];
car.position = ccp(screenSize.width/2, screenSize.height * 0.30);
[self addChild:car z:1];
carRect = [car boundingBox];
}
-(void) initializeMap
{
bg1 = [CCSprite spriteWithFile:#"racingBG.png"];
bg1.anchorPoint = ccp(0, 0);
bg1.position = ccp(0, 0);
[self addChild:bg1 z:-1];
bg2 = [CCSprite spriteWithFile:#"racingBG2.png"];
bg2.anchorPoint = ccp(0,0);
bg2.position = ccp(0, bg1.boundingBox.size.height - 1);
[self addChild:bg2 z:-1];
convertedWidth = (int)bg1.boundingBox.size.width;
convertedHeight = (int)bg1.boundingBox.size.height;
for (y = 0; y < 15; y++)
{
positionX = arc4random()%convertedWidth;
positionY = arc4random()%convertedHeight;
items = [CCSprite spriteWithFile:#"item.png"];
items.position = ccp(positionX, positionY + 300);
[bg1 addChild:items z:100];
[itemsArray addObject:items];
}
for (y = 0; y < 15; y++)
{
positionX = arc4random()%convertedWidth;
positionY = arc4random()%convertedHeight;
items = [CCSprite spriteWithFile:#"item.png"];
items.position = ccp(positionX, positionY);
[bg2 addChild:items z:100];
[itemsArray addObject:items];
}
}
-(void) accelerate
{
bg1.position = ccp(0, bg1.position.y - accelerateNumber);
bg2.position = ccp(0, bg2.position.y - accelerateNumber);
if (bg1.position.y < -bg1.boundingBox.size.height)
{
questionCount++;
bg1.position = ccp(0, bg2.position.y + bg2.boundingBox.size.height - 1);
[self question];
[bg1 removeAllChildrenWithCleanup:YES];
for (y = 0; y < 15; y++)
{
positionY = arc4random()%convertedHeight;
positionX = arc4random()%convertedWidth;
items.position = ccp(positionX, positionY);
items = [CCSprite spriteWithFile:#"item.png"];
[bg1 addChild:items z:100];
[itemsArray addObject:items];
}
}
else if (bg2.position.y < -bg2.boundingBox.size.height)
{
questionCount++;
bg2.position = ccp(0, bg1.position.y + bg1.boundingBox.size.height - 1);
[self question];
[bg2 removeAllChildrenWithCleanup:YES];
for (y = 0; y < 15; y++)
{
positionY = arc4random()%convertedHeight;
positionX = arc4random()%convertedWidth;
items.position = ccp(positionX, positionY);
items = [CCSprite spriteWithFile:#"item.png"];
[bg2 addChild:items z:100];
[itemsArray addObject:items];
}
}
}
-(void) update:(ccTime)deltaTime
{
[self ifEdgeOfScreen];
[self accelerate];
for (CCSprite *itemFromArray in itemsArray)
{
CGRect itemRect = [itemFromArray boundingBox];
if (CGRectIntersectsRect(carRect, itemRect))
{
NSLog(#"Collision!");
}
}
if (leftButton.active == TRUE)
{
[self moveLeftRight:1];
}
else if (rightButton.active == TRUE)
{
[self moveLeftRight:2];
}
}
UPDATE:
It's fixed :)
-(void) update:(ccTime)deltaTime
{
car = [car boundingbox];
[self ifEdgeOfScreen];
[self accelerate];
for (CCSprite *itemFromArray in itemsArray)
{
if (CGRectIntersectsRect(carRect, [itemFromArray boundingbox]))
{
NSLog(#"Collision!");
}
}
if (leftButton.active == TRUE)
{
[self moveLeftRight:1];
}
else if (rightButton.active == TRUE)
{
[self moveLeftRight:2];
}
}
I found so many problems with the code....
When you call removeAllChildren.. Make sure you also remove objects from array.. Removing sprite from parents does not remove it from array.
update the car rect in update Method. So in your update method
-(void) update:(ccTime)deltaTime
{
[self ifEdgeOfScreen];
[self accelerate];
carRect = [car boundingBox];
...........
}
Hope this helps.. :)