Search code examples
iosiphoneswiftsprite-kitframe-rate

Odd SpriteKit frame rate drop - lingering SKPhysicsBody instances suspected


I am working on a small SpriteKit game, and the game involves maneuvering a player SKSpriteNode through a cascading series of gates that are generated:

Screenshot from an iPhone 5c

In order to stop the frame rate from dropping over time, I have implemented a simple block of code that goes in my GameScene's update function:

        for node in children {
        if(node.position.y < -100) {
            if(node.name == "player") {
                switchMode(2)
            }
            node.removeFromParent()
        }

The game starts off at a perfect 60 fps, and remains at that state for a few minutes.

Unexpectedly, a frame rate drop occurs after the game has been running for around 2-3 minutes on an iPhone 5c, and after 4 minutes or so the game drops to an unplayable 12 fps. Looking at my code, not much else happens over time in the update loop, so this behavior is quite odd. Interestingly, when the game is lost and the game over GUI is called up, the framerate recovers to 60 fps, but drops back to a slower FPS based on how long the game has been running for.

I suspect that the SKNodes for the barriers are somehow not being properly deleted, perhaps that the SKPhysicsBody instances attached to the gate SKSpriteNode instances are remaining active and straining the CPU. Is there anything I can / need to do to explicitly remove the .physicsBody property of the SKSpriteNode instances to remove them from physics calculations? I have tried setting .physicsBody = nil during removal, and that has not helped.

I am running Xcode 7.1 on OS X 10.11.1, testing my app on an iPhone 5c running iOS 9.


Solution

  • Hmm, last time I monitored the GameScene's children.count I must have not monitored for long enough. Contrary to what I thought I confirmed, children.count slowly climbed, although it did not increase on every update. I suspected that some gates were not being generated properly and weren't falling down through the screen. I looked through my source code, and I found this mess:

     func genObstacle(rift: Int, x: CGFloat, width: CGFloat) {
        if(width > 0) {
            let obstacle = SKSpriteNode(color: obstacleColor(rift), size: CGSize(width: frame.width * width / 12, height: frame.width / 12))
            obstacle.position = CGPoint(x: frame.width * (x + width / 2) / 12, y: frame.height * 9 / 8)
            obstacle.position.x += (CGFloat)(rift - activeRift) * 2048
            obstacle.physicsBody = SKPhysicsBody(rectangleOfSize: obstacle.size)
            obstacle.physicsBody?.velocity = CGVector(dx: 0, dy: (CGFloat)(-500 - rift * 300) * (frame.height / 500))
            obstacle.physicsBody?.affectedByGravity = false
            obstacle.physicsBody?.allowsRotation = false
            obstacle.physicsBody?.linearDamping = 0
            obstacle.physicsBody?.mass = 32767
            obstacle.name = "obs"
            addChild(obstacle)
        }
    }
    

    The random generation uses random integers (casted to floats) to decide how many "units" wide a gate piece should be. I accidentally left in the possibility for the width to be 0, which created buggy SKNodes which were lingering around and complicating physics. To anyone else who has an odd frame rate drop with SpriteKit, keep an eye on your GameScene's children.count, often that can be an indicator of the source of your issue.