Search code examples
iosswiftsprite-kitparallaxuimotioneffect

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


Solution

  • 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
         }
     }