11

I'm working on a sprite-kit game where nodes spawn below the lowest point on the screen and gravity is set to have them float to the top of the screen. Everything works perfectly, but it quickly starts to slow down the FPS, and eventually lags and glitches becoming very slowly. I thought the way to solve this would have been to remove the nodes from the parent after they'd past a point, this was the code I used in the update:

-(void)update:(CFTimeInterval)currentTime {

    if (_bubble1.position.y > CGRectGetMaxX(self.frame)+40) {
        [self removeFromParent];
    }

}

And in case it is needed, this is how I spawned said bubble below the initWithSize method:

-(void)didMoveToView:(SKView *)view {

    [self performSelector:@selector(spawnBubbles) withObject:nil afterDelay:1.0];
    [self performSelector:@selector(spawnBubbles1) withObject:nil afterDelay:1.5];

}

-(void)spawnBubbles {
    randomPosition = arc4random() %260*DoubleIfIpad;
    randomPosition = randomPosition + 20*DoubleIfIpad;

    randomNumber = arc4random() %7;
    randomNumber = randomNumber + 1;

    myColorArray = [[NSArray alloc] initWithObjects:colorCombo1, colorCombo2, colorCombo3, colorCombo4, colorCombo5, colorCombo6, colorCombo7, colorCombo8, nil];
    myRandomColor = [myColorArray objectAtIndex:randomNumber];

    _bubble1 = [SKShapeNode node];
    [_bubble1 setPath:CGPathCreateWithEllipseInRect(CGRectMake(-25*DoubleIfIpad, -25*DoubleIfIpad, 50*DoubleIfIpad, 50*DoubleIfIpad), nil)];
    _bubble1.strokeColor = _bubble1.fillColor = myRandomColor;
    _bubble1.position = CGPointMake(randomPosition, CGRectGetMinY(self.frame)-60);
    _bubble1.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:20];
    _bubble1.physicsBody.categoryBitMask = CollisionBubble;

    [self addChild:_bubble1];

    [self runAction:[SKAction sequence:@[
                                         [SKAction waitForDuration:1.0],
                                         [SKAction performSelector:@selector(spawnBubbles) onTarget:self],
                                         ]]];

}

How can I make it so that the nodes are properly disposed of when they leave the screen? And how can I keep the FPS at a constant rate of 60 FPS?

Thanks in advance!!

user3576196
  • 275
  • 4
  • 11
  • 1
    "FPS" is only loosely related to number of nodes. other factors are going to be contribute to this more. – Brad Allred Jun 11 '14 at 18:41
  • Realy?! like what @BradAllred – user3576196 Jun 11 '14 at 18:46
  • You only have one bubble ivar, you would need to use an array to access all of them later. With your update code you would only be able to remove the bubble that was created last. More easily you can have each bubble run a custom action that frequently checks its position and calls removeFromParent when necessary. – CodeSmile Jun 11 '14 at 19:58

2 Answers2

12

I would recommend using the built in contact detection in spritekit. Create a skspritenode mimicking a roof that has a physics body set to detect contact with bubbles. Create an event on contact between the roof node and bubble node that will simply remove the bubbles. This will ensure that bubbles are removed and you maintain a constant FPS.

Example of event called on contact:

- (void)bubble:(SKSpritenode*)bubble didCollideWithRoof:(SKSpriteNode*)roof{
[bubble removeFromParent];}

Contact detection example:

- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
    firstBody = contact.bodyA;
    secondBody = contact.bodyB;
}
else
{
    firstBody = contact.bodyB;
    secondBody = contact.bodyA;
}

if (firstBody.categoryBitMask==bubbleCategory && secondBody.categoryBitMask == roofCategory)
{
    [self bubble:(SKSpriteNode*)firstBody.node didCollideWithRoof:(SKSpriteNode*)secondBody.node];
}}

Bubble needs:

 _bubble.physicsBody.contactTestBitMask = roofCategory;

Roof:

SKSpriteNode *roof = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:CGSizeMake(self.scene.size.width, 1)];   
roof.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.scene.size.width, 1)];

    roof.position = CGPointMake(self.scene.size.width/2,self.scene.size.height)
    roof.physicsBody.dynamic = NO;
    roof.physicsBody.categoryBitMask = floorCategory;
    roof.physicsBody.contactTestBitMask = bubbleCategory;
    [self addChild:roof];
meisenman
  • 1,818
  • 1
  • 15
  • 25
4

With Swift 1.1 / iOS 8, you can use the following pattern in order to solve your problem.


1. Init your scene's physicsBody property with the following code:

physicsBody = SKPhysicsBody(edgeLoopFromRect: frame) // or CGRectInset(frame, -10, -10) if you need insets 

2. Create category masks for your scene and your sprite nodes:

let boundaryCategoryMask: UInt32 =  0x1 << 1
let someNodeCategoryMask: UInt32 =  0x1 << 2

3. Set the proper categoryBitMask, collisionBitMask and contactTestBitMask for your scene and sprite nodes physicsBody properties:

// Scene
physicsBody!.categoryBitMask = boundaryCategoryMask

// Sprite node
/* ... */
someNode.physicsBody!.categoryBitMask = someNodeCategoryMask
someNode.physicsBody!.contactTestBitMask = boundaryCategoryMask 

As an example, the following Swift SKScene implementation shows you how to remove all sprite nodes that go beyond your scene's frame:

class GameScene: SKScene, SKPhysicsContactDelegate {

    let boundaryCategoryMask: UInt32 =  0x1 << 1
    let squareCategoryMask: UInt32 =  0x1 << 2


    override func didMoveToView(view: SKView) {
        // Scene
        backgroundColor = SKColor.whiteColor()
        physicsWorld.contactDelegate = self
        physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)
        physicsBody!.categoryBitMask = boundaryCategoryMask

        // Square
        let square = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 80, height: 80))
        square.zPosition = 0.1
        square.position = CGPoint(x: size.width / 2.0 - square.size.width / 2, y: size.height - square.size.height)
        square.physicsBody = SKPhysicsBody(rectangleOfSize: square.frame.size)
        square.physicsBody!.dynamic = true
        square.physicsBody!.affectedByGravity = true
        square.physicsBody!.categoryBitMask = squareCategoryMask
        // square.physicsBody!.collisionBitMask = 0 // don't set collisions (you don't want any collision)
        square.physicsBody!.contactTestBitMask = boundaryCategoryMask // this will trigger -didBeginContact: and -didEndContact: 
        addChild(square)
    }

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        // Check if the array containing the scene’s children is empty
        println("children: \(children)")
    }

    func didBeginContact(contact: SKPhysicsContact) {
        if contact.bodyA.categoryBitMask == squareCategoryMask {
            contact.bodyA.node?.removeFromParent()
            println("square removed")
        }

        if contact.bodyB.categoryBitMask == squareCategoryMask {
            contact.bodyB.node?.removeFromParent()
            println("square removed")
        }
    }

    func didEndContact(contact: SKPhysicsContact) {
        /* ... */
    }

    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
    }

}
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218