Swift Настройка отслеживания акселерометра с помощью CMMotionManager

Я искал способ добавить смещение к тому, как акселерометр отслеживает движение игрока в моей игре. В моей игре вы управляете персонажем игрока, наклоняя телефон вперед и назад. Это преобразуется акселерометром в координаты x и y на экране. к сожалению, это означает, что для того, чтобы персонаж не постоянно находился внизу экрана, вы должны держать телефон горизонтально. Что не очень удобно для игроков.

Итак, я пытаюсь реализовать способ, при котором любой угол телефона при запуске игрового сеанса является углом по умолчанию, а затем наклон определяется дельтой этого угла, а не необработанными данными, которые были бы против 0,0 наверное...

вот моя попытка разобраться:

func instantiateAcceleration() {
    if motionManager.accelerometerAvailable{
        let queue = NSOperationQueue()


        motionManager.startAccelerometerUpdatesToQueue(queue) { data, error in
            let currentX = self.player.position.x
            let currentY = self.player.position.y

            if(!self.hasCapturedOffset){

                /* So the first time we enter this loop. we'll cature the offset */

                self.offsetY = data!.acceleration.y;
                self.offsetX = data!.acceleration.x;

                print("captured offset : x - \(self.offsetX) y - \(self.offsetY)")
                self.hasCapturedOffset = true;
            }

            /* x coords */
            if data!.acceleration.y < (0 - self.offsetY) {
                self.destX = currentX + CGFloat(data!.acceleration.y * 100)
            }

            else if data!.acceleration.y > (0 + self.offsetY) {
                self.destX = currentX + CGFloat(data!.acceleration.y * 100)
            }

            /* y coords */
            if data!.acceleration.x > (0.1 - self.offsetX){
                self.destY = currentY - CGFloat(data!.acceleration.x * 100)
            }

            else if data!.acceleration.x < (0.1 + self.offsetX) {
                self.destY = currentY - CGFloat(data!.acceleration.x * 100)
            }

            /* keep it in bounds */
            if(self.destY > self.size.height){
                self.destY = self.size.height;

            }else if(self.destY < 0){
                self.destY = 0;
            }

            if(self.destX > self.size.width){
                self.destX = self.size.width;

            }else if(self.destX < 0){
                self.destX = 0;
            }

        }

    } else {
        print("Accelerometer is not available")
    }

}

указанная выше функция вызывается для viewDidLoad, а self.DestX, self.DestY используются для обновления проигрывателя в методе обновления:

override func update(currentTime: CFTimeInterval) {
    /* Called before each frame is rendered */
    /* Accelerometer data */
    let action = SKAction.moveTo(CGPoint(x:destX, y:destY), duration: 0.1)
    self.player.runAction(action)

}

Я действительно хотел бы узнать, как это сделать правильно, я знаю, что за этим определенно стоит какая-то математика, с которой я не знаком. Если бы кто-то мог указать мне правильное направление, я был бы очень признателен:)


person agovan    schedule 03.12.2015    source источник


Ответы (1)


Вы можете сохранить начальную ориентацию как referenceAttitude и использовать

 currentattitude.multiplyByInverseOfAttitude(referenceAttitude) 

для расчета движения.

Вот пример кода, который я сделал некоторое время назад:

import Foundation
import CoreMotion

// Damping factor
let cLowPassFactor: Float = 0.95

class MotionManagerSingletonSwift: NSObject {


var motionManager: CMMotionManager
var referenceAttitude:CMAttitude?=nil
var bActive = false
var lastVector:[Float] = [0.0, 0.0, 0.0]


override init()  {
    motionManager=CMMotionManager()
    motionManager.deviceMotionUpdateInterval = 0.25
    motionManager.startDeviceMotionUpdates()
    bActive=true;
}

// only one instance of CMMotionManager can be used in your project.
// => Implement as Singleton which can be used in the whole application
class var sharedInstance: MotionManagerSingletonSwift {
struct Singleton {
    static let instance = MotionManagerSingletonSwift()
    }
    return Singleton.instance
}

class func getMotionManager()->CMMotionManager {
    if (sharedInstance.bActive==false) {
        sharedInstance.motionManager.startDeviceMotionUpdates()
        sharedInstance.bActive=true;

    }
    return sharedInstance.motionManager
}

// Returns an array with the movements
// At the first time a reference orientation is saved to ensure the motion detection works
// for multiple device positions
class func getMotionVectorWithLowPass() -> [Float] {
    // Motion
    var attitude: CMAttitude? = getMotionManager().deviceMotion?.attitude

    if sharedInstance.referenceAttitude==nil {
        // Cache Start Orientation to calibrate the device. Wait for a short time to give MotionManager enough time to initialize
        dispatch_after(250, dispatch_get_main_queue(), {
            MotionManagerSingletonSwift.calibrate()
            })
    } else if attitude != nil {
        // Use start orientation to calibrate
        attitude!.multiplyByInverseOfAttitude(sharedInstance.referenceAttitude)
    }

    if attitude != nil {
        return lowPassWithVector([Float(attitude!.yaw), Float(attitude!.roll), Float(attitude!.pitch)])
    } else {
        return [0.0, 0.0, 0.0]
    }
}

// Stop collection motion data to save energy
class func stop() {
    sharedInstance.motionManager.stopDeviceMotionUpdates()
    sharedInstance.referenceAttitude=nil
    sharedInstance.bActive=false
}

// Calibrate motion manager with a ne reference attitude
class func calibrate() {
    sharedInstance.referenceAttitude = getMotionManager().deviceMotion?.attitude?.copy() as? CMAttitude
}


// Damp the jitter caused by hand movement
class func lowPassWithVector(var vector:[Float]) -> [Float]
{
    vector[0] = vector[0] * cLowPassFactor + sharedInstance.lastVector[0] * (1.0 - cLowPassFactor)
    vector[1] = vector[1] * cLowPassFactor + sharedInstance.lastVector[1] * (1.0 - cLowPassFactor)
    vector[2] = vector[2] * cLowPassFactor + sharedInstance.lastVector[2] * (1.0 - cLowPassFactor)

    sharedInstance.lastVector = vector
    return sharedInstance.lastVector
}
}

Полное руководство: http://developerplayground.net/?p=19

person Stefan    schedule 04.12.2015
comment
большое спасибо! Сейчас я попробую адаптировать этот подход к моему коду. - person agovan; 07.12.2015
comment
возможно, глупый вопрос, но я предполагаю, что массив с плавающей запятой, который вы создаете для lastvector, имеет значения x, y, z в этом порядке? Итак, если бы я должен был извлечь движение из этого, я бы создал экземпляр MotionManger в viewDidLoad, а затем вызывал бы lastVector и вычислял свою позицию каждый тик в обновлении? - person agovan; 07.12.2015