Creating multiple identical CCSprites - iphone

Is there a clean and efficient way to create an arbitrary number of identical CCSprites?
I really just need a tag to reference them for later removal.
For example in my game I am displaying the number of lives in a HUD:
- (void)displayOneLife
{
CGPoint positionOne = ccp(90, 450);
CCSprite *life1 = [CCSprite spriteWithFile:#"life.png"];
[life1 setPosition:positionOne];
[life1 setScale:0.5f];
[self addChild:life1 z:5 tag:1];
}
- (void)displayTwoLives
{
CGPoint positionOne = ccp(90, 450);
CGPoint positionTwo = ccp(105, 450);
CCSprite *life1 = [CCSprite spriteWithFile:#"life.png"];
CCSprite *life2 = [CCSprite spriteWithFile:#"life.png"];
[life1 setScale:0.5f];
[life2 setScale:0.5f];
[life1 setPosition:positionOne];
[life2 setPosition:positionTwo];
[self addChild:life1 z:5 tag:1];
[self addChild:life2 z:5 tag:2];
}
- (void)displayThreeLives
{
CGPoint positionOne = ccp(90, 450);
CGPoint positionTwo = ccp(105, 450);
CGPoint positionThree = ccp(120, 450);
CCSprite *life1 = [CCSprite spriteWithFile:#"life.png"];
CCSprite *life2 = [CCSprite spriteWithFile:#"life.png"];
CCSprite *life3 = [CCSprite spriteWithFile:#"life.png"];
[life1 setPosition:positionOne];
[life2 setPosition:positionTwo];
[life3 setPosition:positionThree];
[life1 setScale:0.5f];
[life2 setScale:0.5f];
[life3 setScale:0.5f];
[self addChild:life1 z:5 tag:1];
[self addChild:life2 z:5 tag:2];
[self addChild:life3 z:5 tag:3];
}

Actually, the CCSprite constructor looks up in cocos' texture cache, and if the cache already contains the texture, il reuses its texture. So you could simplify further by removing the *texture (which i believe is leaked here), and just use do this:
- (void)displayLifes:(int) nrOfLifes
{
CGPoint position = ccp(90, 450);
for(int i = 1 ; i <= nrOfLifes ; i++)
{
CCSprite *life = [CCSprite spriteWithFile:#"life.png"];
[life setPosition:position];
[life setScale:0.5f];
[self addChild:life z:5 tag:i];
position.x += 15;
}
}

Create a CCTexture2D using your image and then init all of the sprites using that texture.
This way you only load the image once.
Hope this helps.
EDIT:
Also , you can add them dynamically. Like this:
- (void)displayLifes:(int) nrOfLifes
{
CGPoint position = ccp(90, 450);
CCTexture2D *texture = [[[CCTexture2D alloc] initWithImage:[UIImage imageNamed:#"life.png"]]autorelease];
for(int i = 1 ; i <= nrOfLifes ; i++)
{
CCSprite *life = [CCSprite spriteWithTexture:texture];
[life setPosition:position];
[life setScale:0.5f];
[self addChild:life z:5 tag:i];
position.x += 15;
}
}
Cheers!

Related

CCPhysicsBody Collision not working in cocos2d-swift v3.4

Here is code:
#interface MainScene : CCNode<CCPhysicsCollisionDelegate>
//.m
-(void)onEnter
{
[super onEnter];
mPhysicsWorld = [CCPhysicsNode node];
mPhysicsWorld.gravity = ccp(0,0);
mPhysicsWorld.debugDraw = YES;
mPhysicsWorld.collisionDelegate = self;
[self addChild:mPhysicsWorld];
//Body_A
{
CCSprite *sprite = [CCSprite spriteWithImageNamed:#"sprite_1.png"];
sprite.position = ccp(280, 220);
sprite.rotation = 13;
sprite.name = #"Body_A";
sprite.physicsBody.collisionGroup=#"Group1";
sprite.physicsBody.collisionType=#"typeA";
CGRect rect = {CGPointZero, sprite.contentSize};
CCPhysicsBody *body = sprite.physicsBody = [CCPhysicsBody bodyWithRect:rect cornerRadius:0.0];
body.velocity = ccp(1000, 2000);
body.angularVelocity = 1.0;
body.type = CCPhysicsBodyTypeDynamic;
[mPhysicsWorld addChild:sprite];
}
//Body_B
{
CCSprite *sprite = [CCSprite spriteWithImageNamed:#"sprite_2.png"];
sprite.position = ccp(380, 220);
sprite.rotation = 13;
sprite.name = #"Body_B";
sprite.physicsBody.collisionGroup=#"Group2";
sprite.physicsBody.collisionType=#"typeB";
CGSize size = sprite.contentSize;
CGPoint points[] = {
ccp(0, 0),
ccp(size.width, 0),
ccp(size.width/2, size.height),
};
CCPhysicsBody *body = sprite.physicsBody = [CCPhysicsBody bodyWithPolygonFromPoints:points count:3 cornerRadius:0.0];
body.velocity = ccp(1000, -1000);
body.angularVelocity = -1.0;
body.type = CCPhysicsBodyTypeDynamic;
[mPhysicsWorld addChild:sprite];
}
//Delegate method
- (BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair typeA:(CCNode *)nodeA typeB:(CCNode *)nodeB
{
printf("Function Called\n"); //control not coming here
if([nodeA.name isEqualToString:#"Body_A"] && [nodeB.name isEqualToString:#"Body_B"])
{
return YES;
}
return NO;
}
Similar Question: Already tried this similar post but didn't get solution.
you are probably going to 'palm-forehead' , took me a while too :(
For both body A and body B the two lines below
sprite.physicsBody.collisionGroup=#"Group1";
sprite.physicsBody.collisionType=#"typeA";
are executed while physicsBody is still nil. Move the lines under the line :
CCPhysicsBody *body = sprite.physicsBody = [CCPhysicsBody bodyWithRect:rect cornerRadius:0.0];
ob cit. tested and verified, works on sim and device. Good luck with your project.

Animation in cocos2d crashes

I am having trouble with my game, it was running fine until i tried to add the animation, now whenever i go to shoot it crashes my game and i cant work out what is wrong with my animation code. Below i will put in all the code that causes the ninja star to shoot but as i said i believe the error is in the animation.
ccsprite Projectile is what gets shoot and what im trying to animate
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (StrategyBullet > 0) {
//SOME IF STATEMENTS
if (Strategyscore == 47) {
StrategyBullet = StrategyBullet +5;
}
if (Strategyscore == 97) {
StrategyBullet = StrategyBullet +5;
}
if (Strategyscore == 197) {
StrategyBullet = StrategyBullet +10;
}
// Choose one of the touches to work with
UITouch *touch = [touches anyObject];
CGPoint location = [self convertTouchToNodeSpace:touch];
// Set up initial location of projectile
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *projectile = [CCSprite spriteWithFile:#"ninja star 1.png"];
[self addChild:projectile z:2];
{
NSString *animationName = #"UNIQUE_ANIMATION_NAME";
CCAnimation* animation = nil;
animation = [[CCAnimationCache sharedAnimationCache] animationByName:animationName];
if(!animation)
{
NSMutableArray *animFrames = [NSMutableArray array];
for( int i=1;i<=5;i++)
{
NSString* path = [NSString stringWithFormat:#"ninja star %d.png", i];
CCTexture2D* tex = [[CCTextureCache sharedTextureCache] addImage:path];
CGSize texSize = tex.contentSize;
CGRect texRect = CGRectMake(0, 0, texSize.width, texSize.height);
CCSpriteFrame* frame = [CCSpriteFrame frameWithTexture:tex rect:texRect];
[animFrames addObject:frame];
}
animation = [CCAnimation animationWithSpriteFrames:animFrames];
animation.delayPerUnit = 0.175f;
animation.restoreOriginalFrame = YES;
[[CCAnimationCache sharedAnimationCache] addAnimation:animation name:animationName];
}
if(animation)
{
CCAnimate *animAction = [CCAnimate actionWithAnimation:animation];
[projectile runAction:animAction];
}
}
projectile.position = ccp(20, winSize.height/2);
// Determine offset of location to projectile
CGPoint offset = ccpSub(location, projectile.position);
// Bail out if you are shooting down or backwards
if (offset.x <= 0) return;
// Ok to add now - we've double checked position
[self addChild:projectile];
int realX = winSize.width + (projectile.contentSize.width/2);
float ratio = (float) offset.y / (float) offset.x;
int realY = (realX * ratio) + projectile.position.y;
CGPoint realDest = ccp(realX, realY);
// Determine the length of how far you're shooting
int offRealX = realX - projectile.position.x;
int offRealY = realY - projectile.position.y;
float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
float velocity = 480/1; // 480pixels/1sec
float realMoveDuration = length/velocity;
// collison stuff
projectile.tag = 2;
[_projectiles addObject:projectile];
StrategyBullet --;
[StrategyBulletLabel setString:[NSString stringWithFormat:#"%d", StrategyBullet]];
// Move projectile to actual endpoint
[projectile runAction:
[CCSequence actions:
[CCMoveTo actionWithDuration:realMoveDuration position:realDest],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[node removeFromParentAndCleanup:YES];
// CCCallBlockN in ccTouchesEnded
[_projectiles removeObject:node];
}],
nil]];
}
}
The crash is because you addChild projectile twice (my best guess). The rest looks ok, although i tend to use sprite sheets for animations as opposed to file-based frames as you do.

Infinite scrolling on a sprite(Parallax)

I am a newbie to the world of cocos2d i am developing my first tutorial and facing one problem
my problem is i have an image (1024 X 320) and my orientation is landscape i need to move that image continuously from right to left for this purpose i have used space shooter tutorial by Ray(Thanks to him) but the image doesn't seem to be appearing again and again.
my code is..
-(id) init
{
if( (self=[super init])) {
CGSize screenSize = [CCDirector sharedDirector].winSize;
// 1) Create the CCParallaxNode
backgroundNode = [CCParallaxNode node];
[self addChild:backgroundNode z:-1];
// 2) Create the sprites we'll add to the CCParallaxNode
Back = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
//Back.position=ccp(screenSize.width/2, screenSize.height/2);
Back.rotation = -90;
Back1 = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
Back1.rotation = -90;
// 3) Determine relative movement speeds for space dust and background
CGPoint dustSpeed = ccp(0.1, 0.1);
// 4) Add children to CCParallaxNode
[backgroundNode addChild:Back z:0 parallaxRatio:dustSpeed positionOffset:ccp(screenSize.width/2, screenSize.height/2)];
NSLog(#"back.content width is...%f",Back.contentSize.width);
[backgroundNode addChild:Back1 z:1 parallaxRatio:dustSpeed positionOffset:ccp(screenSize.width/2, screenSize.height*2)];
// 5) Enable updates
[self scheduleUpdate];
}
return self;
}
- (void)update:(ccTime)dt {
// 1) Update background position
CGPoint backgroundScrollVel = ccp(0,-1000);
backgroundNode.position = ccpAdd(backgroundNode.position, ccpMult(backgroundScrollVel, dt));
// 2) Check for background elements moving offscreen
NSArray *spaceDusts = [NSArray arrayWithObjects:Back, Back1, nil];
for (CCSprite *spaceDust in spaceDusts) {
if ([backgroundNode convertToWorldSpace:spaceDust.position].x < -spaceDust.contentSize.width) {
[backgroundNode incrementOffset:ccp(2*spaceDust.contentSize.width,0) forChild:spaceDust];
}
}
}
please help me out of this
Thanks in advance.
try this one
if (backgroundNode.position.y <-screenSize.height*2)
backgroundNode.position = ccp(0,0);
As init method is called only once the approach you are doing will be done only one time you need to again set the Position of the backgroundNode to 0 in your update method.
here the multiple may vary
Try this code it is creating a paralax moving from bottom to top change CGPointMake(0, 1.0) to this CGPointMake(1.0,0) in paraNode addChild line.
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super's" return value
if( (self=[super init]) )
{
float yPos =0.0;
NSMutableString *fileNameString = [[NSMutableString alloc]initWithCapacity:0];
if (IS_IPHONE_5)
{
[fileNameString appendString:#"Background-568h.png"];
yPos= 560.0;
}
else
{
[fileNameString appendString:#"Background.png"];
yPos= 470.0;
}
background1 = [CCSprite spriteWithFile:fileNameString];
background1.tag = 1;
background1.anchorPoint = CGPointMake(0,0);
background2 = [CCSprite spriteWithFile:fileNameString];
background2.tag = 2;
background2.anchorPoint = CGPointMake(0,0);
background3 = [CCSprite spriteWithFile:fileNameString];
background3.tag = 3;
background3.anchorPoint = CGPointMake(0,0);
background4 = [CCSprite spriteWithFile:fileNameString];
background4.tag = 4;
background4.anchorPoint = CGPointMake(0,0);
paraNode = [CCParallaxNode node];
[paraNode addChild:background1 z:1 parallaxRatio:CGPointMake(0, 1.0) positionOffset:CGPointMake(0, 0)];
[paraNode addChild:background2 z:2 parallaxRatio:CGPointMake(0, 1.0) positionOffset:CGPointMake(0, -yPos)];
[paraNode addChild:background3 z:3 parallaxRatio:CGPointMake(0, 1.0) positionOffset:CGPointMake(0, -yPos*2)];
[paraNode addChild:background4 z:4 parallaxRatio:CGPointMake(0, 1.0) positionOffset:CGPointMake(0, -yPos*3)];
[self addChild:paraNode z:-1 tag:123];
[self updateFrameRate:0.7 andYposition:yPos];
[fileNameString release];
fileNameString = nil;
}
return self;
}
-(void)updateFrameRate:(float)speedValue andYposition:(float)yposToSet
{
move1 = [CCMoveBy actionWithDuration:speedValue position:CGPointMake(0, yposToSet)];
move2 = [CCMoveBy actionWithDuration:0.0 position:CGPointMake(0, -yposToSet)];
move3 = [CCMoveBy actionWithDuration:0.0 position:CGPointMake(0, 0)];
CCSequence* sequence = [CCSequence actions:move1,move2,move3, nil];
CCRepeatForever* repeat = [CCRepeatForever actionWithAction:sequence];
[paraNode runAction:repeat];
}

Cocos-2d actions -- CCallFunc not doing anything

Basically, I'm trying to animate a sprite. When moving right I want one animation to play, and when moving left another to play. Here's my code-- nothing is happening at all, the sprite is merely shown on the screen.
-(void)moveLeft{
[sprite stopActionByTag:0];
NSMutableArray *spriteFrames2 = [NSMutableArray array];
CCSpriteFrame *frame4 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesLeft10.png"];
CCSpriteFrame *frame5 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesLeft11.png"];
CCSpriteFrame *frame6 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesLeft12.png"];
[spriteFrames2 addObjectsFromArray:[NSArray arrayWithObjects:frame4,frame5,frame4,frame6, nil]];
CCAnimation *anim = [CCAnimation animationWithFrames:spriteFrames2 delay:0.15f];
CCAnimate *animate = [CCAnimate actionWithAnimation:anim];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:animate];
repeat.tag = 1;
[sprite runAction:repeat];
id moveToLeft = [CCMoveTo actionWithDuration:3.0 position:CGPointMake(0, sprite.position.y)];
[sprite runAction:moveToLeft];
}
-(void)moveRight{
[sprite stopActionByTag:1];
CGSize winSize = [[CCDirector sharedDirector]winSize];
NSMutableArray *spriteFrames = [NSMutableArray array];
CCSpriteFrame *frame1 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesRight6.png"];
CCSpriteFrame *frame2 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesRight4.png"];
CCSpriteFrame *frame3 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:#"KnightSpritesRight5.png"];
[spriteFrames addObjectsFromArray:[NSArray arrayWithObjects:frame1,frame2,frame1,frame3, nil]];
CCAnimation* anim = [CCAnimation animationWithFrames:spriteFrames delay:0.15f];
CCAnimate* animate = [CCAnimate actionWithAnimation:anim];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:animate];
repeat.tag = 0;
[sprite runAction:repeat];
id moveToRight = [CCMoveTo actionWithDuration:3.0 position:CGPointMake(winSize.width, sprite.position.y)];
[sprite runAction:moveToRight];
}
-(id) init
{
if( (self=[super init])) {
[[CCSpriteFrameCache sharedSpriteFrameCache]addSpriteFramesWithFile:#"KnightImages2.plist"];
sprite = [CCSprite spriteWithSpriteFrameName:#"KnightSpritesRight6.png"];
sprite.position = ccp(240, 180);
[self addChild:sprite];
id actionSequence = [CCSequence actions:[CCCallFunc actionWithTarget:self selector:#selector(moveRight)],[CCCallFunc actionWithTarget:self selector:#selector(moveLeft)], nil];
CCRepeatForever *repeatForever = [CCRepeatForever actionWithAction:actionSequence];
[sprite runAction:repeatForever];
}
return self;
}
The way you've set this up is that the following happens, frame by frame:
Frame 1
execute moveRight
execute moveLeft
Frame 2
execute moveRight
execute moveLeft
Ad infinitum.
Since you're starting the animation all over every time you call moveRight or moveLeft, nothing really happens. You need to wait some time before running another animation for it to actually play. You can use the CCDelayTime action for this:
id actionSequence = [CCSequence actions:
[CCCallFunc actionWithTarget:self selector:#selector(moveRight)],
[CCDelayTime actionWithDuration:2],
[CCCallFunc actionWithTarget:self selector:#selector(moveLeft)],
[CCDelayTime actionWithDuration:2],
nil];

Constrain CCRenderTexture to top of screen when using CCCamera - Cocos2d

i have a "mini map" that I am using to show a portion of the background.
How can I constrain the CCRenderTexure to the top right corner of the screen? I am also using CCCamera to follow a sprite.
in init()
CGSize s = [[CCDirector sharedDirector] winSize];
minimap = [[CCRenderTexture renderTextureWithWidth:s.width * 1.5 height:s.height * 1.5] retain];
[minimap setPosition:ccp( s.width - ( s.width * kMinimapScaleFactor ) - 5, s.height - ( s.height * kMinimapScaleFactor ) - 5)];
[minimap begin];
[self visit];
[minimap end];
CCSprite *mms = [minimap sprite];
[mms setScale:kMinimapScaleFactor];
mms.scaleY *= -1;
mms.anchorPoint = ccp(0, 0);
[self addChild:minimap z:1 tag:1];
[self schedule:#selector(updateMinimap:) interval:1.0f];
Above init()
#define kMinimapScaleFactor 0.2
-(void) updateMinimap: (ccTime) dt
{
[minimap begin];
[self visit];
[minimap end];
}
in .h
CCRenderTexture *minimap;
#property(nonatomic, retain) CCRenderTexture *minimap;
Here is how I use CCCamera
id cameraMove = [CCFollow actionWithTarget:_ball];
[self runAction:cameraMove];
Anyone know?
Thanks
All you have to do is add it to the scene id
+ (id)scene
{
CCScene *scene = [CCScene node];
GameScene *layer = [GameScene node];
[scene addChild:layer];
CGSize s = [[CCDirector sharedDirector] winSize];
layer.player = [CCRenderTexture renderTextureWithWidth:s.width height:s.height];
CCSprite *mms = [layer.player sprite];
[mms setScale:kMinimapScaleFactor];
mms.scaleY *= -1;
mms.position = ccp(300, 400);
[scene addChild:layer.player z:2 tag:1];
[layer schedule:#selector(updateMinimap:) interval:1/30.0f];
layer._mini = [CCSprite spriteWithFile:#"minimap.png"];
layer._mini.position = ccp(405, 255);
layer._mini.opacity = 150;
[scene addChild:layer._mini];
return scene;
}