appcodix appcodix - 2 months ago 22
iOS Question

Use UIInterpolatingMotionEffect in SpriteKit?

UIInterpolatingMotionEffect is intended for use with UIView, but is there a possibility to use it within SpriteKit on a node ?

If not, does someone has an idea to realize the effect another way.

Thanks in advance

Answer

Creating a parallax effect in Sprite Kit can be achieved by creating a set of SKNode layers, adding sprites to the layers, and then moving each layer by a different amount when the user tilts the device. You can optionally scale the layers to give the illusion of depth.

Here's an implementation in Swift. Since it uses the accelerometer, this will only work on a device (not the Simulator).

import SpriteKit
import CoreMotion

class GameScene: SKScene {

    let squareSize = CGFloat(50.0)
    let manager = CMMotionManager()
    let maxDistance = CGFloat(40.0)

    override func didMoveToView(view: SKView) {

        let colors = [SKColor.redColor(), SKColor.greenColor(), SKColor.blueColor()]

        for i in 0..<colors.count {
            let node = createLayer(colors[i], depth: CGFloat(colors.count-i), screenSize: view.frame.size)
            addChild(node)
        }

        var tiltX:CGFloat = 0.0
        var alpha:CGFloat = 0.25

        // Define block to handle accelerometer updates
        if manager.accelerometerAvailable {
            manager.accelerometerUpdateInterval = 0.1
            manager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()) {
                [weak self] (data: CMAccelerometerData!, error: NSError!) in
                // Low-pass filter to smooth the measurements
                tiltX = tiltX * (1-alpha) + CGFloat(data.acceleration.x) * alpha
                for (index, node) in enumerate(self!.children as! [SKNode]) {
                    // Move each layer by a different amount to produce parallax effect
                    let deltaX = tiltX*CGFloat(index+1)*self!.maxDistance
                    node.position = CGPoint(x:self!.size.width/2.0+deltaX, y:self!.size.height/2.0)
                }
            }
        }
    }

    func createLayer(color:SKColor, depth:CGFloat, screenSize:CGSize) -> SKNode {
        let scale = 1.0/depth
        let node = SKNode()
        // Scale layer to create depth effect
        node.setScale(scale)
        node.zPosition = -depth
        node.position = CGPoint(x:screenSize.width/2.0, y:screenSize.height/2.0)
        let size = CGSize(width: squareSize, height: squareSize)
        // Create squares at random positions
        for i in 1...10 {
            let square = SKSpriteNode(color: color, size: size)
            let x = CGFloat(arc4random_uniform(UInt32(screenSize.width))) - screenSize.width / 2.0
            let y = CGFloat(arc4random_uniform(UInt32(screenSize.height))) - screenSize.height / 2.0
            square.position = CGPoint(x:x, y:y)
            node.addChild(square)
        }
        return node
     }
 }