Is there a way to create an SKTexture from an SKShapeNode? Shape nodes are known to cause memory issues and I am looking for a way to create a static graphic from them (.png preferably).
-
Out of curiosity, what kinds of memory issues are you having with SKShapeNodes ? – prototypical May 05 '14 at 01:16
-
Here's some info about it http://sartak.org/2014/03/skshapenode-you-are-dead-to-me.html I have about 80 static non-animating shape nodes in my scene (a board game) and it runs around 20fps on a 3rd gen iPad. – Ivan Lesko May 05 '14 at 15:05
-
1Running at 20fps is not a memory issue, that's a performance issue. I have not tested the limitations of `SKShapeNode` in terms of memory, however it would seem that CGPath is the real issue. Isn't `SKShapeNode` just using the path you created with CGPath ? Lastly, another way to solve your framerate issue is to wrap your `SKShapeNode` in an `SKEffectNode and use `shouldRasterize` property of the `SKEffectNode`. That will 'flatten' the image and drastically increase performance if you have alot of shape nodes. – prototypical May 05 '14 at 21:21
-
Also, it would be interesting to do a test with the code from this post : http://stackoverflow.com/questions/18889297/skshapenode-has-unbounded-memory-growth outside of the init method. I have run into some quirky things when you create things inside the init, as opposed to having a method I call AFTER the instance has been returned. So would be interesting to remove that aspect from the equation. – prototypical May 05 '14 at 21:26
-
I tried it out, and it does seem to be some kind of memory leak with `SKShapeNode`. Interesting to note that in playing with it some, there was less of a leak if I set the `path` property of a given `SKShapeNode` to `NULL` and then the node itself to `NULL` before after removing. It reduced a little more when I set the properties strokeColor and fillColor to NULL and lineWidth and glowWidth to 0 after removing each node. Not a true solution, but if you have to use shape nodes... might be worthwhile to know :) – prototypical May 05 '14 at 23:11
-
I updated my answer to include a workaround that works rather nicely in tests I have run with a very large number of SKShapeNodes. – prototypical May 06 '14 at 00:30
1 Answers
You can use the SKView
method called textureFromNode:
Here's from the reference :
textureFromNode:
Renders and returns a Sprite Kit texture that contains the node’s contents.
- (SKTexture *)textureFromNode:(SKNode *)node
Parameters
node
A node object representing the content you want to render to the texture.
Return Value
A Sprite Kit texture that holds the rendered image.
Discussion The node being rendered does not need to appear in the view’s presented scene. The new texture is created with a size equal to the rectangle returned by the node’s calculateAccumulatedFrame method. If the node is not a scene node, it is rendered with a clear background color ([SKColor clearColor]).
UPDATE-- After exploring SKShapeNode a bit and verifying there indeed was a memory leak issue revolving around assigning a path and then removing the node, I had an idea...
Not really a new idea, more of a repurposed one. This wonderful idea actually allowed me to use SKShapeNodes to my hearts content :)
POOLING
Yep... I just created a pool of SKShapeNodes that I reused as needed. What a difference that makes :) So simply redefine the path whenever needed, when done using return to your pool, and it'll be waiting there for you to play with again at a later time.
You simply redefine the path whenever needed, when done using return to your pool, and it'll be waiting there for you to play with again at a later time.
Create a ivar or property NSMutableArray
in your SKScene
called pool and create it when you init the SKScene
. You can either populate the array with your shape nodes during init, or you can create them as needed.
This is something quick method I created for grabbing a new node from the pool :
-(SKShapeNode *)getShapeNode
{
if (pool.count > 0)
{
SKShapeNode *shape = pool[0];
[pool removeObjectAtIndex:0];
return shape;
}
// if there is not any nodes left in the pool, create a new one to return
SKShapeNode *shape = [SKShapeNode node];
return shape;
}
So wherever in the scene you need a SKShapeNode you'd do this :
SKShapeNode *shape = [self getShapeNode];
// do whatever you need to do with the instance
When you are done using the shape node, just return it to the pool and set the path to . For example :
[pool addObject:shape];
[shape removeFromParent];
shape.path = NULL;
I know it's a workaround and not an ideal solution, but certainly this is a very viable workaround for anyone wanting to use a large number of SKShapeNodes and not bleed memory.

- 6,731
- 3
- 24
- 34
-
I now it is late, but I am trying to convert my ShapeNode to SpriteNode and the problem is, that texture is not crated using nodeFromTexture. NSLog of my texture returns (null), so my texture is empty after I'm crating it? – MOzeb Apr 18 '16 at 15:46