Remove sprite from screen cocos2d iphone? - iphone

I have a game that I wrote. I am about ready to call it finished but I found a bug. Basically the game gets slower as the longer you play. My guess is this is due to sprites that are still being drawn off screen. I will paste the code below but basically the sprite is created in the "addNewBall" method. In this method it is added to an array which calculates its motion. After the ball reaches a position where it is off the screen it is removed from the array which causes it to stop moving but it is still being "drawn" off screen. How do I remove the sprite so the processor no longer calculates it. Thanks in advance for your help!
Tanner
Code:
-(void) addNewBall {
NumberOfBalls = NumberOfBalls + 1;
int RandomXPosition = (arc4random() % 240) + 40;
NSString *BallFileString = #"OrangeBall.png";
switch (arc4random() % 5) {
case 1:
BallFileString = #"OrangeBall.png";
break;
case 2:
BallFileString = #"GreenBall.png";
break;
case 3:
BallFileString = #"YellowBall.png";
break;
case 4:
BallFileString = #"PinkBall.png";
break;
case 0:
BallFileString = #"BlueBall.png";
break;
}
Ball = [CCSprite spriteWithFile:BallFileString];
Ball.position = ccp(RandomXPosition, 520);
BallIsMoving = YES;
[self addChild:Ball z:10];
[AllObjectsArray_ addObject:Ball];
[BallArray_ addObject:Ball];
}
//And here is where it is removed...
if (Ball.position.y <= -100) {
[BallArray_ removeObject: Ball];
}

You seem to be missing some conditions in your removal method. Don't you also want to remove the ball if its y position is greater than the screen height, or if its x position is off-screen? At any rate, in the same place that you're removing the ball from the array, you should add:
[self removeChild:Ball cleanup: YES]
I should also point out that your BallArray is probably redundant, since you're adding all the balls to another node anyway. If the only children of that node are Balls, you can get the array of balls using its children property. In this case, the child array would be: self.children (See http://www.cocos2d-iphone.org/api-ref/latest-stable/interface_c_c_node.html#a5e739ecda0c314283a89ac389dfca2fa for more info.)
If you have non-Ball children on the same node, you might want to add an intermediate node to simplify the design so that you can use one less array.

You said you are removing the objects from the arrays, but you didn't mention that you are also removing the sprite from the parent CCNode.
Check the methods from CCNode to remove childs: http://www.cocos2d-iphone.org/api-ref/latest-stable/interface_c_c_node.html#a0d4e615f688458c74001acf10f0ae011
You could use:
[Ball removeFromParentAndCleanup:YES];
This will remove the ball from it's parent CCNode and will remove all actions and callbacks.

You need to specify your sprite,and you can use this following line..
[self removeChild:Ball cleanup: YES]

Related

How do you stop a particle effect? (SKEmitterNode)

I currently have this code in a collide statement where if collide with object then this particle happens but how do I stop it? As it goes on forever whereas I only want to happen a couple of times per contactetc
SKEmitterNode *emitter = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"ff" ofType:#"sks"]];
emitter.zPosition = 0;
emitter.particlePositionRange = CGVectorMake(0, self.size.height);
emitter.position = CGPointMake(self.size.width, self.size.height/2);
[self addChild:emitter];
When you use the particle-editor you can set the maximum number of particles to create. It's the field below "Particle Texture".The official description is:
"The maximum number of particles that the emitter creates over the emitter’s lifetime. After this number is reached, no more particles are created by the emitter. Enter 0 to remove particle limits."
Also see: Particle Emitter Editor Guide
Of course, you should remove the emitter-node from its parent after it created the maximum number of particles. This can be done by creating an action-sequence that waits for a few seconds and removes the emitter-node from its parent [SKAction removeFromParent].
Use setParticleBirthRate:0 to stop emitting particles. This is the most realistic way to turn off an emitter.
If you want it to disappear immediately then use removeFromParent.
If using setParticleBirthRate remember the original value to turn it back on later. for instance.
#implementation GameScene
{
SKEmitterNode *rocketfire;
float rocketfire_birthrate;
}
// ...
-(void)init {
// ...
rocketfire_birthrate = rocketfire.particleBirthRate;
}
// turn on the emitter (thrust rocket) when the screen is touched
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[rocketfire setParticleBirthRate:rocketfire_birthrate];
}
// turn off the emitter on touches ended
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[rocketfire setParticleBirthRate:0];
}
This is similar to the answer here provided by Tapir, with the exception that I don't use the selector. I'm not sure if that is required in previous versions but in ios 10 I don't see my node counts increasing and it does work. ie. add an emitter, pop a specified number of particles and then remove the emitter. In this case, I've modified it to add a single particle.
-(void) gainPoints:(int)x y:(int)y {
NSString *pPlus1Path = [[NSBundle mainBundle] pathForResource:#"PlusEmitter" ofType:#"sks"];
SKEmitterNode *pEmitter = [NSKeyedUnarchiver unarchiveObjectWithFile:pPlus1Path];
// Place the emitter at the specified position
pEmitter.position = CGPointMake(x,y);
pEmitter.name = #"plus1";
pEmitter.particleLifetime = 1;
pEmitter.particleBirthRate = 1;
pEmitter.numParticlesToEmit = 1;
// Send the particles to the scene.
pEmitter.targetNode = self.scene;
[self addChild:pEmitter];
SKAction *pRemoveNode = [SKAction removeFromParent];
SKAction *pWaitForSecsN = [SKAction waitForDuration:1];
SKAction *pSequence = [SKAction sequence:#[pWaitForSecsN,pRemoveNode]];
[pEmitter runAction:pSequence];
}
SWIFT 4:
I think I have a slightly better approach for turning it off:
1) Put the birth rate to 0:
pEmitter.particleBirthRate = 0
2) Run a delay action for the lifetime:
let waitAction = SKAction.wait(forDuration: pEmitter.particleLifetime)
pEmitter.run(waitAction, completion: {
pEmitter.removeFromParent()
})
NOTE: Might not be 100% accurate if you have a range of partilcelifetime.
For those who want to freeze a particle emitter on a specific part of the animation, instead of removing it from screen, you can pause the emitter:
emitter.isPaused = true
when pause you need :
emitter.hidden = true
when restart you need :
emitter.hidden = false
emitter.resetSimulation()

How to refer to sprites when using Cocos2d collision detection?

Using the method below, how can one refer to specific sprites when checking to see if they intersect?
- (void)update:(ccTime)dt {
for (CCSprite *sprite in movableSprites) {
if (CGRectIntersectsRect(sprite.boundingBox, sprite.boundingBox)) {
break;
}
}
}
It appears that all sprites are available in the moveableSprites object, but I don't know how to check if specific sprites are colliding... I don't know how to refer to them. If there is an easier way to perform collision detection I'm interested.
It appears your code above will always return TRUE because you are checking if the boundingbox of sprite collides with sprite, and since they are the same it always will.
if (CGRectIntersectsRect(sprite.boundingBox, sprite.boundingBox)) {//
break;
}
Should be comparing to a different sprite not the same sprite.
if (CGRectIntersectsRect(sprite.boundingBox, otherSprite.boundingBox)) {//
break;
}
If that does not answer your question maybe you are looking to avoid enumerating through the array? If that is the case try using tags. Someting like below.
CCSprite *aSprite = [CCSprite spriteWithFile:#"hurdle1.png"];
[self addChild:aSprite tag:2];
Now [self getChildByTag:2] can take the place of sprite
and you can just add boundingBox to check collisions, as below.
if (CGRectIntersectsRect([self getChildByTag:2].boundingBox, checkSprite.boundingBox)) {//
break;
}

cocos2d sprite collision

I have an Array of CCSprites that being displayed all at once.
Every sprite has a movement path, a movement path is a random point on screen.
All the sprites are moving all at once to random points on screen.
What I want to do is to detect collision between the sprites and then change their movement path.
Is it possible?
Iterate through every CCSprite in your array (call it A), and for every iteration iterate again through every CCSprite in the array (excluding A itself of course) (call this one B). Now, use CGRectIntersectsRect along with boundingBox to find a collision between them. It goes something like this:
for (CCSprite *first in mySprites) {
for (CCSprite *second in mySprites) {
if (first != second) {
if (CGRectIntersectsRect([first boundingBox], [second boundingBox])) {
// COLLISION! Do something here.
}
}
}
}
Edit: But of course, it is possible that if two sprites collide, the "collision event" will occur twice (first from the point of view of sprite A, and then from the point of view of sprite B).
If you only want the collision event to trigger once every check, you will need to memorize the pairs so that you can ignore collisions that already did happen on that check.
There are countless ways you could check for that, but here's an example (updated code):
Edited again:
NSMutableArray *pairs = [[NSMutableArray alloc]init];
bool collision;
for (CCSprite *first in mySprites) {
for (CCSprite *second in mySprites) {
if (first != second) {
if (CGRectIntersectsRect([first boundingBox], [second boundingBox])) {
collision = NO;
// A collision has been found.
if ([pairs count] == 0) {
collision = YES;
}else{
for (NSArray *pair in pairs) {
if ([pair containsObject:first] && [pair containsObject:second]) {
// There is already a pair with those two objects! Ignore collision...
}else{
// There are no pairs with those two objects! Add to pairs...
[pairs addObject:[NSArray arrayWithObjects:first,second,nil]];
collision = YES;
}
}
}
if (collision) {
// PUT HERE YOUR COLLISION CODE.
}
}
}
}
}
[pairs release];
Have a look at this S.O. answers.
You can do simple collision detection using CGRectIntersectsRect and the node boundingBox. If you need more advanced features, have a look at a physics engine like chipmunk or Box2D.
Ray Wenderlich has written a good tutorial about using Box2D just for collision detection, if you're interested in going that way. http://www.raywenderlich.com/606/how-to-use-box2d-for-just-collision-detection-with-cocos2d-iphone
Check first that your sprites can be approximated by rectangles. If so then #Omega's answer was great. If they can't be, perhaps because they contain a lot of transparency or for some other reason, you might need to approximate your sprites with polys and work with those.

Acting on all sprites from the add new sprite method cocos2d xcode

I am building a game for iphone, I am new to game devolopment and somewhat new to the language. I have balls that fall from the top of the screen to the bottom and the player is supposed to try and catch them. The problem is, I add the new sprites with an "addnewBallSprite" method, and later act on the position in my UpdateEveryFrame method. But if there is still a ball on the screen when a new ball is created the old ball stops moving. Is there a way I can make my change in position command control all instances of the ball sprite??? Please explain in detail. Below is my addNewBall method:
-(void) addNewBall {
int RandomXPosition = (arc4random() % 240) + 40;
int RandomBallSprite = (arc4random() % 5);
NSString *BallFileString = #"OrangeBall.png";
switch (arc4random() % 5) {
case 1:
BallFileString = #"OrangeBall.png";
break;
case 2:
BallFileString = #"GreenBall.png";
break;
case 3:
BallFileString = #"YellowBall.png";
break;
case 4:
BallFileString = #"PinkBall.png";
break;
case 0:
BallFileString = #"BlueBall.png";
break;
}
Ball = [CCSprite spriteWithFile:BallFileString];
Ball.position = ccp(RandomXPosition, 520);
[self addChild:Ball z:1];
}
If you want to run some action (i mean CCAction or something other if you want) on the sprite you should have a pointer to this sprite. So you can create an NSMutableArray to keep those pointers and then to interact with them. If the only children of you CCLayer are balls - you can take there pointers via calling children method on your CCLayer.
EDIT
If you have many balls the only way to interact with them is to keep their pointers. Add this declaration to your class:
NSMutableArray *ballArray_;
Initialize array in your class init method:
ballArray_ = [[NSMutableArray alloc] init];
When you create a new ball add the ball to the array:
[ballArray addObject:myNewBall];
And now in your update method you can have the access to each ball in your scene:
for (Ball *ball in ballArray)
{
//increase the position of the ball
}
When the ball is catched by User or out of the scene remove it from array:
[ballArray removeObject: someBall];

Stalling in a game loop and removing sprites naturally

I'm making a game with cocos2d and I have a bunch of sprites that I would like to delete. For example I might have a bunch of characters on the screen but when my game is over I would like to clean them up. Right now I have created a special effect (particle system) as a distraction, but because it is transparent and does not cover all of the screen you can see through and watch the sprites disappear as I remove them from the layer.
Also because the instructions execute so fast to the user it appears as if the sprites disappear before the particle effect begins!
Any suggestions on my 2 problems? Thanks.
NSMutableArray *toRemove = [[NSMutableArray alloc] init]; // array of sprites that I collect to remove
spriteCount = 0;
if([self findAllSprites:parent forRemoval:toRemove] > 0){ // is there is at least one sprite to delete. If not then don't do anything
[self specialEffect]; // runs for maybe 3 seconds.
// how can I stall here so that the sprites aren't removed "instantaneously"?
for (Character* aCharacter in toRemove) {
[aCharacter.parent remove:aCharacter];
}
}
You can delay the removal action using performSelector:withObject:afterDelay:. For example:
NSMutableArray *toRemove = [[NSMutableArray alloc] init]; // array of sprites that I collect to remove
spriteCount = 0;
if([self findAllSprites:parent forRemoval:toRemove] > 0){ // is there is at least one sprite to delete. If not then don't do anything
[self specialEffect]; // runs for maybe 3 seconds.
[self performSelector:#selector(removeSprites:) withObject: toRemove afterDelay:1.0];
}
[toRemove release];
- (void) removeSprites: (NSArray*) toRemove
{
for (Character* aCharacter in toRemove) {
[aCharacter.parent remove:aCharacter];
}
}
Note that performSelector:withObject:afterDelay: will retain the toRemove object and keep it alive until after it calls removeSprites, so you don't have to do anything special with toRemove (except that you still need to release it as shown since you own it as well).
You need to do your 'special effect' in a thread, so that it runs alongside your sprite remove. Lookup NSThread for more information, but this will enable you to synchronize the two processes.