4

I have a game using Sprite-Kit and Swift where I generate random circles falling from the top of the screen to the bottom of the screen.

When launching the game, it runs perfectly fine at the beginning (at around 60 FPS or less) but then the FPS drops gradually and the game becomes extremely slow... I don't understand why the FPS drops with time (the number of nodes stays good at around 8-10, so they get removed when they go off the screen) - I tested it on both the iOS Simulator and an actual device, any ideas?

I have checked, the problem isn't coming from a memory leak. Also, I am using only one view controller.

The only function that I think could cause this issue is this one, but I don't know why:

/* Function to generate single random circle */
func generateCircle() -> Void {
    let circleSize:CGFloat = CGFloat(arc4random_uniform(40) + 3)
    let xPosition:CGFloat = CGFloat(arc4random_uniform(UInt32(size.width)))

    var randomCircle = SKShapeNode(circleOfRadius: circleSize)
    randomCircle.strokeColor = SKColor.redColor()
    randomCircle.fillColor = SKColor.redColor()
    randomCircle.physicsBody = SKPhysicsBody(circleOfRadius: circleSize)
    randomCircle.physicsBody?.dynamic = false
    randomCircle.position = CGPoint(x: xPosition, y: size.height + circleSize*2)
    randomCircle.physicsBody?.dynamic = true
    randomCircle.physicsBody?.categoryBitMask = randomCirclesGroup
    addChild(randomCircle)
}
sachalondon
  • 165
  • 1
  • 11
  • Are you maybe using multiple UIVIewControllers in your game? From previous experience this causes really bad performance. – Bokoskokos Jan 06 '15 at 19:40
  • The node count only considers nodes being drawn. Offscreen nodes are not counted, but may still be in the hierarchy. But since they aren't drawn. Anyhow, this can have plenty of reasons and needs some code that you at least suspect, or better: have measured to perform badly. A very common beginner's mistake is to create just too many nodes or bodies, possibly every time something is supposed to change, as opposed to just changing their properties. But that's a pot shot. Start removing code until you find performance is noticably better, then post that code (which has a perf. impact) here. – CodeSmile Jan 06 '15 at 19:56
  • @LearnCocos2D Thanks, I have posted some code that I think could be the problem (it's when I generate the falling circles that it might slow down) – sachalondon Jan 06 '15 at 21:35
  • and where and how do you remove the nodes? That doesn't happen automatically in case you are not aware of that. – CodeSmile Jan 06 '15 at 21:47
  • @LearnCocos2D I am not removing the nodes for the circles falling off the screen, how can I do that? Do you think it is the problem? – sachalondon Jan 06 '15 at 21:52
  • that is the problem, you accumulate nodes over time. The node count only considers the nodes that were drawn. See removeFromParent method. – CodeSmile Jan 07 '15 at 00:36
  • @LearnCocos2D yes I get it but how can I know when a circle goes out of the screen bounds to remove it? – sachalondon Jan 07 '15 at 10:46
  • When CGRectIntersectsRect of the scene frame and sprite frame no longer returns true. – CodeSmile Jan 07 '15 at 11:01
  • @LearnCocos2D Okay thanks, I'm not sure in which function I should do that check? – sachalondon Jan 07 '15 at 14:05

3 Answers3

3

Maybe there is a memory leak.

  1. Launch your game with Xcode.
  2. Open the Xcode Debug Navigator panel with CMD + 6.
  3. Select Memory and wait to see if the allocated memory grows up.

If this happen, I mean if the allocated memory continue to grow even when you know it should not, then you are leaking memory.

And the best tool to find where exactly is the problem in your code is Instruments.

Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
1

I've had this before, the node count makes it look they are gone, but they really aren't.

You need to remove the circles from the view to really get rid of them.

First you would define a set (inside your class, but not inside any function)

 let circles = Set<SKShapeNode>()

then in the generateCircle() function you would say:

 circles.insert(randomCircle)

Then in the update() function:

 override func update(currentTime: CFTimeInterval){
      for index in circles {
           if index.position.y <= 0 {
                index.removeFromParent()
                circles.remove(index)
           }
      }
 }

Basically what this does is, at every frame, check if any of the circles are lower than the bottom of the screen, and deletes them if they are.

Arkidillo
  • 91
  • 6
0

SKShapeNode has worse performance compared to SKSpriteNode and one should try to avoid using SKShapeNode if at all possible.

From Apple's documentation:

Shape nodes are useful for content that cannot be easily decomposed into simple textured sprites. Shape nodes are also very useful for building and displaying debugging information on top of your game content. However, the SKSpriteNode class offers higher performance than this class, so use shape nodes sparingly.

If you really want to use SKShapeNodes (perhaps because it's easy for prototyping) I would advise you to do the following:

  • Create textures for your shapes a single time, perhaps when the scene gets loaded.
  • Cache the rendered textures. In your situation a dictionary with size and texture seems appropriate, e.g. [CGSize: SKTexture].
  • From then on, whenever you need a shape, retrieve the texture from the cache and display them using an SKSpriteNode.

To render an SKShapeNode into a texture you can use SKView's textureFromNode function:

Renders the contents of a node tree and returns the rendered image as a SpriteKit texture.

Wolfgang Schreurs
  • 11,779
  • 7
  • 51
  • 92