Search code examples
swiftsprite-kitskphysicsbody

How to give a physics body to an arc?


I have two arcs that form a circle(SKCropNode), each with a different color. I know I can give the whole circle a circleOfRadius, but I was wondering if it was possible to give just one arc a physics body to fit the shape of the arc.

Help much appreciated.


Solution

  • You may want to avoid using an SKCropNode to build your arcs. From Apple's documentation,

    Use clipping and effect nodes sparingly. Both are very powerful, but can be expensive, especially when nested together within the node tree.

    Alternatively, you can construct an arc-shaped core graphics path and then create a shape node from the path. You can then create a physics body using the CG path.

    To build the arc-shaped path,

    1. Add an inner arc from the starting angle to the ending angle
    2. Add a line from the inner arc's ending point to the outer arc's ending point
    3. Add the outer arc from the ending angle to the starting angle
    4. Close the path (which connects the arcs starting points)

    Extending CGPath to construct the arc path is not necessary, but often it's more convenient to use. Once extended, the new class method can be called from anywhere in your code. Here's an example:

    extension CGPath {
        static func arcWithWidth(arcWidth:CGFloat, start:CGFloat, end:CGFloat, radius:CGFloat, clockwise:Bool) -> CGPath {
            // The radius parameter specifies the middle of the arc; adjust this as needed
            let innerRadius:CGFloat = radius - arcWidth / 2.0
            let outerRadius:CGFloat = radius + arcWidth / 2.0
    
            // Note the arc is upside down because CGPath uses UIKit coordinates
            let path = UIBezierPath()
            // Add inner ring.
            path.addArcWithCenter(CGPointZero, radius: innerRadius, startAngle: start, endAngle: end, clockwise: clockwise)
            let x = outerRadius * cos(end)
            let y = outerRadius * sin(end)
    
            // Connect the inner to the outer ring
            path.addLineToPoint(CGPointMake(x, y))
    
            // Add outer ring
            path.addArcWithCenter(CGPointZero, radius: outerRadius, startAngle: end, endAngle: start, clockwise: !clockwise)
    
            path.closePath()
    
            return path.CGPath
        }
    }
    

    With the extension, you can create the top and bottom arcs:

        // Top arc
        var path = CGPath.arcWithWidth(20, start:0, end: CGFloat(M_PI), radius: 100, clockwise: true)
    
        let topArc = SKShapeNode(path: path)
        topArc.position = view.center
        topArc.fillColor = SKColor.redColor()
        topArc.strokeColor = SKColor.clearColor()
    
        // Add a physics body to the top half
        topArc.physicsBody = SKPhysicsBody(polygonFromPath: path)
        topArc.physicsBody?.affectedByGravity = false
    
        addChild(topArc)
    
        // Bottom arc
        path = CGPath.arcWithWidth(20, start:0, end: CGFloat(M_PI), radius: 100, clockwise: false)
        let bottomArc = SKShapeNode(path: path)
        bottomArc.position = view.center
        bottomArc.fillColor = SKColor.blueColor()
        bottomArc.strokeColor = SKColor.clearColor()
    
        addChild(bottomArc)