0

While learning and playing around with Swift and Spritekit i run into an issue: I can´t seem to create many SKShapeNodes at Runtime. I am trying to fill the Screen with "random" Triangles Triangles on the Screen

These brown-ish Triangles are SKShapeNodes created like this:

func createTwoFloorTrianglesAtLocation(xPos:CGFloat, yPos:CGFloat, width:CGFloat, height:CGFloat, orientation:Bool) -> [SKShapeNode] {
    
    let path1 = UIBezierPath()
    let path2 = UIBezierPath()
    
    if orientation {
        path1.move(to: CGPoint(x: xPos, y: yPos))
        path1.addLine(to: CGPoint(x: xPos, y: yPos + height))
        path1.addLine(to: CGPoint(x: xPos + width, y: yPos))
        
        path2.move(to: CGPoint(x: xPos + width, y: yPos))
        path2.addLine(to: CGPoint(x: xPos + width, y: yPos + height))
        path2.addLine(to: CGPoint(x: xPos, y: yPos + height))
    } else {
        path1.move(to: CGPoint(x: xPos, y: yPos))
        path1.addLine(to: CGPoint(x: xPos + width, y: yPos + height))
        path1.addLine(to: CGPoint(x: xPos, y: yPos + height))
        
        path2.move(to: CGPoint(x: xPos + width, y: yPos))
        path2.addLine(to: CGPoint(x: xPos, y: yPos))
        path2.addLine(to: CGPoint(x: xPos + width, y: yPos + height))
    }
    
    
    let triangle1 = SKShapeNode(path: path1.cgPath)
    triangle1.fillColor = groundColors[GKRandomSource.sharedRandom().nextInt(upperBound: groundColors.count)]!
    triangle1.name = "colorSprite"
    triangle1.lineWidth = 0.0
    
    let triangle2 = SKShapeNode(path: path2.cgPath)
    triangle2.fillColor = groundColors[GKRandomSource.sharedRandom().nextInt(upperBound: groundColors.count)]!
    triangle2.name = "colorSprite"
    triangle2.lineWidth = 0.0
    
    return [triangle1, triangle2]
}

I add these Triangles calling #addChild(SKNode) to the GameScene.

These Triangles don´t move or anything, they are supposed to disappear later in the Game by "drawing lines" above them.

The Problem is that i get very bad FPS and a high CPU-Usage as well as a high Power-Usage in both the XCode-Simulator and on a real Device (iPhone 13 Pro Max, too). If i reduce the Amount of Triangles it gets better, but i´d like to not do that.

Is there any way to fix this issue?

I also read online (Create an SKTexture from an SKShapeNode) that some use SKSpriteNode instead of SKShapeNode. But if use the following code to "convert" my ShapeNodes to SpriteNodes before adding them as a Child to the Scene, i see the Node-Counter in the Bottom left is also around 1000ish but there are no Rectangles rendered.

func addNodesToParent(_ stuff:[SKShapeNode]) {
    for n in stuff {
        addChild(SKSpriteNode(texture: self.view?.texture(from: n)))
        //addChild(n)
    }
}

I hope someone has an idea that i can try. Thanks in advance,

Greetings, Nico

//Edit1: Tried to use Arrays of CGPoints instead of CGPath, sadly it didn´t change anything in the Performance. Code:

func createTwoFloorTrianglesAtLocation(xPos:CGFloat, yPos:CGFloat, width:CGFloat, height:CGFloat, orientation:Bool) -> [SKShapeNode] {
    var p1:[CGPoint]
    var p2:[CGPoint]
    
    if orientation {
        p1 = [CGPoint(x: xPos, y: yPos), CGPoint(x: xPos, y: yPos + height), CGPoint(x: xPos + width, y: yPos)]
        p2 = [CGPoint(x: xPos + width, y: yPos), CGPoint(x: xPos + width, y: yPos + height), CGPoint(x: xPos, y: yPos + height)]
        
    } else {
        p1 = [CGPoint(x: xPos, y: yPos), CGPoint(x: xPos + width, y: yPos + height), CGPoint(x: xPos, y: yPos + height)]
        p2 = [CGPoint(x: xPos + width, y: yPos), CGPoint(x: xPos, y: yPos), CGPoint(x: xPos + width, y: yPos + height)]
        
    }
    
    let triangle1 = SKShapeNode(points: &p1, count: p1.count)
    triangle1.fillColor = groundColors[GKRandomSource.sharedRandom().nextInt(upperBound: groundColors.count)]!
    triangle1.name = "colorSprite"
    triangle1.lineWidth = 0.0
    
    let triangle2 = SKShapeNode(points: &p2, count: p2.count)
    triangle2.fillColor = groundColors[GKRandomSource.sharedRandom().nextInt(upperBound: groundColors.count)]!
    triangle2.name = "colorSprite"
    triangle2.lineWidth = 0.0
    
    return [triangle1, triangle2]
}
Nico770
  • 45
  • 5
  • 1
    Did you try to create triangles with points arrays instead of cgpath ? – Ptit Xav Oct 31 '21 at 15:59
  • Tried it right now, sadly not much of a Performance change (tried it just in the simulator now, but i don´t think it will differ too much from a real device). I updated my Post with the new Code in it, maybe i did something wrong? – Nico770 Nov 01 '21 at 07:30
  • Did you tried to have different name for each node ? Something like xpos_ypos_2. – Ptit Xav Nov 01 '21 at 10:07
  • Found interesting [article](https://www.hackingwithswift.com/articles/184/tips-to-optimize-your-spritekit-game). Especially part 4 (replace not blend) , 5 : (remove nodes the player can’t see) and 8 (disabling sibling order) – Ptit Xav Nov 01 '21 at 10:30
  • So i tried basically everything you told/linked me, sadly nothing worked. But i think some stuff referenced in the Article doesn´t really fits my use-case, but nonetheless it didn´t change anything. – Nico770 Nov 01 '21 at 16:37

1 Answers1

1

SKShapeNodes are relatively expensive. your scene will definitely bog down when you have a lot of them. Render to Texture is a good approach. this reduces lots of SKShapeNodes (or labels or other graphical nodes) to a single SKTexture.

func renderToTexture() -> SKTexture? {
    let container:SKNode = SKNode()
    
    //create shapes
    for _ in 0...100 {
        let shape = SKShapeNode(circleOfRadius: CGFloat.random(in:1...30))
        shape.position.x = CGFloat.random(in: 0..<frame.width)
        shape.position.y = CGFloat.random(in: 0..<frame.height)
        container.addChild(shape)
    }
    
    //render container to a single texture
    let texture:SKTexture? = self.view?.texture(from:container)
    return texture
}

assign this texture to a sprite node and you will have 1 draw call. it's expensive to make the render, but cheaper in the long run if you reduce many graphical nodes to one. note: since the resulting texture is not atlased, this 1 call will never be batched with others.

Fault
  • 1,115
  • 1
  • 7
  • 11