I'm making a "Achtung die kurve"-clone in Sprite Kit. For the constantly moving lines/players I'm using A CGMutablePathRef along with an SKShapeNode. In the update method I'm doing this
// _lineNode is an instance of SKShapeNode and path is CGMutablePathRef
CGPathAddLineToPoint(path, NULL, _xPos, _yPos);
_lineNode.path = path;
to add to the line. The update method is also updating the _xPos and _yPos constantly to make it grow.
I guess what I'm really asking is is there another, more efficient way of drawing the lines, since the way I'm doing it now drops the frame rate way too much after a while (about 15-20 seconds). At this point the FPS just drops constantly until the game is unplayable. The Time Profiler tells me that this line: _lineNode.path = path is the cause of the FPS drop.
Thanks for any help! It is greatly appreciated.
PS. I'm trying to not use SKShapeNode at all since they seem to not being able to draw the lines too good (Small holes/artifacts in the curves etc.)
Screenshot:
To fix the "holes" in the curves just set the lineCap to a non-zero value:
I've try to translate to Swift 2.2 the good answer proposed by prototypical:
A custom node:
The GameScene :
This above it's all the code but if you want to try it, this is the link to the github repo.
Reading some articles around the possibilities to improve better
SKShapeNode
alternatives or strong re-factoring, I've found this project called SKUShapeNode, actually incorporated in SKUtilities 2 project, an idea to make a subclass ofSKSpriteNode
that renders using aCAShapeLayer
(some documentation), there are some bugs and there is always to convertPoint fromUIKit
CALayer
to actual Sprite Kit using node.Unfortunately, SKShapeNode is not that great for what you are trying to do. However, there is a way to optimize this, albeit with some caveats.
First one of the largest problems with the fps is that the draw count gets extremely high because each line segment you add is another draw. If you set
showsDrawCount
on yourSKView
instance, you will see what I mean.In this answer Multiple skshapenode in one draw?, you can get more information about how you can use
shouldRasterize
property of aSKEffectNode
to solve the problem if you are drawing something once. If you don't do this, you will have processor time spent on numerous draws each frame.So you can see that the draws is the main issue with you not getting the performance you desire. However, you seem to want to be drawing consistently over time, so what I am going to suggest might be a viable solution for you.
The logic of the solution I am suggesting is as such :
1 - Create a
SKSpriteNode
that we can use as a canvas.2 - Create one
SKShapeNode
that will be used to draw ONLY the current line segment.3 - Make that
SKShapeNode
a child of the canvas.4 - Draw a new line segment via
SKShapeNode
5 - Use the
SKView
method `textureFromNode to save what has currently been drawn on the canvas.6 - set the texture of the canvas to that texture.
Loop back to #4 and make a new path for your
SKShapeNode
for the next line segment.Repeat as needed.
The result should be that your draw count will never be higher than 2 draws, which would solve the problem of a high draw count.
Basically, you are preserving what has previously been drawn in a texture, therefore only ever needing one
SKShapeNode
draw for the latest line segment and one draw for theSKTexture
.Again, I have not tried this process yet, and if there is any lag it would be in that
textureFromNode
call each frame. If anything would be your bottleneck, that would be it!I might try this theory out some time today, as I need
textureFromNode
for another problem I am trying to solve, and so I'll definitely find out how fast/slow that method is! hahaUPDATE
This is not complete code, but is the important parts to achieve the desired drawing performance (60fps) :
The basic node elements are :
container -> SKNode that contains all elements that need to be cached
canvas -> SKSpriteNode that will display the cached version of drawn segments
pool of segments -> used to draw segments initially, and get reused as needed
First create a pool of SKShapeNodes :
Next create method for getting a SKShapeNode from pool :
Next create a method for getting a segment from pool and drawing the line :
Next is a method for creating a texture and returning existing segments to the pool :
Lastly the touch handlers :
As I said, this is not all inclusive code, I assume you understand enough about the concept that you can implement into your application. These are just examples of my barebones implementation.