Search code examples
iosobjective-csprite-kitparticle-systemskemitternode

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 !


Solution

  • 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].