Optimizing my usage of textures/SKSpriteNodes in SpriteKit - ios

I'm making a game in Swift using SpriteKit that has a background (landscape) and some objects on it. All of these are created as SKSpriteNodes and are added using addChild.
I also add cropping nodes (again using addChild) to be used for some purposes.
In total there are 23 nodes being loaded. In the profiler, it says my memory usage is 238MB.
I wanted to ask if this is a lot (I suspect it is) and then ask what I can do to reduce this and what I should be looking at?
My SKSpriteNodes are all about 375-400kb (per each node at 3x) and a resolution #3x of about 300-600 x 300-600 range (width/height ranges depending upon the object).
Am I doing something wrong here? Are these images too big? Are they not optimized enough?
Thanks so much in advance for any insight/pushes in the right direction.

Related

Sprite Animation file sizes in SpriteKit

I looked into inverse kinematics as a way of using animation, but overall thought I might want to proceed with using sprite texture atlases to create animation instead. The only thing is i'm concerned about size..
I wanted to ask for some help in the "overall global solution":
I will have 100 monsters. Each has 25 frames of animation for an attack, idle, and spawning animation. Thus 75 frames in total per monster.
I'd imagine I want to do 3x, 2x and 1x animations so that means even more frames (75 x 3 images per monster). Unless I do pdf vectors then it's just one size.
Is this approach just too much in terms of size? 25 frames of animation alone was 4MB on the hard disk, but i'm not sure what happens in terms of compression when you load that into the Xcode and texture atlas.
Does anyone know if this approach i'm embarking on will take up a lot of space and potentially be a poor decision long term if I want even more monsters (right now I only have a few monsters and other images and i'm already up to ~150MB when I go to the app on the phone and look at it's storage - so it's hard to tell what would happen in the long term with way more monsters but I feel like it would be prohibitively large like 4GB+).
To me, this sounds like the wrong approach, and yet everywhere I read, they encourage using sprites and atlases accordingly. What am I doing wrong? too many frames of animation? too many monsters?
Thanks!
So, you are correct that you will run into a problem. In general, the tutorials you find online simply ignore this issue of download side and memory use on device. When building a real game you will need to consider total download size and the amount of memory on the actual device when rendering multiple animations at the same time on screen. There are 3 approaches, just store everything as PNG, make use of an animation format that compresses better than PNG, or third you can encode things as H264. Each of these approaches has issues. If you would like to take a look at my solution to the memory use issue at runtime, have a peek at SpriteKitFireAnimation link at this question. If you want to roll your own approach with H264, you can get lots of compression but you will have issues with alpha channel support. The lazy thing to do is use PNGs, it will work and support alpha channel, but PNGs will bloat your app and runtime memory use is heavy.

SpriteKit using 95-100% CPU when running game with large tilemap (JSTileMap)

My project runs at 55-60FPS on an iPhone 6 but anything older is completely unplayable because something is eating CPU.
I think the issue is related to the number of tiles and layers on my map (64x256 with 4 layers) and Instruments shows "SKCRenderer:preprocessSpriteImp(..." taking 5198ms (23.2%) running time.
Does JSTileMap load every single tile's image (visible or not) at once? This post from RW indicates that is the case and that it could be worked around for large performance boosts:
http://www.raywenderlich.com/forums/viewtopic.php?f=29&t=9479
In another performance note - Sprite Kit checks all it's nodes and
decides which ones it needs to display each frame. If you have a big
tile map, this can be a big performance hit. JSTileMap loads all the
nodes (SKSpriteNode for each tile) when it loads the tile map. So, I
was also seeing performance issues in the Sprite Kit version with my
maps (which are 500 x 40 tiles). I added a check to my version of
JSTileMap that's included in the kit that marks the hidden property of
each tile, then intelligently unhides and hides only the tiles that
enter/exit the screen space. That increased performance on these
larger maps significantly.
Unfortunately that post does not go into detail regarding the steps taken to remedy this.
My first thought was to (I'm a beginner, please be gentle) create an array of nodes by looping through each point and checking for a tile on the specific layer. From there I'd work on adding/removing them based on distance from the player.
This didn't work, because the process of adding nodes to an array simply caused the app to hang forever on larger maps.
Could someone lend a hand? I'd love to work with larger/more complicated tilemaps but this performance issue is destroying my brain.
Thanks for reading!
UPDATE: Big thanks to SKAToolKit: https://github.com/SpriteKitAlliance/SKAToolKit
Their culling feature solved my problem and I'm now running even larger maps at less than 35% CPU.
JSTileMap has some issues handling larger maps but you do have a couple of options to consider:
Break your large map into several smaller pieces and load each new piece as required.
Only load those objects which are in the player's vicinity.
Only load those tiles which are in the player's vicinity.
I personally found it impossible to accomplish #3 with JSTileMap as I could not locate an array holding the map tiles. I solved this issue by using the SKAToolKit which provides easy access to map tile arrays. It is an excellent resource for parsing maps created in Tiled.

SpriteKit low performance

I have an app, that in one screen it has randomly positioned 800 - 1200 images all over the entire screen (small dots - see .png attachment). I tried that with UIImageView, Layers, UIViews, but the performance was always terrible, so I decided to use SpriteKit in this ViewController, to take advantage of device's OpenGL. The boost was really noticeable, but still the performance is not acceptable.
I'm fading out and in 1/3 of all the images (dots) 10/sec.
Any Ideas, how to increase the performance? It's only a couple of hundreds 13px x 13px (retina) .PNGs :)
Here's the .png:
You, my friend, need to be using SKEmitterNode.
In Xcode, you select File -> New -> File... and in the Resources section select SpriteKit Particle File. Supply it with your image and set up the parameters to create the desired effect. Voila!
How can I put this ... it's just a lot of sprites, it may not get much faster no matter what, specifically not with alpha blending (fading).
Perhaps you can simulate the effect using particles, they're faster to render than the same number of sprites but you have less influence over particles.
Also be sure you test performance on a real device, iOS Simulator's software rendering is very slow.

One background image vs repeating pattern/images, for Corona SDK game?

What would be a better approach in Corona re static background for a 2D scrolling based game?
Let's say the game level "size" is the equivalent of two screens wide and two screens deep.
Q1 - Would one large background image be an OK approach? This would probably be easier as you could do the work in Photoshop to prepare. Or is there a significant advantage (re performance) in Corona to have a small image "pattern" and repeat this in Corona (Lua code) to create the backdrop?
Q2 - If one large background image approach is OK, would I assume that one might have to sacrifice the resolution of the image, noting the size (2xscreens wide, and 2xscreens deep) correct for the higher resolution devices? That is for the iPad 3 say, assuming your configuration would normally would try to pickup the 3x image version (for other images, say smaller play icons, etc.) that for the background you might have to remain with the 1x or 2x image size. Otherwise, it may hit the texture limit (I've read "Most devices have a maximum texture size of 2048x2048"). Is this correct / does this make sense?
I used both approaches on my games.
Advantages of tiled mode:
You can make huge backgrounds.
Can be made to use less memory (specially with smallish tiles with lots of repeating, like a real world wallpaper)
Allow for some interesting effects (like parallax scrolling).
Problems of tiled mode:
Use more CPU performance
Might be buggy and hard to make behave correctly (for example one of my games gaps showed between tiles, but only on iPad Retina... it required some heavy math hackery to make it work)
It is hard to make complex and awesome backgrounds (reason why my point and click adventure games don't use tiled backgrounds).
Pay attention that some devices, has a limit in the size of the textures in pixels, this might be the ultimate limit of how large a single-texture background can be.

What kind of images should go into a spritesheet for a cocos2d ios app?

I apologize upfront for the long question, with multiple subquestions, but the question is really as stated in the title. All that follows is a detailed breakup of different aspects of the question.
In my universal ios game built using cocos2d, I have four categories of images - I want to determine which of them should go into spritesheets and which are better off loaded as individual images. My current guess is that only character animations that run throughout the game provide value being loaded in memory as spritesheets:
Character animations that run throughout the game play (except
when user is in menus): for these, I assume that having the images
in a spritesheet will reduce runtime memory usage (due to the
padding of individual files to power of two byte boundaries), hence
they are candidates for a spritesheet. Is that correct?
Small images (about 200 of them) of which 0 to 4 are displayed at any
time, picked at random, during game play. For these, I am not sure
if it is worth having all 200 images loaded in memory when only at
most any 4 are used at a time. So it may be better to directly
access them as images. Is that correct?
A few (about 20) small menu elements like buttons that are used only in static menus: since the menu items are used only during menu display, I assume they are not of much value in improving memory access via spritesheets. Is that correct?
A few large images that are used as backgrounds for the menus, the game play scene, etc. Most of these images are as large as the screen resolution. Since the screen resolution is roughly equal to the maximum size of a single image (for example, for ipad retina, 4096 x 4096 image size versus screen size of 2048 x 1536), there isn't much gain in using spritesheets as at most 1 or 2 large images would fit in one spritesheet. Also, since each of these large files is used only in one scene, loading all these large images as spritesheets in memory upfront seems like an unnecessary overhead. Hence, directly access them as spritesheets. Is that correct?
A couple of common related questions:
a) Packing images used across different scenes into the same spritesheet makes us load them into memory even when only a subset of the images is used in that scene. I assume that is a bad idea. Correct?
b) There is a stutter in the game play only on older devices (iPad 1 and iPhone 3gs). Will spritesheets help reduce such stutter?
c) I assume that spritesheets are only going to benefit runtime memory footprint, and not so much the on disk size of the app archive. I noticed, for instance, that a set of files which are 11.8 MB in size, when put in a spritesheet are 11 Mb - not much of a compression benefit. Is that a valid assumption?
Please let me know your thoughts on my rationale above.
Thanks
Anand
Rule of thumb: everything goes in a spritesheet (texture atlas), unless there's a good reason not to.
Definitely texture atlas.
Cocos2d's caching mechanism will cause all individual images to be cached and remain in memory, so eventually they'll use more than if they were in a texture atlas. I would only use single images if they're rarely needed (ie menu icons) and I would also remove them from CCTextureCache just after they've been added to the scene, to make sure they will be removed from memory when leaving that menu screen.
Assumption may be wrong. A 320x480 image uses 512x512 as a texture in memory. If you have multiple of them, you may be better off having them all in a single texture atlas. Unless you enabled NPOT support in cocos2d. Again, don't forget that CCTextureCache is caching the textures.
Keep in mind that large textures benefit a lot from spritebatching. So even if there may only be 2-3 images in the texture atlas, it can make a difference in performance.
a) It really depends. If your app is running low on memory, you might want to have separate texture atlases. Otherwise it would be wasteful not to pack the images into a single texture atlas (if possible).
b) It depends on what's causing the stutter. Most common issue: loading resources (ie images) during gameplay, or creating and removing many nodes every frame instead of re-using existing ones.
c) Probably. It may depend on the texture atlas tool (I recommend TexturePacker). It definitely depends on the file format. If you can pack multiple PNG into a single .pvr.ccz texture atlas, you'll save a lot of memory (and gain loading speed).
For more info refer to my cocos2d memory optimization blog post.

Resources