Reusing sprites when using a lot of them - objective-c

I am in front of a problem when adding sprites on my game layer.
When the game starts it's initialize a two dimensional array (that I created) and put 30 sprites in it (5 columns, 6 lines). It's always the same image that I use for the 30 sprites.
The problem is that each time I create a new sprite...
Every 3 sec all sprites deseaper and new ones appear. I am using ARC but when I check into Instruments (allocations part) I can see the number of living sprites increasing every 3sec 30, 60, 90, 120 ...
So when I play for 15 minutes my game crash. How can I use the same sprite without creating a new one ?
I tried to create it in the init method but then when I enter in [self addChild : mysprite] it says to me that the sprite had already been add.
Here is my code :
int yValue = 400;
for (int line = 0; line < nbLines; line++) {
int xValue = 32;
for (int section = 0; section < nbSection ; section++) {
MySprite *mySprite = [[MySprite alloc] initWithFile:#"mySprite.png"];
mySprite.position = ccp(xValue, yValue);
[gameTab setObject:mySprite :section :line];
[self addChild:mySprite];
xValue += 64;
}
yValue -= 64;
}
and here is what I do for deleting the sprite :
[gameTab removeChild:mySprite cleanup:YES];
[self removeChild:mySprite cleanup:YES];
I don't know when ARC deallocate my sprites.
Thx.

Related

Remove old tileMap - SpriteKit - Objective C

this code loads a map1.tmx tileMap when player reaches the "door". When the player enters the door it loads a new map2.tmx file. Problem is when the new map2.tmx file is loaded, the old map1.tmx is running behind map.2.tmx and using all of map.1.tmx wall collisions AND ignoring map2.tmx wall collisions. Is there a way to do an opposite of addObject like removeObject and add map2.tmx as the new map? I would like to make map.2 run when the players enters the door.
I have tried removeAllActions, removeAllChildren, removeTileAtCoord: and other approaches but I lack of SpriteKit experience. Any help is appreciated.
- (void)handleDoorCollisions:(Player *)player
{
if (self.doorOver) return;
NSInteger indices[8] = {7, 1, 3, 5, 0, 2, 6, 8};
for (NSUInteger i = 0; i < 8; i++) {
NSInteger tileIndex = indices[i];
CGRect playerRect = [player collisionBoundingBox];
CGPoint playerCoord = [self.doors coordForPoint:player.desiredPosition];
NSInteger tileColumn = tileIndex % 3;
NSInteger tileRow = tileIndex / 3;
CGPoint tileCoord = CGPointMake(playerCoord.x + (tileColumn - 1), playerCoord.y + (tileRow - 1));
NSInteger gid = [self tileGIDAtTileCoord:tileCoord forLayer:self.doors];
if (gid != 0) {
CGRect tileRect = [self tileRectFromTileCoords:tileCoord];
if (CGRectIntersectsRect(playerRect, tileRect)) {
[self.doors removeTileAtCoord:tileCoord];
// add new map2.tmx
self.map = [JSTileMap mapNamed:#"map2.tmx"];
[self addChild:self.map];
[self removeObject:????]
}
}
}
}
I had the same issue. My solution was/is on map load I collect all collision data in a List<> and check any collisions against this list.
When the player enters a different map the list is beeing cleared and reinitialized with the data from the new map.
I use this approach for some other things as well like objects the player can interact with or areas that trigger some actions.

sprite kit - objective c: slow fps when I create a lot nodes

I wanted to create a space background so I make a for loop to create the stars. Here is the code:
for (int i = 0; i<100; i++) {
SKShapeNode *star= [SKShapeNode shapeNodeWithPath:Path.CGPath];
star.fillColor = [UIColor whiteColor];
star.physicsBody = nil;
int xposition = arc4random()%960;
int yposition = arc4random()%640;
star.position = CGPointMake(xposition, yposition);
float size = (arc4random()%3 + 1)/10.0;
star.xScale = size;
star.yScale = size;
star.alpha = (arc4random()%10 + 1 )/ 10.0;
star.zPosition = -2;
[self addChild:star];
}
But it takes a lot from my cpu. when the code is activated the cpu at top 78%.(I check the code in the iPhone simulator);
Somebody know how to fix it? thanks.
Your physics bodies continue to calculate even when off of the screen. You will need to remove them once they go out of the frame, otherwise everything will slow to a crawl. (And to echo what others have stated you will eventually need a real device).
From this document: Jumping Into Sprite Kit
You can implement the "Did Simulate Physics" method to get rid of the stars that fell from the bottom of the screen like so:
-(void)didSimulatePhysics
{
[self enumerateChildNodesWithName:#"star" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.y < 0)
[node removeFromParent];
}];
}
Note that you will first need to set the name of your star shapes by using the name property like so:
star.name = "star"

Creating a lot of buttons for levels in Cocos2d using a for loop

I'm trying to make a couple hundred levels for a game I'm developing, so obviously I want to use a for loop and not create each button individually.
I do all of this fine, but the code I'm currently using makes it so that the call block for each button is the same. Obviously if I'm going to have the different level buttons go to different levels, the call block must be different for each one.
In order to differentiate which level I'm on, I call [[LevelManager sharedInstance] nextLevel] the number of times corresponding to the level number. I attempted to change this number by getting the touch location of the user touching the button, getting a row/column number, then running the above code a certain number of times. However, it was not updating the touch position before running the whole call block after touching the button, which obviously makes my code not work.
Is there a way to update the touch position manually, before the call block finishes? Is there a way to somehow store every button in an array and establish a different call block for each one? I'm not sure what the best approach is to fixing my problem. Thanks and my code is below.
Called upon initialization
for (int l = 0; l < NUMBER_OF_WORLDS; l++) {
for (int j = 0; j < 4; j++) {
for (int k = 0; k < 5; k++) {
NSString *tempTitle = [NSString stringWithFormat:#"%i",(k+1)+(l*5)];
CCButton *tempButton;
tempButton = [CCButton buttonWithTitle:tempTitle]
tempButton.block = ^(id sender) {
int row = floorf(touchLocation.y/(screenBounds.size.height/6)) + 1;
int column = floorf(touchLocation.x/(screenBounds.size.width/4)) + 1;
int buttonIndex = row*column;
for (int m = 1; m < buttonIndex; m++) {
[[LevelManager sharedInstance] nextLevel];
}
CCScene *gameplayScene = [CCBReader loadAsScene:#"LevelScene"];
[[CCDirector sharedDirector] replaceScene:gameplayScene];
levelNumber = i;
};
tempButton.position = ccp((screenBounds.size.width/4)*j + levelImagePlaceholder.contentSize.width*tempButton.scale, screenBounds.size.height/6*k + 50 + l*screenBounds.size.height);
[self addChild:tempButton];
i++;
}
}
}
Method for getting touch position
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CCLOG(#"touch ran");
touchLocation = [touch locationInNode:self];
}
You already have the row/column available. They are j and k. Those variables can be referenced in your button's "on pressed" block.

cocos2d Generating multiple of the same sprites with velocity

I'm pretty new to iOS and cocos2d and I'm having a problem trying to create what I want the code to do. Let me give you the rundown first then i'll show what I've got.
What I got so far is a giant sprite in the middle and when that is touched, I want to have say 2000 of a different sprite generate from the center position and like a particle system, shoot off in all directions.
First off, I tried coding implementing the velocity code (written in Objective-c) over to Cocos2d and that didn't work. -code-
-(void)ccTouchBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if(CGRectContainsPoint([[self getChildByTag:1] boundingBox], location))
{
for( int i = 0; i < 100; i++)
{
CCSprite *ballGuySprite = [CCSprite spriteWithFile:#"ball.png"];
[self addChild:ballGuySprite z:7];
ballGuySprite.position = ccp(((s.width + i *10) /2), (s.height + i *10) /2);
}
}
}
What that does is when I touch the first sprite, 100 of the other sprites are on top of each other leading to the top right corner.
The velocity code that I used when as followed and when I try to apply it to the sprite nothing happens. - Velocity code -
-(void) checkCollisionWithScreenEdges
{
if(ballGuysRect.origin.x <= 0)
{
ballVelocity.x = abs(ballVelocity.x);
}
if(ballGuysRect.origin.x >= VIEW_WIDTH - GUY_SIZE)
{
ballVelocity.x = -1 * abs(ballVelocity.x);
}
if(ballGuysRect.origin.y <= 0)
{
ballVelocity.y = abs(ballVelocity.y);
}
if(ballGuysRect.origin.y >= VIEW_HEIGHT - GUY_SIZE)
{
ballVelocity.y = -1 * abs(ballVelocity.y);
}
}
-(void) updateModelWithTime:(CFTimeInterval)timestamp
{
if(lastTime == 0.0)
{
lastTime = timestamp;
}
else
{
timeDelta = timestamp - lastTime;
lastTime = timestamp;
ballGuysRect.origin.x += ballVelocity.x * timeDelta;
ballGuysRect.origin.y += ballVelocity.y * timeDelta;
[self checkCollisionWithScreenEdges];
}
}
When I attach that code to the sprite, nothing happen.
I also tried adding a CCParticleExplosion which did do what I wanted but I still want to add a touch function to each individual sprite that's generated and they tend to just fade away.
So again, I'm still fairly new to this and if anyone could give any advice that would be great.
Thanks for your patients and time to read this.
Your code looks good to me, but you never seem to update the position of your sprites. Somewhere in updateModelWithTime I would expect you to set ballGuySprite.position = ballGuysRect.origin plus half of its height or width, respectively.
Also, I don't see how updateModelWithTime can control 100 different sprites. I see only one instance of ballGuysRect here. You will need a ballGuysRect for each sprite, e.g. an array.
Finally, I'd say that you don't really need ballGuysRect, ballVelocity, and the sprite. Ball could be a subclass of CCSprite, including a velocity vector. Then all you need to do is keep an array of Balls and manage those.
I am not sure what version of cocos2d you are using but a few things look a bit odd.
Your first problem appears to be that you are just using the same sprite over and over again.
Since you want so many different sprites shooting away, I would recommend that you use a CCSpriteBatchNode, as this should simplify things and speed things up.
The following code should help you get that set up and move them offscreen with CCMoveTo:
//in your header file:
CCSpriteBatchNode *batch;
//in your init method
batch = [CCSpriteBatchNode batchNodeWithFile:#"ball.png"];
//Then in your ccTouches method
for( int i = 0; i < 100; i++)
{
CCSprite *ballGuySprite = [CCSprite spriteWithFile:#"ball.png"];
[batch addChild:ballGuySprite z:7 tag:0];
ballGuySprite.position = ccp(where-ever the center image is located);
id actionMove = [CCMoveTo actionWithDuration:actualDuration
position:ccp(random off screen location)];
[ballGuySprite runAction:actionMove];
}
Also usually your update method looks something like the following:
-(void)update:(ccTime)delta{
//check for sprites that have moved off screen and disable them.
}
Hope this helps.

Mutable Array and Sprites

I'm making a "snake game" like player and I have the body moving fine so that it adds a block in the previous position each time it moves up to 4 then it removes the last one... etc. I'm adding each block to a NSMutableArray (each block is a sprite and I add the sprite to the array) and I was wondering how to get the position of one of the sprites in the array. I need this so I can check to see if the "head" is trying to move onto itself.
P.S. I'm using cocos2d for iPhone.
When I say position I mean the coordinates, not the index position in the array.
[tail insertObject:block atIndex:i];
[self addChild:[tail objectAtIndex:i]];
i +=1;
CCSprite *sect;
for (int j = 0; j >= i; j++) {
sect = [tail objectAtIndex:j];
}
if (i > maxHealth) {
[self removeChild:[tail objectAtIndex:i-maxHealth-1] cleanup:YES];
id object = [tail objectAtIndex:i-maxHealth-1];
[tail removeObject:object];
}
When the scene is started i is set to 0 and the max health equals 3.
Get the position of a node in an array? Like
// Get the node from the array
CCNode *node = (CCNode*)[myArray objectAtIndex:0];
// Retrieve the position
CGPoint nodePosition = node.position;