1
votes

In general, when one wants to make a character in a game face the camera, one can use SCNLookAtConstraint, which actually is serving me well too.

Below, my_object is the node which I am trying to orient as per constraint. 'Enemy' refers to some node in the scene, and pointOfView is scene's point of View. When I tap on my_object it should look at enemy for a brief second, then back to my pointOfView.

Setting LookAtConstraint so that the 'object' looks at the 'enemy' for a second.

my_object.constraints?.removeAll()
let targetNode = sceneView.scene.rootNode.childNode(withName: (chosenScenarioForChallenge?.shape)!, recursively: true)
let lookAt = SCNLookAtConstraint(target: targetNode)
lookAt.isGimbalLockEnabled = true
my_object.constraints = [lookAt]                    
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(resetObject), userInfo: nil, repeats: false)

Resetting object to look at my point of view after a look at the 'enemy' node

@objc
func resetObject() {
    my_object.constraints?.removeAll()
    let targetNode = sceneView.pointOfView
    let lookAt = SCNLookAtConstraint(target: targetNode)
    lookAt.isGimbalLockEnabled = true
    my_object.constraints = [lookAt]
    }

My question is, I don't want the change in constraints to be abrupt. I want it to be smooth and hence I want to incorporate Animation in this. After looking at both SCNAction and SCNAnimation, I have failed to find anything related to constraints. As constraints update the position dynamically, I can understand SCNAction.move(to/ by: ) etc. won't work. Ultimately, when I tap, my object should start looking at the enemy node gradually and after 1-2 seconds goes by, it should come back to look at my pointOfView, that too gradually.

Please note that I don't want the character to look into camera technically, hence I am using SCNLookAtConstraint, rather than SCNBillboardConstraint.

Pointers would be helpful, TIA.

3

3 Answers

4
votes

the target property is read-write so you should be able to reuse the SCNLookAtConstraint instance (instead of creating a new one) and just swap the target.

Edit

The influenceFactor of a constraint allows for a smoother animation between the node's current transform and the one computed by the constraint.

1
votes

Based on testing the answer of @Managanese I can say it works, somehow the influenceFactor and the animationDuration work hand in hand. I don't really understand, why the setting of the influenceFactor is necessary, it should work without, but somehow, it is.

I show a code sample for others struggling with the problem (Swift 5.0, Xcode 11.2):

private func smoothlyLookAtTarget() {

    // influenceFactor and animationDuration work somehow together

    let constraint = SCNLookAtConstraint(target: targetNode)
    constraint.isGimbalLockEnabled = true
    constraint.influenceFactor = 0.1

    SCNTransaction.begin()
    SCNTransaction.animationDuration = 3.0
    cameraNode.constraints = [constraint]
    SCNTransaction.commit()
}
0
votes

Another approach is to lock the camera to a cameraTargetNode using a look constraint, then moving that node via animation or even setting constraints on that node to maintain distance with or replicate a target.position. This way you can set the influence once on the cameras constraints to a suitable fudge value. The camera keeps its constraints throughout its lifecycle and you can enable/disable it at will.

Another benefit of this approach is debugging. You can give the cameraTargetNode a geometry of your choice during development and track the cameras target without having to infer or feel what the actual targets is.

I take this a step further and use a cameraMovementNode with distance and acceleration constraints set on the cameraNode that allows me to see where the look and movement nodes are. Influence factor and isEnabled are helpful here too as you can adjust the influence or disable the constraint and leave the constraints target alone. Sometimes I notice that setting the target, changing/setting constraints or even moving the camera in and out of the tree graph causes weird rotations and spins.

With my approach you can move the look or move targets to other children in the scene and not have any weird behavior. This way you can move the moveMode via action/transaction and when it’s done, add it as a child of the target and get following and rotations either the target. Also, a reset() can handle clearing of constraints from those camera helper nodes and moving them to the root of the scene as necessary.