9

I want to be able to improve my game's performance by somehow "flattening" my background nodes.


This youtube video demonstrates how I build up my background graphics.


But, I have my background graphics set up like this.

I use two textures like stamps and repeat then. In this case... one mountain texture with a snowy top... One mountain texture without the snow. I alter the zPosition of these stamps for a "layering" effect.

enter image description here

enter image description here

I then use "fills" which are just SKSpriteNodes of solid grey to layer over parts that need a grey fill.

E.g. Before fill nodes are added

enter image description here

A colour screen with alpha is then added on top to give the mountain a faded looked.

E.g. Before enter image description here

E.g. After enter image description here


The game sometimes freezes up... but it doesn't freeze when I remove these graphics.

Is there a way to improve performance by merging or flattening my background graphics nodes based on zPosition as a scene is loading up? How would I do this?

Would this improve performance? Or what would be the best way to improve performance in my case?

I think this question is similar... Merge all SKSpriteNode children into a single SKSpriteNode

But, how do I do this in Swift and take zPosition and alpha into account so I don't lose the layering effects?

Corey F
  • 621
  • 4
  • 14
  • The way you describe your intention with words like stamping, repeating and layering, it sounds exactly like the functionality of an `SKTileMapNode`. – Mark Brownsword Oct 18 '17 at 10:21
  • It wouldn't work. The "stamps" layer over each other in a random fashion. This is in order to vary the size of the hills. The snow parts on the platforms are SKTileMapNodes though. – Corey F Oct 18 '17 at 18:26
  • 1
    Oh man a bounty lol, there is a way you can merge them, providing you have enough texture memory. Note, this is going to reduce your draw count unless you add it to an atlas, but you can use `view.textureFromNode` to turn a node tree into a single texture. (Note wrap this in an autorelease pool when doing this) Now if you are using xcassets to do your current atlas, I would recommend going back to the regular .atlas folder till apple fixes a bug inside of the xcassets causing the draw count to increase drastically. – Knight0fDragon Oct 19 '17 at 13:30
  • @Knight0fDragon i was going to do an answer for this, but it seems to me that backgrounds shouldn't really be causing performance issues... does he need the bitblit or just to move his files into a different folder? – Fluidity Oct 23 '17 at 02:22
  • I think he just needs to place his images into the old atlas folder to reduce his draw count. Apple has yet to reply to me about the xcassets bug – Knight0fDragon Oct 23 '17 at 02:25
  • @Knight0fDragon is that bug just for xcode 9? – Fluidity Oct 23 '17 at 02:25
  • @Fluidity, honestly, I have no idea how long this bug existed. Let me see if I can find the sample project I sent them and test it on 8 – Knight0fDragon Oct 23 '17 at 02:26
  • thx.. can you upload so we can see too? @kod – Fluidity Oct 23 '17 at 02:34
  • it looks like the issue is simulator only, @Corey F did you try this on a device? – Knight0fDragon Oct 23 '17 at 02:41
  • @Fluidity I can't, I made it from somebody elses project – Knight0fDragon Oct 23 '17 at 02:42
  • @Knight0fDragon was it DMP? He's still having issues with an odd stutter too... – Fluidity Oct 23 '17 at 02:44
  • @Fluidity, yes, I haven't had time to look into his project, I have been battling too many issues with ARKit on my own project that I do not want to even bother dealing with all the new bugs introduces with Sprite Kit lol – Knight0fDragon Oct 23 '17 at 02:46
  • le sigh... glad I am still using xcode 8 – Fluidity Oct 23 '17 at 02:47

2 Answers2

5

Somethings you may want to do:

Separate your foreground and background into separate SKS files, and load them into your main SKS file via SKReferenceNode

Convert these SKS files into textures instead of SKReferenceNodes via code using view!.texture(from: node)

Ensure your atlas is not being broken up in a way that adds to the draw count

Design your code in a way that not all nodes are on the scene, the more nodes the slower your code becomes.

If you have SKPhysicsBody's, try to use as few as possible. This can be done my merging multiple bodies together with SKPhysicBody(bodies:) or creating 1 body with a polygon that goes around multple objects.

Also, if your bodies are not moving via physics (Not by SKActions) then make sure that isDynamic is set to false

If you are running SKActions, make sure you use as few of these as possible. For example, if you have 4 different nodes travelling left at 10 points per second, you can put these 4 nodes into a parent node, and run the action on the parent node.

Fluidity
  • 3,985
  • 1
  • 13
  • 34
Knight0fDragon
  • 16,609
  • 2
  • 23
  • 44
  • How does one “design code so not all nodes are on the scene”? Do you just mean remove nodes when not in the camera view? Can I get an example with code? I’ve been reluctant to do this ... because I couldn’t figure how to add and remove nodes fast enough for it to makes sense... eg The player walks backwards and you just removed those nodes. – Corey F Oct 25 '17 at 00:23
  • 1
    that is up to you to decide, if you know the node is going to show up in a few minutes, do not remove it, but if you have a node that is 30000 points away, that doesn't need to be on the scene – Knight0fDragon Oct 25 '17 at 00:35
  • I asked a separate question about the atlas issue @Knight0fDragon . If you want to ... feel free to edit it so it's clearer for the community as don't fully understand how atlases could cause performance issues. https://stackoverflow.com/questions/46981858/are-my-assets-being-set-up-in-a-way-that-would-add-to-the-draw-count – Corey F Oct 27 '17 at 19:06
4

Ok so here is an example of what you have now.. all your nodes here like this:

enter image description here

How to execute the bitblit easily is to move all these under a new empty node called "background"

enter image description here

Now head to your swift file, and set up an placeholder variable for the new node we will create in code

class GameScene: SKScene {

  var blitBackground = SKSpriteNode()

  override func didMove(to view: SKView) {

  }

}

Now , add this handy function that you can use in all of your projects if desired:

func blit(from node: SKNode) -> SKSpriteNode {
  return SKSpriteNode(texture: SKView().texture(from: node))
}

and here is the method we will use to initialize our bitblit background, and remove the laggy one from the game:

  func blitTheBackground() { // use in didMove
    let laggyBackground = childNode(withName: "background")!
    blitBackground = blit(from: laggyBackground)
    laggyBackground.removeFromParent()
    addChild(blitBackground)
  }

Here is the completed scene:

class GameScene: SKScene {

  var blitBackground = SKSpriteNode()

  func blit(from node: SKNode) -> SKSpriteNode {
    return SKSpriteNode(texture: SKView().texture(from: node))
  }

  func blitTheBackground() {
    let laggyBackground = childNode(withName: "background")!
    blitBackground = blit(from: laggyBackground)
    laggyBackground.removeFromParent()
    addChild(blitBackground)
  }

  override func didMove(to view: SKView) {
   blitTheBackground()
  }

}

and it reduced the node count from 10 to 4... hopefully, for you it will help with your draw count too!!

enter image description here NOTE, this method is EXTREMELY effective with shapenodes!!

If you need to do this in a loop, be sure to place it in an autorelease pool to be sure that the memory is freed up after it is used. Creating textures from the view uses a lot of memory, so you can expect to get errors if you are not careful with it.

for i in 0..<5
{
    autoreleasepool{
        let laggyBackground = childNode(withName: "background\(i)")!
        blitBackground = blit(from: laggyBackground)
        laggyBackground.removeFromParent()
        addChild(blitBackground)
    }
}
Corey F
  • 621
  • 4
  • 14
Fluidity
  • 3,985
  • 1
  • 13
  • 34
  • This is exactly what I was looking for! Awesome! Thanks so much. – Corey F Oct 24 '17 at 23:31
  • Note, wrap in an autorelease block of you plan on looping it. @CoreyF, you posted this in your question that you were looking for something more than this – Knight0fDragon Oct 24 '17 at 23:37
  • What does the autorelease block do for me? In the above scenario, I'm looping it right? Note: yes I still need to implement your suggestions for performance enhancements. They are all probably things I will want to do. The step by step example with code answer here just makes it very clear. I definitely need to clarify some of the things you've suggested somehow (e.g. what situation would cause "my atlas being broken up in a way that adds to the draw count" .) – Corey F Oct 24 '17 at 23:41
  • Which part specifically do I wrap autorelease around? – Corey F Oct 24 '17 at 23:47
  • i dont even know what he's talking about haha i think KoD knows more than the people working on SK – Fluidity Oct 24 '17 at 23:52
  • corey you forgot to @Knight0fDragon – Fluidity Oct 24 '17 at 23:52
  • you place it in an autorelease block so it frees up the memory it consumes after the autorelease is done, instead of at the end of the method. creating textures from the view uses a lot of memory, so you can expect to get errors if you are not careful with it – Knight0fDragon Oct 24 '17 at 23:55
  • @Knight0fDragon can you edit my question .. showing an example please? I don't know what an autorelease .. – Fluidity Oct 24 '17 at 23:56
  • @CoreyF, I wasn't commenting because I was afraid of Fluidity taking the answer lol, I wanted you to know that you actually posted this in your question and this is the Swift version, but you wanted more. zPosition, alpha, etc – Knight0fDragon Oct 25 '17 at 00:04
  • Well I just care about it looking the same. Like how you flatten an image in Photoshop. If a “layer” has alpha ... losing that alpha would have the layer block the layers beneath it. I’m assuming this method just “flattens things”. – Corey F Oct 25 '17 at 00:18
  • @CoreyF it's like a screenshot...it turns everything into 1 image, with 1 alpha and 1 zPosition... which means it's not suited for parallaxing etc.... unless you have a bunch of junk on 1 zposition.. what it's really good at is converting shapes to sprites (draw heavy), and a bunch of sprites to 1 image (your case) you can essentially take thousands of sprite nodes and turn them into 1 node with little resources involved... but then you lose access to controlling those thousands of nodes – Fluidity Oct 25 '17 at 03:42