【问题标题】:Camera is not following the airplane in Scenekit相机没有跟随 Scenekit 中的飞机
【发布时间】:2018-01-08 22:00:20
【问题描述】:

我有一架飞行的飞机,我正在跟随它,我还展示了飞机所遵循的路径。我正在绘制圆柱体作为绘制路径的线。它有点像在两点之间画一条线。我有一个最初设置为 (0,200,200) 的 cameraNode。那时我可以看到飞机。但是当我开始飞行时。它走出屏幕。我想要两件事:

  1. 只跟随飞机(路径无关紧要)。
  2. 显示整个路径以及飞机。

我尝试找到最小广告最大 x、y 和 z 并取平均值,但它不起作用。如果你看到下面的 gif 它太放大了,飞机已经移出屏幕

这是我设置相机的方法:

- (void)setUpCamera {
SCNScene *workingScene = [self getWorkingScene];
_cameraNode = [[SCNNode alloc] init];
_cameraNode.camera = [SCNCamera camera];
_cameraNode.camera.zFar = 500;
_cameraNode.position = SCNVector3Make(0, 60, 50);
[workingScene.rootNode addChildNode:_cameraNode];

SCNNode *frontCameraNode = [SCNNode node];
frontCameraNode.position = SCNVector3Make(0, 100, 50);
frontCameraNode.camera = [SCNCamera camera];
frontCameraNode.camera.xFov = 75;
frontCameraNode.camera.zFar = 500;
[_assetActivity addChildNode:frontCameraNode]; //_assetActivity is the aircraft node.

}

以下是我如何更改不起作用的相机位置:

- (void)showRealTimeFlightPath {


DAL3DPoint *point = [self.aircraftLocation convertCooridnateTo3DPoint];
DAL3DPoint *previousPoint = [self.previousAircraftLocation convertCooridnateTo3DPoint];
self.minCoordinate = [self.minCoordinate findMinPoint:self.minCoordinate currentPoint:point];
self.maxCoordinate = [self.minCoordinate findMaxPoint:self.maxCoordinate currentPoint:point];
DAL3DPoint *averagePoint = [[DAL3DPoint alloc] init];
averagePoint = [averagePoint averageBetweenCoordiantes:self.minCoordinate maxPoint:self.maxCoordinate];

SCNVector3 positions[] = {
    SCNVector3Make(point.x,point.y,point.z) ,
    SCNVector3Make(previousPoint.x,previousPoint.y,previousPoint.z)
};
SCNScene *workingScene = [self getWorkingScene];
DALLineNode *lineNodeA = [[DALLineNode alloc] init];
[lineNodeA init:workingScene.rootNode v1:positions[0] v2:positions[1] radius:0.1 radSegementCount:6 lineColor:[UIColor greenColor]] ;
[workingScene.rootNode addChildNode:lineNodeA];
self.previousAircraftLocation = [self.aircraftLocation mutableCopy];
self.cameraNode.position = SCNVector3Make(averagePoint.x, averagePoint.y, z);
self.pointOfView = self.cameraNode;

}

欢迎使用 swift 或 Objective c 中的代码。

谢谢!!

【问题讨论】:

  • 如果我理解正确,您是在问如何实现两种不同的相机行为?另外,你能澄清一下“只跟随飞机”的意思吗?摄像头应该与飞机保持最小距离,还是只是更新以朝其方向看?
  • @ErikFoss 是的,我想要 2 种行为: 1. 跟随飞机(相机只会关注飞机而不是路径)。 2.在一个屏幕上显示整个路径和飞机。相机必须与飞机保持一定距离。

标签: ios objective-c swift scenekit


【解决方案1】:

您描述的第一个行为最容易通过链接查看约束和距离约束来实现,两者都针对飞机。

let lookAtConstraint = SCNLookAtConstraint(target: aircraft)

let distanceConstraint = SCNDistanceConstraint(target: aircraft)
distanceConstraint.minimumDistance = 10 // set to whatever minimum distance between the camera and aircraft you'd like
distanceConstraint.maximumDistance = 10 // set to whatever maximum distance between the camera and aircraft you'd like

camera.constraints = [lookAtConstraint, distanceConstraint]

对于 iOS 10 及更早版本,您可以使用 SCNTransformConstraint 实现距离约束。这是一个基本的(虽然有点难看?)实现,它使用线性插值来更新节点的位置。

func normalize(_ value: Float, in range: ClosedRange<Float>) -> Float {
    return (value - range.lowerBound) / (range.upperBound - range.lowerBound)
}

func interpolate(from start: Float, to end: Float, alpha: Float) -> Float {
    return (1 - alpha) * start + alpha * end
}

let target = airplane

let minimumDistance: Float = 10
let maximumDistance: Float = 15

let distanceConstraint = SCNTransformConstraint(inWorldSpace: false) { (node, transform) -> SCNMatrix4 in
    let distance = abs(sqrt(pow(target.position.x - node.position.x, 2) + pow(target.position.y - node.position.y, 2) + pow(target.position.z - node.position.z, 2)))

    let normalizedDistance: Float

    switch distance {
    case ...minimumDistance:
        normalizedDistance = self.normalize(minimumDistance, in: 0 ... distance)
    case maximumDistance...:
        normalizedDistance = self.normalize(maximumDistance, in: 0 ... distance)
    default:
        return transform
    }

    node.position.x = self.interpolate(from: target.position.x, to: node.position.x, alpha: normalizedDistance)
    node.position.y = self.interpolate(from: target.position.y, to: node.position.y, alpha: normalizedDistance)
    node.position.z = self.interpolate(from: target.position.z, to: node.position.z, alpha: normalizedDistance)

    return transform
}

第二种行为可以通过确定您的飞机的边界框及其在相机的局部坐标空间中的所有路径段来实现,然后更新相机与该边界框中心的距离以将所有这些节点框在视口。 frameNodes(_:),一种实现此功能的便捷方法,在 iOS 11 中引入,并在 SCNCameraController 上定义。如果可能,我建议您使用它,除非您想自己深入了解三角学。您可以使用场景视图的默认相机控制器或创建一个临时实例,以适合您的应用程序的需要。

【讨论】:

  • 边界框的意思是求x,y,z的最大值和最小值,求平均值?另外,你能给出一个如何使用frameNodes的示例代码吗?
  • 好吧,我不知道你是如何存储你想要构图的节点的,所以我不能为你写那部分。您需要做的第一件事是将它们放入数组中。然后检索或创建相机控制器;如果您没有使用场景视图的默认相机控制器,只需创建一个新实例 (let cameraController = SCNCameraController()) 并分配当前视点 (cameraController.pointOfView = sceneView.pointOfView)。然后调用frameNodes(_:),传递你的数组(cameraController.frameNodes(nodes))。这应该就是你需要做的!
  • “边界框”是指包围所有节点的矩形体积。该边界框的中心是向或远离移动以达到框架节点的目标距离的点。
  • 所有这些方法都在 ios 11 beta 中。它们不适用于 xcode 8。
  • 抱歉,我不应该假设您使用的是测试版。使用SCNTransformConstraint 可以很容易地实现距离约束,并且SCNLookAtConstraint 在iOS 8 及更高版本中可用。如果你愿意,我可以用一个例子来编辑我的帖子。
【解决方案2】:

您需要计算速度的角度,以便相机指向移动的 SCNNode 的方向。

此代码将为您指明正确的方向。

    func renderer(_ aRenderer: SCNSceneRenderer, didSimulatePhysicsAtTime time: TimeInterval) {

        // get velocity angle using velocity of vehicle

        var degrees = convertVectorToAngle(vector: vehicle.chassisBody.velocity)

        // get rotation of current camera on X and Z axis

        let eX = cameraNode.eulerAngles.x
        let eZ = cameraNode.eulerAngles.z

        // offset rotation on y axis by 90 degrees

        // this needs work, buggy

        let ninety = deg2rad(90)

        // default camera Y Euler angle facing north at 0 degrees

        var eY : Float = 0.0

        if degrees != 0 {

            eY = Float(-degrees) - Float(ninety)

        }

        // rotate camera direction using cameraNode.eulerAngles and direction of velocity as eY

        cameraNode.eulerAngles = SCNVector3Make(eX, eY, eZ)

        // put camera 25 points behind vehicle facing direction of velocity

        let dir = calculateCameraDirection(cameraNode: vehicleNode)
        let pos = pointInFrontOfPoint(point: vehicleNode.position, direction:dir, distance: 25)

        // camera follows driver view from 25 points behind, and 10 points above vehicle

        cameraNode.position = SCNVector3Make(pos.x, vehicleNode.position.y + 10, pos.z)

}

func convertVectorToAngle(vector: SCNVector3) -> CGFloat {

    let degrees = atan2(vector.z, vector.x)

    return CGFloat(degrees)

}


func pointInFrontOfPoint(point: SCNVector3, direction: SCNVector3, distance: Float) -> SCNVector3 {

    var x = Float()
    var y = Float()
    var z = Float()

    x = point.x + distance * direction.x
    y = point.y + distance * direction.y
    z = point.z + distance * direction.z

    let result = SCNVector3Make(x, y, z)
    return result

}

func calculateCameraDirection(cameraNode: SCNNode) -> SCNVector3 {

            let x = -cameraNode.rotation.x
            let y = -cameraNode.rotation.y
            let z = -cameraNode.rotation.z
            let w = cameraNode.rotation.w
            let cameraRotationMatrix = GLKMatrix3Make(cos(w) + pow(x, 2) * (1 - cos(w)),
                                                      x * y * (1 - cos(w)) - z * sin(w),
                                                      x * z * (1 - cos(w)) + y*sin(w),

                                                      y*x*(1-cos(w)) + z*sin(w),
                                                      cos(w) + pow(y, 2) * (1 - cos(w)),
                                                      y*z*(1-cos(w)) - x*sin(w),

                                                      z*x*(1 - cos(w)) - y*sin(w),
                                                      z*y*(1 - cos(w)) + x*sin(w),
                                                      cos(w) + pow(z, 2) * ( 1 - cos(w)))

            let cameraDirection = GLKMatrix3MultiplyVector3(cameraRotationMatrix, GLKVector3Make(0.0, 0.0, -1.0))
            return SCNVector3FromGLKVector3(cameraDirection)
}

func deg2rad(_ number: Double) -> Double {

        return number * .pi / 180

}

【讨论】:

    猜你喜欢
    • 2017-08-28
    • 2016-02-24
    • 2017-09-12
    • 2017-01-29
    • 2018-12-29
    • 1970-01-01
    • 2021-12-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多