Search code examples
iosswiftaccelerometercore-motioncmmotionmanager

Weird accelerometer behaviour (CoreMotion)


I was trying to move the player in x-axis using the built-in accelerometer but something is wrong. When I simulate the app on my iPhone the node keeps moving in all directions and only after 2-3 times when I close the app and launch it again the node moves from left to right which is what I wanted. So it works but only after closing and opening the app again.

Part of the code:

import Foundation
import SpriteKit
import CoreMotion

class Gameplay : SKScene{

    private var player : Player?

    var motionManager = CMMotionManager()

    override func update(_ currentTime: TimeInterval) {
        self.player?.move()
    }

    func initializeGame(){
        player = childNode(withName: "player") as? Player!
        player?.initPlayer()
    }

    override func didMove(to view: SKView) {

        initializeGame()

        motionManager.startAccelerometerUpdates()
        motionManager.accelerometerUpdateInterval = 0.1
        motionManager.startAccelerometerUpdates(to: OperationQueue.current!) { (data , _) in
            if let accelerometerData = data{
                print("x: \(accelerometerData.acceleration.x)")
                print("y: \(accelerometerData.acceleration.y)")
                self.physicsWorld.gravity = CGVector(dx: accelerometerData.acceleration.x*10, dy: 0) //only x axis?
            } else {
                print("NOPE")
            }

        }
    }

Player.swift:

import Foundation
import SpriteKit

class Player : SKSpriteNode{

    func initPlayer(){
        name = "Player"

        physicsBody?.affectedByGravity = true
        physicsBody?.isDynamic = true

    }

    func move(){ //boundaries
        if position.x >= 315{
            position.x = 315
        } else if position.x <= -315{
            position.x = -315
        }
    }

    func shoot(){
       //... 
    }

}

Solution

  • Instead of changing the gravity of your Scene try to move the player directly. If you adjust the gravity it will mess up all your other physics bodies as well. So either move the player with a SKAction or better by applying a Force to it.

     player?.physicsBody?.applyForce(CGVector(dx: 30 * CGFloat(accelerometerData.acceleration.x), dy: 0))
    

    I would also recommend that you do not use the handler for the motion updates, I never got accurate results using it.

    So remove this code block

     motionManager.startAccelerometerUpdates(to: OperationQueue.current!) { (data , _) in
            if let accelerometerData = data{
                print("x: \(accelerometerData.acceleration.x)")
                print("y: \(accelerometerData.acceleration.y)")
                self.physicsWorld.gravity = CGVector(dx: accelerometerData.acceleration.x*10, dy: 0) //only x axis?
            } else {
                print("NOPE")
            }
    
        }
    

    and instead go to the update method of your scene and fetch the motion data from there.

    override func update(_ currentTime: TimeInterval) {
    
       if let accelerometerData = motionManager.accelerometerData {
          player?.physicsBody?.applyForce(CGVector(dx: 30 * CGFloat(accelerometerData.acceleration.x), dy: 0))
       }
        ....
    }
    

    This will make everything much smoother and responsive. Adjust the 30 to get the desired result.

    Hope this helps