rizzes
rizzes

Reputation: 1542

How to Render Many SpriteKit Nodes at Once?

I am using SpriteKit to render a large (20 x 20) dot grid that looks like this: enter image description here

I'd like to highlight rows or columns based on user input. For example, I'd like to change rows 1-10 to a red color, or columns 5-15 to a blue color.

What is the most performant way to do this?

I've tried:

  1. Naming each GridNode based on the column it's in (e.g. @"column-4). Then use enumerateChildNodesWithName: with the string as @"column-n", changing the color of each node (by changing SKShapeNode setFillColor:) in the enumerate block.
  2. Giving all the columns a parent node associated with that column. Then telling the parent node to change its alpha (thus changing the alpha of all its children).
  3. Making arrays for the different columns, then looping through each node and changing its color or alpha.

I've tried making the GridDot class an SKEffectNode with shouldRasterize: set to YES. I've tried both an SKShapeNode and a SKSpriteNode as its child. I've also tried taking away the SKEffectNode parent and just render an SKSpriteNode.

Each of these options makes my whole app lag and makes my framerate drop to ~10 FPS. What is the correct way to change the color/alpha of many nodes (without dropping frames)?

At its heart, the issue is rendering this many nodes, yes?

Upvotes: 4

Views: 1687

Answers (2)

CloakedEddy
CloakedEddy

Reputation: 1995

In contrast to @amobi's statement, 400 nodes is not a lot. For instance, I have a scene with ~400 nodes and a render time of 9.8ms and 9 draw calls.

If you have 400 draw calls though, you should try to reduce that number. To determine the amount of draw calls needed for each frame rendered, implement (some of) the following code. It is actually taken from my own SpriteKit app's ViewController class which contains the SpriteKit scene.

    skView.showsFPS = YES;
    skView.showsNodeCount = YES;
    skView.showsDrawCount = YES;

Proposed solution

I recommend using SKView's ignoresSiblingOrder. This way, SKSpriteNodes with equal zPosition are drawn in one draw call, which (for as many nodes/draw you appear to have) is horribly efficient. Set this in the -viewDidLoad method of the SKView's ViewController.

    skView.ignoresSiblingOrder = YES;

I see no reason to burden the GPU with SKEffectNodes in this scenario. They are usually a great way to tank your frame rate.

Final thoughts

Basic performance issues mean you have a CPU or a GPU bottleneck. It is difficult to guess which you're suffering from with the current information. You could launch the Profiler, but Xcode itself also provides valuable information when you are running your app in an attached device. FPS in the Simulator is not representative for device performance.

Upvotes: 3

Dmitry Klochkov
Dmitry Klochkov

Reputation: 2635

When I faced similar performance problems while using SKShapeNode I came up with this solution:

  1. Create SKShapeNode with required path and color.
  2. Use SKView's method textureFromNode:crop: to convert SKShapeNode to an SKTexture
  3. Repeat steps 1,2 to create all required textures for a node.
  4. Create SKSpriteNode from a texture
  5. Use created SKSpriteNode in your scene instead of SKShapeNode
  6. Change node's texture when needed using SKSpriteNode's texture property

If you have a limited set of collors for your dots, I think this aproach will fit fine for your task.

Upvotes: 3

Related Questions