Zh4rsiest Zh4rsiest - 1 month ago 15
iOS Question

Objective-c, SpriteKit particles not removed after action finished

I'm making a game in Xcode, with SpriteKit and I've encountered a problem while working with particles.

Method of Initializing SKEmitterNode:

-(SKEmitterNode *)newExplosionEmitter {
NSString *explosionPath = [[NSBundle mainBundle] pathForResource:@"explosionH" ofType:@"sks"];
SKEmitterNode *explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:explosionPath];
return explosion;


}

This method of my mine is the AI of CPU1, which is called in the update method.

-(void)CPU1AI {
SKSpriteNode *CPU1 = (SKSpriteNode *)[self childNodeWithName:CPU1CategoryName];

int aiX = CPU1.position.x;
int aiY = CPU1.position.y;
int ballX = self.ball.position.x;
int ballY = self.ball.position.y;

if ((aiY-ballY)<250 && !CPU1isDead) {
//CPU1 AI

}
if (CPU1isDead) {
float posY = CPU1.position.y;
float centerX = self.frame.size.width/2;
CGPoint goToPos = CGPointMake(centerX, posY);

SKAction *moveToPoint = [SKAction moveTo:goToPos duration:3];
SKAction *removeNode = [SKAction removeFromParent];
SKAction *CPUdead = [SKAction runBlock:^{CPU1isDead = NO;}];
SKAction *sequence = [SKAction sequence:@[moveToPoint, removeNode, CPUdead]];
explosion.position = CPU1.position;
explosion.zPosition = 10;
[CPU1 runAction:sequence];
if (![explosion hasActions]) {
explosion = [self newExplosionEmitter];
[self addChild:explosion];
[explosion runAction:[SKAction sequence:@[[SKAction fadeAlphaTo:0.5 duration:3.5],[SKAction removeFromParent]]]];
}
}
[explosion removeAllChildren];
}


After the SK runActions ended, I put "[explosion removeAllChildren]" just to make sure my particles will be removed, but with or without it, one is still left in the memory ( I guess ) and is still buffering.
Is it because I declared it as a static SKEmitterNode in my scene ?

Thanks in advance !

Answer

Add these methods to your SKScene subclass (code from Apple's website that I modified slightly)...

- (SKEmitterNode*) newExplosionNode: (CFTimeInterval) explosionDuration
{
    SKEmitterNode *emitter =  [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:@"explosionH" ofType:@"sks"]];

    // Explosions always place their particles into the scene.
    emitter.targetNode = self;

    // Stop spawning particles after enough have been spawned.
    emitter.numParticlesToEmit = explosionDuration * emitter.particleBirthRate;

    // Calculate a time value that allows all the spawned particles to die. After this, the emitter node can be removed.

    CFTimeInterval totalTime = explosionDuration + emitter.particleLifetime+emitter.particleLifetimeRange/2;
    [emitter runAction:[SKAction sequence:@[[SKAction fadeAlphaTo:0.5 duration:totalTime],
                                            [SKAction removeFromParent]]]];
    return emitter;
}

- (void)createExplosionAtPosition:(CGPoint)position
{
    SKEmitterNode *explosion = [self newExplosionNode:3.5];
    explosion.position = position;
    explosion.zPosition = 10;
    [self addChild:explosion];
}

Remove the following from your code...

explosion.position = CPU1.position;
explosion.zPosition = 10;
[CPU1 runAction:sequence];
if (![explosion hasActions]) {
    explosion = [self newExplosionEmitter];
    [self addChild:explosion];
    [explosion runAction:[SKAction sequence:@[[SKAction fadeAlphaTo:0.5 duration:3.5],[SKAction removeFromParent]]]];
}

and replace it with this

[self createExplosionAtPosition:CPU1.position];

I suspect you want to set CPU1isDead = NO in the if statement, so the code isn't executed multiple times. You should also delete [explosion removeAllChildren].