SpriteKit Memory leaks changing Scenes containing SKTileMapNodes

后端 未结 1 434
醉酒成梦
醉酒成梦 2021-02-06 18:23

I am trying to create a simple 2d platform game using Swift, SpriteKit, and SKTileMaps. But every time i change between scenes containing SKTileMaps I see a lot of memory leaks

相关标签:
1条回答
  • 2021-02-06 18:59

    I have come across this same issue and after looking into it, I believe it is a problem that is inherent to all SKTileMapNodes. It is a bug in SpriteKit.

    When you use an SKTileMapNode with ANY tiles filled (not a blank tile map), then the memory for the tile map will persist even when loading subsequent scenes. If you keep loading levels with SKTileMapNodes, then the memory will keep increasing until the game ultimately crashes. I have tested this with different games I have coded and using the code of others as well.

    Interestingly, if the tile map has all blank tiles (even if it has an SKTileSet assigned), then no memory leak will occur.

    From the best that I can guess, when an SKTileMapNode has any tile in it besides blank tiles, the entire SKTileSet that it is using, is being kept in memory and never gets removed.

    From my experiments, you can prevent this problem by swapping the SKTileSet with a blank tileset in didMove. You cannot do it anywhere else except within didMove (or a function called by didMove).

    So my solution has been to extract the tile set into individual sprites and then "nullify" the tileset on the tilemap. You can do so with the following code:

    extension SKTileMapNode {
    
    func extractSprites(to curScene: SKScene) {
    
        for col in 0..<numberOfColumns {
            for row in 0..<numberOfRows {
    
                if tileDefinition(atColumn: col, row: row) != nil {
    
                    let tileDef = tileDefinition(atColumn: col, row: row)
                    let newSprite = SKSpriteNode(texture: tileDef!.textures.first!)
                    curScene.addChild(newSprite)
    
                    let tempPos = centerOfTile(atColumn: col, row: row)
                    newSprite.position = convert(tempPos, to: curScene)
                }
            }
        }
    
        eraseTileSet()
    }
    
    func eraseTileSet() {
        let blankGroup: [SKTileGroup] = []
        let blankTileSet = SKTileSet(tileGroups: blankGroup)
        tileSet = blankTileSet
    }
    }
    

    Basically within didMove, you will need to call extractSprites on each SKTileMapNode. This will simply create SKSpriteNodes from each tile and put them into the scene. Then the SKTileSet will be "switched" to a blank one. Magically the memory leak will disappear.

    This is a simplified solution which you will need to expand upon. This only puts the sprites there, but doesn't define how they behave. Sorry but this is the only solution I have found and I believe your problem is a major bug in SpriteKit.

    0 讨论(0)
提交回复
热议问题