eric
eric

Reputation: 4951

How to optimize for better frame rate?

This is for a puzzle game. As you can see from the screenshot, there are 256 nodes on screen and frame rate hovers around 10 FPS.

I see plenty of puzzle games out there with what I assume amounts to hundreds of separate nodes on screen, yet with great frame rates... I'd like to achieve the same. What are some optimization points I can consider based on my code? (Drawing code below)

I'm literally just creating a bunch of SKShapeNodes, putting them into an NSArray, and then adding them as children to the scene on didMoveToView

class GameScene: SKScene {

    var grid: Grid! = nil

    override func didMoveToView(view: SKView) {

        self.grid = Grid()
        self.grid.initWithParent(parent: self, rows:16, columns:8, hexagonSize:40.0)
        self.grid.drawGrid()

    }

    override func update(currentTime: CFTimeInterval)
    {
        // nothing here!! why so slow..
    }
}

//…

class Grid : NSObject {

    // this all gets initialized later
    var hexagonSize: CGFloat! = nil
    var hexagonArray: NSMutableArray! = nil
    var numRows: Int! = nil
    var numColumns: Int! = nil
    var parentScene: SKScene? = nil

    // …

    func drawGrid(){

        //…

        if(self.parentScene != nil){

            for rowIdx in 0..<self.numRows {

                for colIdx in 0..<self.numColumns {

                    //…
                    let hex = Hexagon()
                    hex.initWithParentNode(node: self.parentScene!, size: self.hexagonSize)
                    hex.drawAtPoint(positionPoint: hexCenter)
                    self.hexagonArray.addObject(hex) hex.drawAtPoint(:positionPoint)
                }
            }
        }
    }

    func initWithParent(#parent: SKScene, rows: Int, columns: Int, hexagonSize: CGFloat){

        self.parentScene = parent
        self.numRows = rows
        self.numColumns = columns
        self.hexagonSize = hexagonSize
    }
}


//…

class Hexagon: NSObject {

    //…

    var parentNode : SKNode? = nil

    var size : CGFloat! = nil

    var shape : SKShapeNode! = nil

    func drawAtPoint(#positionPoint: CGPoint){

        let diameter = self.size

        let radius = diameter/2

        let normal = radius * sqrt(3)/2

        var path = CGPathCreateMutable()

        self.shape = SKShapeNode(path: path)

        let point = CGPointZero

        CGPathMoveToPoint(path, nil, point.x, point.y+radius)

        CGPathAddLineToPoint(path, nil, point.x+normal, point.y+(radius/2))
        CGPathAddLineToPoint(path, nil, point.x+normal, point.y-(radius/2))

        CGPathAddLineToPoint(path, nil, point.x, point.y-(diameter/2))

        CGPathAddLineToPoint(path, nil, point.x-normal, point.y-(radius/2))
        CGPathAddLineToPoint(path, nil, point.x-normal, point.y+(radius/2))

        CGPathAddLineToPoint(path, nil, point.x, point.y+radius)

        self.shape?.path = path
        self.shape?.fillColor = SKColor.blueColor()

        if(self.shape != nil && self.parentNode != nil){

            self.shape.position = positionPoint
            self.parentNode!.addChild(self.shape!)

        }
    }

    func initWithParentNode(#node: SKNode, size: CGFloat){

        self.parentNode = node
        self.size = size

    }
}

simulator screenshot

Upvotes: 3

Views: 635

Answers (1)

Luca Angeletti
Luca Angeletti

Reputation: 59506

Well... you are manually drawing on the screen... You should leave to the SpriteKit engine the responsibility to draw your sprites on the screen (it has tons of optimizations to do this better then we can usually do).

My suggestions

First of all throw away your classes Grid and Hexagon.

Done? Good :)

The image

  1. Get a software like iDraw
  2. Draw an hexagon with transparent background
  3. Then export it as hexagon.png (I prepared the image for you)
  4. Add it to the Xcode Asset catalog

The Hexagon class

Create a file Hexagon.swift and write into it the following code

import SpriteKit

class Hexagon : SKSpriteNode {
    init() {
        let texture = SKTexture(imageNamed: "hexagon")
        super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Populating the scene

Now it's just a matter of creating a few Hexagon(s) and placing them on your scene.

class GameScene: SKScene {
    override func didMoveToView(view: SKView) {
        let hexagon0 = Hexagon()
        hexagon0.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
        self.addChild(hexagon0)

        // add more Hexagon(s) at different positions...             
    }
}

Conclusions

That's it. SpriteKit will take care of drawing the sprites you added to the scene.

Following this approach you can add a huge number of sprites on the screen, SpriteKit will do optimizations we mortal human beings will never know to keep the framerate as high as possible.

(The optimizations will be even better with Xcode 7 and iOS 9 since SpriteKit is now built on top of Metal).

Hope this helps.

Upvotes: 7

Related Questions