使用CMDeviceMotion如何获取绝对旋转?

4
我正在使用Sprite Kit构建一个简单的游戏,屏幕不会旋转,但我想知道用户拿着手机的角度,以便进行游戏机制。
我想获取的值可以通过加速计(x,y)轻松获取,但我发现这种方法不可靠,所以我正在尝试使用CMDeviceMotion来获得更好的结果。我可以获得相当于data.acceleration.y的值,但我无法弄清如何获得相当于data.acceleration.x的值。
if let data = motionManager.accelerometerData? {
    let y = CGFloat(data.acceleration.y)
    let x = CGFloat(data.acceleration.x)
}

// Device Motion
if let attitude = motionManager.deviceMotion?.attitude? {
    let y = CGFloat(-attitude.pitch * 2 / M_PI) // This matches closely with data.acceleration.y
    let x = ??????????
}

我如何使用CMDeviceMotion计算等效于data.acceleration.x的值?

4
您需要的不是旋转功能,而是陀螺仪API。请查看https://developer.apple.com/library/ios/documentation/coremotion/reference/cmmotionmanager_class/Reference/Reference.html。 - Kevin DiTraglia
3个回答

8
听起来你想要使用运动管理器的设备运动,如果你想让旋转值考虑到陀螺仪和加速度计的输入。根据文档:

CMDeviceMotion的实例封装了设备的姿态、旋转速率和加速度的测量。

所以,不要直接监视陀螺仪数据,而是监视设备运动。下面的示例演示了如何按帧获取设备运动。我决定直接打印CMAttitude对象,但是从这个对象中,你可以直接访问设备的俯仰、翻滚和偏航(以及更多),如果我没记错的话,这正是你要找的。
import SpriteKit
import CoreMotion

class GameScene: SKScene {
    let motionManager = CMMotionManager()

    override func didMoveToView(view: SKView) {
        motionManager.deviceMotionUpdateInterval = 1.0 / 30.0
        motionManager.startDeviceMotionUpdates()
    }

    override func willMoveFromView(view: SKView!) {
        motionManager.stopDeviceMotionUpdates()
    }

    override func update(currentTime: CFTimeInterval) {
        if let attitude = motionManager.deviceMotion?.attitude? {
            println(attitude)
            let y = CGFloat(-attitude.pitch * 2 / M_PI)
            let x = CGFloat(-attitude.roll * 2 / M_PI)
        }
    }
}

如果您正在使用Swift 2,需要进行一些微小的更改,如下所示。
class GameScene: SKScene {
    let motionManager = CMMotionManager()

    override func didMoveToView(view: SKView) {
        motionManager.deviceMotionUpdateInterval = 1.0 / 30.0
        motionManager.startDeviceMotionUpdates()
    }

    override func willMoveFromView(view: SKView) {
        motionManager.stopDeviceMotionUpdates()
    }

    override func update(currentTime: CFTimeInterval) {
        if let attitude = motionManager.deviceMotion?.attitude {
            print(attitude)
            let y = CGFloat(-attitude.pitch * 2 / M_PI)
            let x = CGFloat(-attitude.roll * 2 / M_PI)
        }
    }
}

有关此事的更多信息,请查阅this image

enter image description here


嗨@0x7fffffff,我正在尝试使用CMAttitude获取绝对旋转,就像你建议的那样。我可以使用-attitude.pitch * 2 / M_PI检索Y,但我不知道如何检索X。我为此添加了额外的赏金。 - lisovaccaro
@lisovaccaro,我更新了我的代码。如果我正确理解问题的话,它应该会有所帮助。 - Mick MacCallum
嗨@ 0x7fffffff,我一直在阅读有关俯仰,翻滚和偏航的内容,但我还没有能够将其转换为绝对旋转。我认为我已经想出了一个计算x的算法(请参见我的答案),但我不确定它是否是最可靠的。 - lisovaccaro

2
您可以通过读取CoreMotion提供的陀螺仪数据来获取角度。像这样初始化一个CM对象:
class GameScene: SKScene {
    var motionManager = CMMotionManager()
    override func didMoveToView(view: SKView) {
        motionManager.startGyroUpdates()
    }
}

然后在更新函数中,您可以读取它:
override func update(currentTime: CFTimeInterval) {
    if let data = motionManager.gyroData {
        let x = data.rotationRate.x
        let y = data.rotationRate.y
        let z = data.rotationRate.z
    }
}

现在您可以使用x、y和z值来进行游戏机制。但是请注意,它提供的是当前旋转速率而不是绝对旋转。如果需要,您可以自行跟踪它。或者,您可以使用加速度计数据:

motionManager.startAccelerometerUpdates()
...
if let data = motionManager.accelerometerData {
    let x = data.acceleration.x
}

嗨teshel,我添加了一个赏金,请问您能否添加代码以获取加速度计和陀螺仪的绝对旋转值?(相当于data.acceleration.x和data.acceleration.y) - lisovaccaro

0

这是我现在尝试的。我通过实验得到了它,所以我不能确定它是否正确,可能有更好的解决方案。

            if let attitude = motionManager.deviceMotion?.attitude? {
                 // Convert pitch and roll to [-1, 1] range
                let pitch = CGFloat(-attitude.pitch * 2 / M_PI)
                let roll = CGFloat(abs(attitude.roll) > M_PI / 2 ? (2 - abs(attitude.roll) * 2 / M_PI) * (attitude.roll > 0 ? 1 : -1) : attitude.roll * 2 / M_PI)

                // Calculate x and y
                let y = pitch
                let x = roll*(1.0-abs(pitch))
            }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接