【问题标题】:cameranode rotate as iOS device movingcameranode随着iOS设备移动而旋转
【发布时间】:2015-10-27 16:02:49
【问题描述】:

这是 360 度视频播放器项目。

我添加一个cameranode(SCNNode)到一个rootnode,cameranode被放在SCNSphere的中心(0,0,0),它现在可以播放视频了。

现在我必须使用 devicemotion。当设备移动时,我需要旋转相机。不只是旋转某个角度。 (不只是设备移动,当我拿着设备时,我的移动被认为是设备移动。因为我发现如果我使用deviceMotion.attitude.roll,cameranode只会在设备自行移动时移动,而不是当我带着设备转圈时)

当设备位于 (x1,y1,z1) 位置时,cameranode 会随着设备的移动而旋转,当设备再次位于 (x1,y1,z1) 位置时,视图应与我们最后一次离开。

这是我所做的:

if (self.motionManager.gyroAvailable) {
        self.motionManager.gyroUpdateInterval = 1.0/60.0;
        [self.motionManager startGyroUpdatesToQueue:self.queue withHandler:^(CMGyroData *gyroData, NSError *error){
            if (error) {
                [self.motionManager stopGyroUpdates];
                NSLog(@"Gyroscope encountered error:%@",error);
            }else {
                CGFloat tempX = 0;
                CGFloat tempY = 0;
                CGFloat tempZ = 0;
                if ((fabs(gyroData.rotationRate.y)/60) > 0.002) {
                    tempY = gyroData.rotationRate.y/60;
                }
                tempX = gyroData.rotationRate.x/60;
                tempZ = gyroData.rotationRate.z/60;
                [self.cameraNode runAction:[SCNAction rotateByX:-tempY y:tempX z:tempZ duration:0]];
            }
        }];
    }else {
        NSLog(@"This device has no gyroscope");
    }

数学可能是错误的,我不知道为什么要除以 60,但是当我使用 [self.cameraNode runAction:[SCNAction rotateByX:-tempY y:tempX z:tempZ duration:0]]; 时,它似乎是我需要的最接近的值

问题出在哪里?

  1. 我应该使用哪些数据?

CMGyroData 或 CMDeviceMotion。如果使用CMDeviceMotion,我应该使用哪个具体值? deviceMotion.attitude、deviceMotion.attitude.quaternion、deviceMotion.gravity

  1. 旋转相机节点的正确方法是什么?方法很多,我不确定。

+ (SCNAction *)rotateByX:(CGFloat)xAngle y:(CGFloat)yAngle z:(CGFloat)zAngle duration:(NSTimeInterval)duration;

+ (SCNAction *)rotateToX:(CGFloat)xAngle y:(CGFloat)yAngle z:(CGFloat)zAngle duration:(NSTimeInterval)duration;

【问题讨论】:

    标签: ios scenekit cmmotionmanager scnnode


    【解决方案1】:

    设备运动是最好的,因为它连接多个传感器并以有意义的方式将数据融合在一起。最好避免使用欧拉角,因为它们会受到所谓的万向节锁定的影响。相反,请使用四元数并使用它们设置您的相机方向。

    我创建了a Swift class extension for CMDeviceMotion,它可以让您确定设备正在查看的内容(对于屏幕的任何方向)。

    从那里可以直接旋转您的相机。首先,设置你的设备动作(在视图中确实出现了,或者其他一些合适的地方):

    if motionManager.deviceMotionAvailable {
    
        motionManager.deviceMotionUpdateInterval = 0.017
        motionManager.startDeviceMotionUpdatesToQueue(NSOperationQueue(), withHandler: deviceDidMove)
    }
    

    然后在你的 deviceDidMove 实现中处理这些更新,使用前面提到的 CMDeviceMotion 扩展:

    func deviceDidMove(motion: CMDeviceMotion?, error: NSError?) {
    
        if let motion = motion {
    
            yourCameraNode.orientation = motion.gaze(atOrientation: UIApplication.sharedApplication().statusBarOrientation)
        }
    }
    

    现在您的相机应该跟随您的设备在太空中的方向。

    【讨论】:

    • 这应该被标记为正确答案。为了完整起见,最好 (1) 将 cmets 添加到您的类扩展中以解释它的具体作用,以及 (2) 将框架导入语句添加到文件顶部。
    • 在您的解决方案中,当您构造最终的 SCNQuaternion 时,您能解释一下为什么有时需要交换 x 和 y 的符号和值吗?我知道这是在所有情况下工作的必要条件。我们可以用另一个乘法向量代替吗?
    • @AlfieHanssen 标志和交换正在纠正设备的姿态。如果有更聪明的方法来计算它们,我不知道。但我同意,这很好。
    • @Travis 感谢您非常有用的扩展。你知道如何阻止我相机中的滚动组件吗? stackoverflow.com/q/39026329/235297
    【解决方案2】:

    Swift 5.0 / iOS 13 更新

    使用最初答案中的精彩扩展,您可以简单地执行以下操作:

    override func viewDidAppear(_ animated: Bool) {
        // let motionManager = CMMotionManager() // definition made globally
        
        // CoreMotion
        motionManager.startDeviceMotionUpdates()
        motionManager.deviceMotionUpdateInterval = 1.0 / 60.0
        
        let interfaceOrientation = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.windowScene?.interfaceOrientation // for iOS13 and later
        
        // .xTrueNorthZVertical // always aligns to the real north
        // .xArbitraryZVertical // will use this one for initial direction
        motionManager.startDeviceMotionUpdates(using: .xArbitraryZVertical, to: OperationQueue.main, withHandler: { (motion: CMDeviceMotion?, err: Error?) in
            guard let m = motion else { return }
            // self.cameraNode.orientation = m.gaze(atOrientation: UIApplication.shared.statusBarOrientation) // before iOS 13
            self.cameraNode.orientation = m.gaze(atOrientation: interfaceOrientation!) // for iOS13 and later
        })
    }
    

    【讨论】:

      【解决方案3】:

      对于 iOS 15,使用初始答案的扩展名

      override func viewDidAppear(_ animated: Bool) {
          // let motionManager = CMMotionManager() // definition made globally
      // CoreMotion
      motionManager.startDeviceMotionUpdates()
      motionManager.deviceMotionUpdateInterval = 1.0 / 60.0
      
      //this following line is changed to avoid deprecated term 
      let interfaceOrientation = keyWindow!.windowScene?.interfaceOrientation
      
      // .xTrueNorthZVertical // always aligns to the real north
      // .xArbitraryZVertical // will use this one for initial direction
      motionManager.startDeviceMotionUpdates(using: .xArbitraryZVertical, to: OperationQueue.main, withHandler: { (motion: CMDeviceMotion?, err: Error?) in
          guard let m = motion else { return }
          // self.cameraNode.orientation = m.gaze(atOrientation: UIApplication.shared.statusBarOrientation) // before iOS 13
          self.cameraNode.orientation = m.gaze(atOrientation: interfaceOrientation!) // for iOS13 and later
      })
      }
      
      extension (change name UIApplication) {
      
      var keyWindow: UIWindow? {
          // Get connected scenes
          return UIApplication.shared.connectedScenes
              // Keep only active scenes, onscreen and visible to the user
              .filter { $0.activationState == .foregroundActive }
              // Keep only the first `UIWindowScene`
              .first(where: { $0 is UIWindowScene })
              // Get its associated windows
              .flatMap({ $0 as? UIWindowScene })?.windows
              // Finally, keep only the key window
              .first(where: \.isKeyWindow)
      }
      

      【讨论】:

        猜你喜欢
        • 2023-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-14
        • 2013-02-20
        相关资源
        最近更新 更多