Changing a texture on a custom spritenode class drops the framerate, but only the first time?

61 Views Asked by At

I'm working on a game right now, in which when triggered, my custom spritenode subclass, FireAlarm, performs a function where it changes it's texture twice and spawns an "explosion" node. This works great, except for the very first time it happens in the game, where the game lags significantly. The source of the lag seems to be the changing of the FireAlarm's texture, and I've tried a bunch of different ways to preload the textures using singleton classes and other strategies but nothing seems to fix it. I find it strange that this only occurs the first time and then seems to work fine, is there something I'm missing?

class FireAlarm: SKSpriteNode {
var isOccupied: Bool = false
var firstTexture = SKTexture(imageNamed: "fireAlarm")
var pulledTexture = SKTexture(imageNamed: "fireAlarmPulled")
var charredTexture = SKTexture(imageNamed: "fireAlarmCharred")
... 

func occupy() {
  
print("occupying alarm")
isOccupied = true
        
let quickDelay = SKAction.wait(forDuration: 1.85)
let explosionAction = SKAction.run {
self.addExplosion()}
        
let sequence = SKAction.sequence([quickDelay, explosionAction])
self.run(sequence)
let delayAction = SKAction.wait(forDuration: 1.0)
let changeTexture1 = SKAction.run {
self.texture = self.pulledTexture}
let changeTexture2 = SKAction.run {
 self.texture = self.charredTexture
 }
   
    let finalSequence = SKAction.sequence([delayAction, changeTexture1, delayAction, changeTexture2])
    self.run(finalSequence){
    self.isAlive = false
    self.shadow.removeFromParent()
    
    
    }
    
1

There are 1 best solutions below

2
Luca Angeletti On

You are not preloading the textures

I've tried a bunch of different ways to preload the textures using singleton classes and other strategies but nothing seems to fix it.

I see you declared some instance properties for the textures

var firstTexture = SKTexture(imageNamed: "fireAlarm")
var pulledTexture = SKTexture(imageNamed: "fireAlarmPulled")
var charredTexture = SKTexture(imageNamed: "fireAlarmCharred")

But this is not Texture preloading. Infact the SKTexture(imageNamed:) initializer creates just a reference, the real image load and preparation is done later.

The new texture object is initialized with the name of the image file and then control returns immediately to your game. Sprite Kit loads and prepares the texture data when it is needed by your game.

https://developer.apple.com/documentation/spritekit/sktexture/1520086-init

Solution: use SKTextureAtlas to preload textures

Here's an example.

class GameScene: SKScene {

  let textureAtlas = SKTextureAtlas(dictionary: [
    "fireAlarm": UIImage(named: "fireAlarm")!, // Force unwrapping only for demonstration purposes.
    "fireAlarmPulled": UIImage(named: "fireAlarmPulled")!,
    "fireAlarmCharred": UIImage(named: "fireAlarmCharred")!
  ])

  override func didMove(to view: SKView) {
    
    textureAtlas.preload {
      print("Now the textures are preloaded and you can start your game.")
    }

  }
}

Be aware that the textureAtlas.preload closure is escaping, so it will be called asynchronously at some point in the future when SpriteKit is done preloading the textures.

After the preload is done, you can use it in your game.

class FireAlarm: SKSpriteNode {
  func occupy() {
    guard let gameScene = self.scene as? GameScene else { fatalError() }
    
    // ...

    let changeTexture1 = SKAction.run {
      self.texture = gameScene.textureAtlas.textureNamed("fireAlarmPulled")
    }
    
    let changeTexture2 = SKAction.run {
      self.texture = gameScene.textureAtlas.textureNamed("fireAlarmCharred")
    }
    
    
  }
}

Let me know if this solves your issue.

Next

You can find here more details about SKTextureAtlas.