App Crashes: Memory issue? - ios

I was experimenting a little with Sprite Kit, and I noticed that when I let my app run for a while it crashes.
What I'm trying to do, is to draw a line that follows my player movement, I did this like so:
var ref: CGMutablePathRef = CGPathCreateMutable()
var shapeLine: SKShapeNode!
override func update (){
shapeLine.removeFromParent()
CGPathAddLineToPoint(ref, nil, player.position.x, player.position.y)
shapeLine = SKShapeNode(path: ref)
shapeLine.lineWidth = 3
shapeLine.path = ref
addChild(shapeLine)
}
And this works pretty fine, I remove the node everytime, updating the path, and creating another node with the new path.
The point is that after some seconds (around 25) it crashes.
The nodes are constant since I add and remove everytime one. The framerate starts to decrease right before crashing and goes from 30fps to 20fps.
What I find strange is the use of memory, it increases a lot, getting even to use around 800mb before crashing.
Is there something I'm forgetting, or is it just that the Path increases too much to be handled?

Unfortunately SKShapeNode is bugged, there are many bugs around this library component, most of these are reported here.
You can convert a SKShapeNode to a texture (textureFromNode) than use it with SKSpriteNode.

Related

SceneKit RunAction command take too much memory space

I noticed that when application using SceneKit runs the memory usage increase
steadily. After some search I pin pointed it to aNode.runAction.
Every time runAction is called it gets a bit of memory space and never release it.
Because runAction is used often in the application it is obvious that it is going to crash it.
Is there something to do to avoid that problem?
I am using this kind of functions to move some nodes.
I tried moving one node but nothing changed it takes less memory that is all.
func moveMyNodes(x:CGFloat, y:CGFloat, z: CGFloat, speed: CGFloat) {
for k in 0..myNodes.count {
let action = SCNAction.moveBy(x:x, y:y, z:z, duration: speed)
myNodes[k].runAction(action)
}
}
I found the reason.
In loop some of the nodes did not have geometry because they should not be seen.
When .runAction executed for a node without geometry it takes a bit of memory and they add up.
I've just get rid of nodes without geometry.

Swift Spritekit - Problems adding the same sprite to the scene if one is already added

My SKScene uses the following code to add sprites to the screen at certain time intervals, but if there is already a sprite on the screen when the next one is added my application freezes. Is there a way to add the same sprite to the screen without the application freezing?
let timer = SKAction.waitForDuration(1.00)
let addSpriteNode = SKAction.runBlock{
self.addSprite()
}
let sequence = SKAction.sequence([timer, addSpriteNode])
self.runAction(SKAction.repeatActionForever(sequence), withKey: "Sprites")
Note: I am not currently at a computer that is capable of running Xcode, so I'm going off of memory.
Note 2: If I could comment, I would ask you to include the code located in the addSprite function. However, due to a lack of reputation, I am unable to do so. You could get a much faster and accurate answer by including that code, since that is the code that creates and adds the sprite.
Answer:
You mention that you are attempting to add the same sprite to the screen - possibly like this:
let sprite = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(50,50))
func addSprite() {
addChild(sprite)
}
You can not have the same sprite on the screen multiple times. Instead, each time you want a new sprite to be added to the screen, you have to create a new sprite. In your addSprite function, your code should create a new sprite, set it's properties, and then add it to the main view, as so:
fun addSprite() {
let sprite = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(50,50)) // Creates a new sprite. You can customize this as needed.
addChild(sprite) // Adds newly created sprite to screen.
}
I hope this helps. If you post your code, I could provide an answer more tuned to your question.

Sprite kit sprites loading performance improvement

I am making a sprite kit game and I am using the plist file to set properties of each level. One of the properties in my plist file is a dictionary called patterns, which contains n items, where each of the items is a block, with hand typed x and y positions. This model is working perfectly fine for the kind of game I am making, as it is very convenient to set the levels right in a quick manner. However, I am facing one drawback I cant solve myself due to the lack of coding experience: some of the levels have as many as 290 blocks, so when the app tries to read the level, the app freezes for like 5 seconds. This is very annoying for the user. At the beginning my approach was: Read the plist file, and for each item call the method which creates the block as a SKSpriteNode using its imageNamed "" method. I thought this is the reason it lags so much, the fact that I am trying to load 300 sprites at the runtime seemed as a promising cause of the problem. Then I tried the following: I made the method which loads a pool of block initially, when the game starts for the first time. This is my method for that
func addObsticles1ToPool() {
for i in 0...300 {
let element = SKSpriteNode(imageNamed: "obsticle1")
element.hidden = true
obsticle1Pool.append(element)
}
}
Then, my code reads the plist file, and for each of the block calls the following:
func block(x: CGFloat, y: CGFloat, movingUp: Bool, movingSide: Bool, spin: Bool, type: Int16) {
var block: SKSpriteNode!
for obs in obsticle1Pool {
if obs.hidden {
block = obs
break
}
}
block.hidden = false
// Further down the properties of the block are set, such as actions it should perform depending on the input values, also its physics body is set.
I also have methods handling the fact that new elements should be added to the pool as game the proceeds and all that works just fine. The lag time dropped to around 3.5 - 4 secs, but that is still not good enough obviously. I would like to have a game with no lag. However, I am not sure if there is another, more efficient way, to do what I am trying to do than using the sprites pool.
Does anyone know how to reduce this lag time?
I have had the same problem! The issue is in this line...
let element = SKSpriteNode(imageNamed: "obsticle1")
SpriteKit isn't smart enough to know that a texture was already created with that image. So what it is doing is creating that texture over and over again and that is expensive.
Instead create a texture outside of the loop first and then create the sprite node with a texture. Something like this...
let elementTexture = SKTexture(imageNamed: "objstical1")
for i in 0...300 {
let element = SKSpriteNode(texture: elementTexture)
element.hidden = true
obsticle1Pool.append(element)
}
Not only will this be a ton faster it will decrease your apps memory a ton...assuming it was the same issue I was having. Hopefully that helps.

iOS app in SpriteKit using Swift lags after 6 minutes, any ideas why?

I have been working on an app in SpriteKit that displays a new image after every gesture from the user. During each session, the images are inevitably removedFromParent() but the user calls them frequently.
After using my app for about 6 minutes though, I noticed that it starts to get...choppy. The gestures I have set up use swiping but for some reason when I tap the screen a lot the CPU usage shoots up to 104%?!?
After every swipe, I call this code:
var chooseImage:String = imageCollection[Int(arc4random() % 16)]
var swiperImage = SKSpriteNode(imageNamed: chooseImage)
swiperImage.position = nodePosition
swiperImage.xScale = SH * 0.002
swiperImage.yScale = SH * 0.002
addChild(swiperImage)
This is part of the code that gets called after every swipe I make. Could this be a memory leak? I am at a total loss.
This is is the CPU usage result:
Instead of storing an array of Strings you should try storing the SKTextures for those images. These textures would be created when the object you referenced code from is instantiated. Then you can use those textures to instantiate the nodes later, reducing the amount of unnecessary image loading each time a node is created. It would look something like:
let imageTextures = imageCollection.map { SKTexture(imageNamed: $0) }
...
let swiperImage = SKSpriteNode(texture: imageTextures[Int(arc4random_uniform(imageTextures.count))]
Additionally, you can use arc4random_uniform() to generate a "uniformly distributed random number less than upper_bound" instead of using the mod.
This solution also has the benefit of letting you have multiple nodes use the same texture without having to reload it for each node.
Why don't you clear the child of your parents before you add a new one.

SpriteKit loses textures

I'm making multilevel game based on SpriteKit.
Everything works well except one thing: when user plays long time, changes many levels, etc... then SpriteKit starts losing textures.
I mean there is no big red cross like when image load fails but just empty space like nothing is there.
Hours of debugging and googling did not produce any results.
How can I deal with that bug?
I think I might be having a related issue, except the loss of textures occurs when I am rapidly running actions on a SKSpriteNode. In my code, I run an action each time I get a touch and when the touches are rapid and the animations are firing quickly, the base texture of the SKSpriteNode seemingly disappears. No memory warnings, not a peep from the console; the SKSpriteNode's texture is magically set to nil.
I get the impression from your question that this isn't your exact cause, but you are having the same symptoms. Unfortunately I don't know what is causing it. What I've done to work around the issue has been to constantly check if the texture on my SKSprite node has been set to nil immediately after I run an SKAction and then re-assign it if needed.
So, an abridged version (in Swift) of what I'm doing looks like this :
func doAnimation( ) {
_character.runAction(someSKAction, withKey: "animation")
//Whoops!, we lost our base texture again!
if _character.texture == nil {
let atlas = SKTextureAtlas(named: "someAtlasName")
let texture = atlas.textureNamed("idleFrame" )
_character.texture = texture
}
}
This is not really solution so much as a workaround, but it might be adaptable to your situation until you (or someone else on SO) figures it out. I won't argue that it's not ugly.
BTW, you are not alone with the disappearing texture issue; I wrote a similar response to a similar question here.

Resources