2
votes

Currently working on a game in scenekit with swift, and i'm trying to implement camera constraints to take full advantage of everything scenekit has to offer. I am very close to getting what I want, but i am missing one piece and cannot seem to figure it out. Videos of the issue below.

This video shows my camera following my ship with code I wrote. The orientation and angle are good, and it follows with a nice inertia. I achieve this with the follow code:

func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
    updateCameraPosition()
}

func updateCameraPosition () {
    let currentPosition = player.node.presentation.position
    let newVector = getNewCameraVector(currentPosition: currentPosition, t: 0.03 )

    cameraNode.runAction( SCNAction.move(to: newVector, duration: 0.2) )

    prevCameraPosition = currentPosition
}

func getNewCameraVector (currentPosition:SCNVector3, t: Float) -> SCNVector3 {
    let x = (1 - t) * prevCameraPosition.x + t * currentPosition.x
    let y = cameraZoom
    let z = ((1 - t) * prevCameraPosition.z + t * currentPosition.z) + cameraZPos!

    return SCNVector3(x,y,z)
}

This video shows the camera following my ship with constraints. It seems to just be rotating on the x,z axis. So i'd really like for it to move back and forth across the x,z axis like the code I wrote... or something similar. These constraints are taken from the WWDC 2017 scenekit fox demo, and i've implimented them like so:

    // look at "lookAtTarget"
    let lookAtConstraint = SCNLookAtConstraint(target: player.node)
    lookAtConstraint.influenceFactor = 0.07
    lookAtConstraint.isGimbalLockEnabled = true

    // distance constraints
    let distanceConstraint = SCNDistanceConstraint(target: player.node)
    let distance = CGFloat(simd_length(cameraNode.simdPosition))
    distanceConstraint.minimumDistance = distance
    distanceConstraint.maximumDistance = distance

    // configure a constraint to maintain a constant altitude relative to the character
    //let desiredAltitude = abs(cameraNode.simdWorldPosition.y)
    //weak var weakSelf = self

    let keepAltitude = SCNTransformConstraint.positionConstraint(inWorldSpace: true, with: {(_ node: SCNNode, _ position: SCNVector3) -> SCNVector3 in
        return SCNVector3(0, self.cameraZoom, self.cameraZoom)
    })

    let accelerationConstraint = SCNAccelerationConstraint()
    accelerationConstraint.maximumLinearVelocity = 1500.0
    accelerationConstraint.maximumLinearAcceleration = 50.0
    accelerationConstraint.damping = 0.05

    cameraNode.constraints = [distanceConstraint, keepAltitude, accelerationConstraint, lookAtConstraint]

If anybody has any idea how i can achieve this i'd love to hear it! There isn't any documentation about these new constraints so i'm kind of just guessing when i put them on.

1

1 Answers

4
votes

Answering my own question in hopes it helps others. I was finnally able to replicate the camera code with the following constraints:

func setupCamera () {
    cameraNode.camera = SCNCamera()
    cameraZPos = cameraZoom / 2 // cameraZoom == 100

    if let cam = cameraNode.camera {
        cam.zFar = cameraZFar
    }

    let replicatorConstraint = SCNReplicatorConstraint(target: player.node)
    replicatorConstraint.positionOffset = SCNVector3(0,cameraZoom,cameraZPos!)
    replicatorConstraint.replicatesOrientation = false

    let lookAtConstraint = SCNLookAtConstraint(target: player.node)
    lookAtConstraint.influenceFactor = 0.07
    lookAtConstraint.isGimbalLockEnabled = true

    let accelerationConstraint = SCNAccelerationConstraint()
    accelerationConstraint.maximumLinearAcceleration = 300.0

    cameraNode.constraints = [replicatorConstraint, lookAtConstraint, accelerationConstraint]
}

Comment on this answer if you have any questions and i'll be happy to share what I've learnt!