I'm using SpriteKit for a 2D game and have an array of images for a simple animation.
- First, I'm creating an array with SKTextures to keep references to those images.
var hyperLeapArray: [SKTexture] = []
for i in 0 ..< 90 {
hyperLeapArray.append(SKTexture(imageNamed: "hyper_leap_\(i)"))
}
- Then I create an animation action.
let animation = SKA.animate(with: hyperLeapArray, timePerFrame: 0.03)
- Finally, I run this action on my SKScene's node to play it on the screen.
self.animationHost.run(SKAction.repeatForever(animation)) // Plays the animaion on a loop.
The first time this is executed - FPS drops to ~20 and then smoothly stabilizes up to 60. To avoid this, I've decided to use SKTexture.preload
method before playing the animation.
Task.init {
await SKTexture.preload(hyperLeapArray)
}
And, on a simulator it worked fine, FPS are still dropping, but for acceptable > ~35 rate. But this solution caused another issue with memory on a real device. After investigating, I realized that calling SKTexture.preload
- fills the memory up to ~1.5GB - which is enormous and keeps that memory in use before the animation is cashed. Original 90 images weight only 12Mb, I have no idea how this is growing to the enormous amount of space.
What I've tried:
- To preload textures one by one, e.g.
func preload(index: Int, array: [SKTexture]) {
if (index < array.count) {
array[index].preload {
print("Preloaded \(index)")
sleep(1)
preload(index: index + 1, array: array)
}
} else {
print("Done")
}
}
- To initialize UIImages first and then build SKTexutres(image: UIImage)
- Run animations in background without preloading for SpriteKit to cache that before actual use
- Used SKTextureAtlas - but it made it even worse, up to 4Gb of memory usage
But nothing helped so far.
Any ideas on
- Why does this happen?
- How to keep FPS on a high level at any time?
- Why SKTexture.preload reservice so much space in memory?
Here is full code snippet if you'd like to reproduce it locally:
final class TempScene: SKScene {
private var animationHost: SKSpriteNode!
private var hyperLeapAnimation: Animation!
convenience init(test: Bool) {
self.init(size: FULL_SCREEN_SIZE)
let center = CGPoint(x: frame.width / 2, y: frame.height / 2)
animationHost = SKSpriteNode(color: .clear, size: FULL_SCREEN_SIZE)
animationHost.position = center
animationHost.zPosition = 110
addChild(animationHost)
var hyperLeapTextures: [SKTexture] = []
for i in 0 ..< 90 {
// You can use any texture instead.
hyperLeapTextures.append(SKTexture(imageNamed: "hyper_leap_\(i)"))
}
Task.init {
await SKTexture.preload(hyperLeapTextures)
}
let animation = SKAction.animate(with: hyperLeapTextures, timePerFrame: 0.03)
animationHost.run(SKAction.sequence([SKAction.wait(forDuration: 6),
SKAction.repeatForever(animation)]))
}
}