i create a sprite object which sets a CCSprite as its visual
//shapes.h
#interface Shapes : CCSprite
{
CCSprite *mySprite;
GamePlayLayerShapes *theGame;
NSInteger shapeID;
}
#property (nonatomic, retain) CCSprite *mySprite;
#property (nonatomic, retain) GamePlayLayerShapes *theGame;
#property (nonatomic, readwrite) NSInteger shapeID;
#end
then in shapes.m i initialize the object
//shapes.m
#implementation Shapes
#synthesize mySprite, theGame, active, shapeID;
-(id) initWithGame:(GamePlayLayerShapes *)game andFile:(NSString *)file andNumber: (NSInteger)number andZ:(NSInteger)z
{
self = [super init];
if(self != nil)
{
self.theGame = game;
if (number > 25)
{
mySprite = [CCSprite spriteWithFile:file];
}
else
{
mySprite = [CCSprite spriteWithSpriteFrameName:file];
}
[theGame addChild:mySprite z:z];
[mySprite setPosition:ccp(500, 200)];
self.shapeID = number;
}
return self;
}
then in gameplaylayershapes.h i set up two NSMutableArray (i didn't add all irrelevant variables.
#interface GamePlayLayerShapes : CCLayer
{
CCSpriteBatchNode *shapesSpriteBatchNode;
NSMutableArray *shapeArray;
NSMutableArray *targetShapeArray;
Shapes *newShape;
}
then i set it all up in gameplaylayershapes.m
-(id) init
{
self = [super init];
if(self != nil)
{
srandom(time(NULL));
shapeArray = [[NSMutableArray alloc] init];
targetShapeArray = [[NSMutableArray alloc] init];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"GameplayShapes.plist"];
shapesSpriteBatchNode = [CCSpriteBatchNode batchNodeWithFile:#"GameplayShapes.png"];
[self addChild:shapesSpriteBatchNode];
[self createTargetShape];
[self runGame];
}
return self;
}
-(void)createTargetShape
{
//fills 4 variables with random numbers between 1 and 25
targetShape1 = (arc4random() % 25) +1;
targetShape2 = (arc4random() % 25) +1;
targetShape3 = (arc4random() % 25) +1;
targetShape4 = (arc4random() % 25) +1;
for (int x=1; x<5; x++)
{
switch (x)
{
case 1:
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape1];
shapeNumber = targetShape1;
break;
case 2:
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape2];
shapeNumber = targetShape2;
break;
case 3:
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape3];
shapeNumber = targetShape3;
break;
case 4:
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape4];
shapeNumber = targetShape4;
break;
default:
break;
}
newShape = [[Shapes alloc] initWithGame:self andFile:shapeFileName andNumber:shapeNumber andZ:10];
[targetShapeArray addObject:newShape];
[newShape release];
}
}
now what I'm trying to do is change the shapeID and mySprite for each shape object in targetshapearray. i can get the shapeID to change, but i cant for the life of me get the mySprite image to change which would change the appearance of it on my game. i keep getting a EXC_BAD_ACCESS error. Any Help would be appreciated
-(void)createNewTargetShapes
{
//fills 4 variables with random numbers between 1 and 25
targetShape1 = (arc4random() % 25) +1;
targetShape2 = (arc4random() % 25) +1;
targetShape3 = (arc4random() % 25) +1;
targetShape4 = (arc4random() % 25) +1;
for (int x=0; x<4; x++)
{
Shapes * n = (Shapes *) [targetShapeArray objectAtIndex:x];
switch (x)
{
case 0:
n.shapeID = targetShape1;
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape1];
n.mySprite = [CCSprite spriteWithFile:shapeFileName];
break;
case 1:
n.shapeID = targetShape2;
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape2];
n.mySprite = [CCSprite spriteWithFile:shapeFileName];
break;
case 2:
n.shapeID = targetShape3;
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape3];
n.mySprite = [CCSprite spriteWithFile:shapeFileName];
break;
case 3:
n.shapeID = targetShape4;
shapeFileName = [NSString stringWithFormat:#"shape%i.png",targetShape4];
n.mySprite = [CCSprite spriteWithFile:shapeFileName];
break;
default:
break;
}
}
}
To change a CCSprites image you must change its texture. Here is an example block of code for how to do that:
CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:#"spriteimagefile.png"];
[sprite setTexture: tex];
This will change the sprites image to "spriteimagefile.png".
Use the above code instead of
n.mySprite = [CCSprite spriteWithFile:shapeFileName];
I hope that's what you're looking for!
Related
Thank you for taking the time to look at my question, As I am very new to programming any help or pointers in the right direction is much appreciated.
Overview...
I have a simple game I am trying to create and it includes a class for 'Ball' sprite that is fired from the left of the screen to the right. The speed at which this "Ball" sprite is created is random and each time the "Ball" moves past the right of the screen a point is added to the score.
Problem....
The problem I have is when 2 "Balls" fire at almost the same time (which happens some times as its random) they pass the right hand of the screen less then a second apart and when this happens it only seems to add 1 when it should be adding 2 to the score (as 2 balls has passed the right side).
Below is the code for my Ball launcher class (use this class to call the ball randomly).
If you have used or gone through 'Ray Wenderlichs' Learn Cocos2D then allot of this will look familiar as after going through his book I have tried to edit his code to do what I want (to me after reading the book its the best way to learn, by messing with the code etc).
#implementation BL
#synthesize delegate;
#synthesize lauchingAnim;
#synthesize afterlauchingAnim;
-(void) dealloc {
delegate = nil;
[lauchingAnim release];
[afterlauchingAnim release];
[super dealloc];
}
//--The below shootPhaser method takes the current direction the launcher is facing and asks the delegate (gameplaylayer) to create the ball moving in that direction.
//--The below createPhaserWithDirection method was declared (created) in the GamePlayLayerDelegate protocol.
-(void)shootPhaser {
CGPoint phaserFiringPosition;
PhaserDirection phaserDir;
CGPoint position = [self position];
float xPosition = position.x + position.x * 0.51f;
float yPosition = position.y + position.y * 0.045f;
{
phaserDir = kDirectionRight;
}
phaserFiringPosition = ccp(xPosition, yPosition);
[delegate createPhaserWithDirection:phaserDir andPosition:phaserFiringPosition];
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
id action = nil;
characterState = newState;
switch (newState) {
case kStatespawning:
// CCLOG(#“launcher->Changing State to Spwaning");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#“lancher_1.png"]];
break;
case kStateIdle:
// CCLOG(#“laucher->Changing state to idle");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#"lancher_1.png"]];
break;
case kStateFiring:
// CCLOG(#“launcher->Changing State to firing");
action = [CCSequence actions:
[CCAnimate actionWithAnimation:lauchingAnim],
[CCCallFunc actionWithTarget:self
selector:#selector(shootPhaser)],
[CCAnimate actionWithAnimation:afterlauchingAnim],
[CCDelayTime actionWithDuration:2.0f],
nil];
lauchingAnim.restoreOriginalFrame = NO;
afterlauchingAnim.restoreOriginalFrame = NO;
[self changeState:kStateIdle];
break;
case kStateDead:
CCLOG(#“launcher->changing state to dead");
break;
default:
CCLOG(#"unhandled state %d in launcher", newState);
break;
}
if (action !=nil) {
[self runAction:action];
}
}
-(void)updateStateWithDeltaTime: (ccTime)deltaTime andListOfGameObjects:(CCArray*)listOfGameObjects {
if (characterState == kStateFiring) {
// 5
if (characterState != kStateFiring) {
// If RadarDish is NOT already taking Damage
[self changeState:kStateFiring];
return;
}
}
if ((([self numberOfRunningActions] == 0) && (characterState != kStateDead)) ) {
// CCLOG(#"launcher Going to Idle!!!");
[self changeState:kStateIdle];
return;
}
}
-(void)initAnimations {
[self setLauchingAnim:[self loadPlistForAnimationWithName:#"lauchingAnim" andClassName:NSStringFromClass([self class])]];
[self setAfterlauchingAnim:[self loadPlistForAnimationWithName:#"afterlauchingAnim" andClassName:NSStringFromClass([self class])]];
}
-(id) initWithSpriteFrameName:(NSString*)frameName{
if ((self=[super init])) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### Laauncher initialized");
[self initAnimations];
characterHealth = 3.0f;
gameObjectType = kBallLaucher;
// 3
[self changeState:kStateIdle];
}
}
return self;
}
#end
Below is the code for my "Ball" class...
#import “Ball.h"
#implementation Ball
#synthesize delegate;
#synthesize myDirection;
#synthesize travelingAnim;
#synthesize ScoreAnim;
-(void) dealloc {
delegate = nil;
[travelingAnim release];
[ScoreAnim release];
[super dealloc];
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
[self setCharacterState:newState];
CGSize screenSize1 = [CCDirector sharedDirector].winSize;
CGPoint position = [self position];
CGPoint endPosition = ccp(screenSize1.width*1.5f, screenSize1.height*0.20f);
id action = nil;
//id action1 = nil;
switch (newState) {
case kStatespawning:
CCLOG(#“Spawning Ball");
[self setDisplayFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"Ball_1.png"]];
break;
case kStateTravelling: {
movementAction = [CCMoveTo actionWithDuration:5.0f
position:endPosition];
}
break;
case kStateScore:
PLAYSOUNDEFFECT(SCORE);
CCLOG(#“Ball Past Left Of Screen => Add 1 to Score");
action = [CCSequence actions:
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
break;
default:
CCLOG(#“Ball -> Unknown CharState %d",
characterState);
break;
}
if (action !=nil)
[self runAction:action];
}
-(void)removeSelf{
CCLOG(#"Removing Ball Object Has Scored.");
[self setVisible:NO];
[self removeFromParentAndCleanup:YES];
return;
}
-(void)updateStateWithDeltaTime:(ccTime)deltaTime andListOfGameObjects:(CCArray *)listOfGameObjects {
CGPoint currentSpitePosition = [self position];
CGSize screenSize = [CCDirector sharedDirector].winSize;
if (currentSpitePosition.x > screenSize.width*1.1f) {{
[self changeState:kStateScore];
}
}
return;
}
if ([self numberOfRunningActions] == 0) {
if (characterState == kStatespawning) {
[self changeState:kStateTravelling];
return;
}
}
}
-(void)initAnimations {
[self setTravelingAnim:[self loadPlistForAnimationWithName:#"travelingAnim" andClassName:NSStringFromClass([self class])]];
[self setScoreAnim:[self loadPlistForAnimationWithName:#"ScoreAnim" andClassName:NSStringFromClass([self class])]];
}
-(id) initWithSpriteFrameName:(NSString*)frameName{
if ((self=[super init])) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### Ball Initialised");
[self initAnimations];
characterHealth = 3.0f;
gameObjectType = kEnemyTypeBall;
[self changeState:kStatespawning];
}
}
return self;
}
#end
Below is the code for my "GameplayLayer" class...
#import "GamePlayLayer.h"
#import “Ball.h"
#import “BL.h"
#implementation GamePlayLayer
#synthesize delegate;
-(void) dealloc {
delegate = nil;
[super dealloc];
}
-(void) update:(ccTime)deltaTime {
CCArray *listOfGameObjects =
[sceneSpriteBatchNode children];
for (GameCharacter *tempChar in listOfGameObjects) {
[tempChar updateStateWithDeltaTime:deltaTime andListOfGameObjects:listOfGameObjects];
}
GameCharacter *tempChar = (GameCharacter*)[sceneSpriteBatchNode
getChildByTag:kEnemyTypeBall];
if ([tempChar characterState] == kStateScore) <==HERE I AM SEEING IF THE BALL HAS SCORED - THIS IS ALSO WHERE I THINK I MAY BE GOING WRONG SOMEHOW.
{
CCLOG(#"Add 1 Points To Score");
[self addPoint];
return;
}
}
-(void)addPoint
{
score = score + 1;
[scoreLabel setString:[NSString stringWithFormat:#"$%i", score]]; <===ADDING THE POINT TO THE SCORE
return;
}
-(void) createObjectOfType: (GameObjectType)objectType
withHealth:(int)initialHealth atLocation:(CGPoint)spawnLocation withZValue:(int)ZValue {
if (kBallLaucher == objectType) {
CCLOG(#"Creating launcher Object");
BL *ballLauncher = [[[BL alloc] init] initWithSpriteFrameName:#“launcher_1.png"];
[ballLauncher setCharacterHealth:initialHealth];
[ballLauncher setPosition:spawnLocation];
[sceneSpriteBatchNode addChild:tBT z:ZValue tag:kBallLaucher ];
[ballLauncher setDelegate:self];
[ballLauncher release];
}
}
**BELOW IS HOW I CONTROL WHEN THE BALL IS FIRED**
-(void)ChangeStateLaucher:(int)brandnewState withState:(CharacterStates)newState andObject:(GameObjectType)objectType; {
BL *bL = (BL*)
[sceneSpriteBatchNode getChildByTag:kBallLaucher];
int x = (arc4random() % 2);
if (x==0) {
CCLOG(#"Start Laucher Firing");
[bL changeState:kStateFiring];
count = 0;
}
if (x==1) {
CCLOG(#"No Laucher Firing");
count = count + 1;
if (count == 2) {
CCLOG(#"No Laucher Firing x2 - Start Laucher Firing");
[bL changeState:kStateFiring];
} else if (count > 3) {
CCLOG(#"No Laucher Firing x3 - Start Laucher Firing");
[bL changeState:kStateFiring];
}
}
[delegate ChangeStateLaucher:x withState:kStateFiring andObject:objectType];
}
-(void)createPhaserWithDirection:(PhaserDirection)phaserDirection andPosition:(CGPoint)spawnPosition {
CCLOG(#"Creating Ball from Gameplay Layer");
Ball *ballSprite = [[Ball alloc]initWithSpriteFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"Ball_1.png"]];
[ballSprite setPosition:spawnPosition];
[ballSprite setMyDirection:phaserDirection];
[ballSprite setCharacterState:kStatespawning];
[ballSprite setCharacterHealth:3.0f];
[sceneSpriteBatchNode addChild:ballSprite z:20 tag:kEnemyTypeBall];
[ballSprite release];
}
-(id)init {
self = [super init];
if (self !=nil) {
CGSize screenSize = [CCDirector sharedDirector]. winSize;
self.TouchEnabled = YES;
srandom(arc4random()); // Seeds the random number generator
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:#"scene1atlas.plist"]; // 1
sceneSpriteBatchNode =
[CCSpriteBatchNode batchNodeWithFile:#"scene1atlas.png"]; // 2
[self createObjectOfType:kBallLaucher withHealth:3 atLocation:ccp(screenSize.width * 0.05f, screenSize.height * 0.822f) withZValue:10];
[gameBeginLabel setPosition:ccp(screenSize.width/2,screenSize.height/2)]; // 2
[self addChild:gameBeginLabel]; // 3
id labelAction = [CCSpawn actions:
[CCFadeOut actionWithDuration:2.5f],
nil]; // 4
[gameBeginLabel runAction:labelAction];
lives = 3;
scoreLabel = [CCLabelBMFont labelWithString:#"$0"
fntFile:#“BallTest.fnt"];
scoreLabel.position = ccp(screenSize.width * 0.5f, screenSize.height * 0.9f);
[self addChild:scoreLabel
z:-1 tag:kNewScoreTagValue];
[self scheduleUpdate];
}
return self;
}
#end
Once again I would like to thank anyone that has taken the time to look at this question regardless if you have posted a pointer or answer or not.
Thanks.
I think that when there are more than one Ball objects on the screen, the GamePlayLayer update method is unable to pick up on the state change on one of the balls at times because that ball is removed as soon as it enters kStateScore.
If you add a certain delay before removing the ball , the update method should pick up the changes in all the balls and your problem should be resolved.
In your Ball.m -(void)changeState:(CharacterStates)newState function , modify the action on state kStateScore like
action = [CCSequence actions:[CCDelayTime actionWithDuration:0.2],
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
Try this out and let me know if this worked for you!
EDIT
The above solution would end up adding more than one point as the ball would remain in kStateScore state for a long time due to delay. In order to counter that problem , we can introduce a new state call kStateDead and change the ball state to kStateDead after adding the point. This will ensure a point is added only once and also we can put the responsibility of removing the ball in kStateDead state. The new code would like as follows :
Ball.m
case kStateScore:
PLAYSOUNDEFFECT(SCORE);
CCLOG(#“Ball Past Left Of Screen => Add 1 to Score");
break;
case kStateDead:
action = [CCSequence actions:
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
default:
CCLOG(#“Ball -> Unknown CharState %d",
characterState);
break;
and in your Gameplayer.m
if ([tempChar characterState] == kStateScore) <==HERE I AM SEEING IF THE BALL HAS SCORED - THIS IS ALSO WHERE I THINK I MAY BE GOING WRONG SOMEHOW.
{
CCLOG(#"Add 1 Points To Score");
[self addPoint];
[tempChar changeState:kStateDead]; //change the state to dead after the point is added
return;
}
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;
}
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
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.. :)
I want to create pulsing platforms and have some problems with CCScale.
I have some objects of my Platforms class in NSMuttableArray.
#interface Platforms : CCNode{
CCSprite *platformSprite;
int typeP;
}
#property (nonatomic, retain) CCSprite *platformSprite;
#property int typeP;
#implementation
- (id)init:(int)type
{
self = [super init];
if (self != nil) {
typeP = type;
platformSprite = [CCSprite spriteWithFile:#"ball.png" rect:CGRectMake(0, 0, 51, 52)];
[self addChild:platformSprite];
}
return self;
}
I added all objects to screen
for (int i = 0 ; i < [_array count]; i++) {
Platforms* _platforms2 = [[_array objectAtIndex:i ]retain];
[self addChild:_platforms2];
[_platforms2 release];
}
So ... i want to get all objects where typeP == 1
i get all this objects and add them to NSMuttableArray
for (int i = 0 ; i < [platformsArrayOnScreen count]; i++) {
Platforms* temp = [platformsArrayOnScreen objectAtIndex:i];
if (temp.typeP == 1) {
[scaleArray addObject:temp];
}
Than i want to scale all of them to [CCScaleTo actionWithDuration:0.5 scale:0.2f];
and after that scale to [CCScaleTo actionWithDuration:0.5 scale:1.0f];
i created function [self schedule:#selector(checkForScale:)];
-(void)checkForScale:(ccTime)dt {
for (int i = 0; i < [scaleArray count]; i++) {
Platforms* temp2 = [scaleArray objectAtIndex:i];
if ([temp2.platformSprite scale] == 1.0f) {
[self scaleDown:temp2];
}
if([temp2.platformSprite scale] == 0.2f){
[self scaleUp:temp2];
}
}
}
-(void)scaleDown:(Platforms *)tempSprite {
id scaleDown = [CCScaleTo actionWithDuration:0.3 scaleX:0.2f scaleY:0.2f];
[tempSprite.platformSprite runAction:scaleDown];
}
-(void)scaleUp:(Platforms *)tempSprite {
id scaleUp = [CCScaleTo actionWithDuration:0.3 scaleX:1.0f scaleY:1.0f];
[tempSprite runAction:scaleUp];
}
But it's don't work (( my platforms don't pulsing. Where is my problem?
Thanks.
if you want to do this you can CCSequence.
[tempSprite.platformSprite runAction:[CCSequence actions:[CCScaleTo actionWithDuration:3.0f scale:0.2f],[CCScaleTo actionWithDuration:3.0f scale:1.0f], nil]];
Need not to do all this :
-(void)checkForScale:(ccTime)dt
-(void)scaleDown:(Platforms *)tempSprite
-(void)scaleUp:(Platforms *)tempSprite