Search code examples
swiftsksceneskshapenode

Should I use an alternate of SKShapeNode to create a drawing? If so what?


I have the following code to draw a picture in an SKScene:

override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch in touches {
        let location = touch.locationInNode(self)
        newPoint = location

        let pathToDraw:CGMutablePathRef = CGPathCreateMutable()
        let myLine:SKShapeNode = SKShapeNode(path:pathToDraw)


        CGPathMoveToPoint(pathToDraw, nil, lastPoint.x, lastPoint.y)
        CGPathAddLineToPoint(pathToDraw, nil, newPoint.x, newPoint.y)
        lastPoint = newPoint

        myLine.lineWidth = 3.0
        myLine.path = pathToDraw
        myLine.strokeColor = SKColor.whiteColor()

        self.addChild(myLine)
    }
}

This does what I want, I can draw with it but I do not think it is very efficient. Should I use an alternate method to get the same effect of drawing a picture or is this okay? If I should use something else, what would the alternate method be?


Solution

  • Rather add an "SKShapeNode" as your drawing node and have it's path as a property i.e.

    class MyDrawingNode: SKNode {
        ...
    }
    

    and update the path rather than adding individual SKNode children per line to point in the path. So something to the effect of:

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        for touch in touches {
            let location = touch.locationInNode(self)
            newPoint = location
    
            let myLinePath:CGMutablePathRef = myDrawingNode.path
    
            ...
       }
    

    *edit: *The difference here being that you're only manipulating one object, and that is the path.. In your case you were creating many objects... Think of the outline of a house:

    enter image description here

    In this example, I've labelled each portion of the path.

    1. If you think of the path as a whole i.e. in my code example above, then you've referring to all portions of the path as one...
    2. If you think of the path as the individual components, i.e. you creating individual nodes, you're then dealing with 5 components (your individual nodes), i.e. the path parts as well as one more SKNode referring to them ass as a collective... In total 6 nodes.

    The 1 above, is more efficient in terms of memory and cpu/gpu use. 2 above is heftier on the cpu/gpu and memory, but them allows you to individually manipulate each part of the path individually.

    If the concept of 2 above is what you're after, i.e. referring to each part of the path individually, rather:

    1. Create one subclass SKNode, say SKHouseNode
    2. Create several variables referring to each part of the house i.e. leftRoofPath, rightRoofPath, rightVertPath, bottomHorzPath, leftVertPath perhaps as a struct and put it all together with:

    struct HousePathComponent { var startPoint: CGPoint var endPoint: CGPoint var lineThickness: CGFloat } typealias HousePath = [HousePathComponent]

    var myHouse = HousePath()
    
    var leftVertPath = HousePathComponent(startPoint: CGPointMake(0.0,0.0), endPoint: CGPointMake(0.0,100.0), lineThickness: 1.0)
    
    var leftRoofNode = HousePathComponent(startPoint: CGPointMake(0.0,100.0), endPoint: CGPointMake(50.0,150.0), lineThickness: 1.0)
    
    var rightRoofNode = HousePathComponent(startPoint: CGPointMake(50.0,150.0), endPoint: CGPointMake(100.0,100.0), lineThickness: 1.0)
    
    var rightVert = HousePathComponent(startPoint: CGPointMake(100.0,100.0), endPoint: CGPointMake(100.0,0.0), lineThickness: 1.0)
    
    var bottomHorz = HousePathComponent(startPoint: CGPointMake(100.0,0.0), endPoint: CGPointMake(0.0,0.0), lineThickness: 1.0)
    
    myHouse.appendContentsOf([leftVertPath, leftRoofNode, rightRoofNode, rightVert, bottomHorz])
    

    From here draw the path using the contents of myHouse which is an array of the various structs.

    There are loads of ways you can approach this, so I'm just throwing rough ideas out there for you to toy with :)

    FPS Dropping

    One way about this would be to:

    1. In the SKScene in the update function, put some code together to only update the scene every x ms as opposed to every pass with something like:

      var nthFrameCount = 0 let nthFrame = 6 override func update(currentTime: CFTimeInterval) {

      hero.tileSpriteIso.position = point2DToIso(hero.tileSprite2D.position)
      nthFrameCount += 1
      if (nthFrameCount == nthFrame) {
          nthFrameCount = 0
          updateOnNthFrame()
      }
      

      }

    then handle the actual update code in the updateOnNthFrame function. So in the above example, it'd be every 6 frames as opposed to every frame.

    Although less will be drawing per ms of time, the human eye starts registering smooth motion from about 25~30fps... On a television, in the US the television analog standard is NTSC, and that was 30 fps.

    So as you update your movements less times per second, the actual times the "update code" is run is 10 times per second (in my example, 10 times / second if the FPS is 60 fps)...

    Hope this explains a little more :) If not, pop me a chat request and I'll try help you further!

    Cheers :)