Search code examples
iosswiftcamerascenekit

How to ensure SceneKit camera always points at specific position


I'm writing a game in Swift 5 (but had the same problem with Swift 4 which i recently updated by game from), with all my SCNNode's centered around SCNVector3Zero. In other word's (0,0,0) is at the center of my play area.

The SCNCamera is attached to a SNCNode and positioned just outside the limits of the play area, looking at (0,0,0).

cameraNode.look(at: SCNVector3Zero)

If I place 5 nodes in the scene, all on the y=0 plane, (0,0,0), (-1,0,-1), (1,0,1), (-1,0,1) and (1,0,-1) like the face of a dice showing five, it all works great. I can rotate around the scene with the node at (0,0,0) staying centered with no movement.

If I add a 2nd row, so the first row moves to y=1 and the second row gets nodes with y=-1 the scene wobbles a little when rotating.

The further a node is moved from the center, the more exaggerated this wobble becomes.

Here's the code setting up the scene (this example has three rows and looks like a three dimensional "five" face of a dice, point in the center at (0,0,0) and the other dots at each corner of a cube) ...

        addSphere(
            x: 0.0,
            y: 0.0,
            z: 0.0,
            radius: radius,
            textureName: "earth")

        addSphere(
            x: -2.0 * spacing,
            y: -2.0 * spacing,
            z: 0.0,
            radius: radius,
            textureName: "earth")
        addSphere(
            x: 2.0 * spacing,
            y: 2.0 * spacing,
            z: 0.0,
            radius: radius,
            textureName: "earth")


        addSphere(
            x: -2.0 * spacing,
            y: 2.0 * spacing,
            z: 0.0,
            radius: radius,
            textureName: "granite")
        addSphere(
            x: 2.0 * spacing,
            y: -2.0 * spacing,
            z: 0.0,
            radius: radius,
            textureName: "granite")


        addSphere(
            x: 0.0,
            y: -2.0 * spacing,
            z: -2.0 * spacing,
            radius: radius,
            textureName: "slime")
        addSphere(
            x: 0.0,
            y: 2.0 * spacing,
            z: 2.0 * spacing,
            radius: radius,
            textureName: "slime")


        addSphere(
            x: 0.0,
            y: 2.0 * spacing,
            z: -2.0 * spacing,
            radius: radius,
            textureName: "wood")
        addSphere(
            x: 0.0,
            y: -2.0 * spacing,
            z: 2.0 * spacing,
            radius: radius,
            textureName: "wood")

This code behaves, everything is symmetrical around (0,0,0) with the node at (0,0,0) staying exactly where it should.

If I introduce this node into the scene, it all goes badly wrong ...

        addSphere(
            x: 6.0,
            y: 6.0,
            z: 6.0,
            radius: radius,
            textureName: "earth")

I've tried adding a fixed physics body to each node that has a mass of zero to no avail. It's like the camera is no longer looking directly at (0,0,0) but is influenced but the nodes in the scene.

I've tried all sorts of permutations of adding nodes, and on some tests it appears adding anything with value for the z-axis caused problems

This is the addSphere method ...

    internal func addSphere(x: Float, y: Float, z: Float, radius: Float, color: UIColor, textureName: String) -> SCNNode {

        let sphereGeometry = SCNSphere(radius: CGFloat(radius))
        sphereGeometry.firstMaterial?.diffuse.contents = UIImage(imageLiteralResourceName: textureName)

        let sphereNode = SCNNode(geometry: sphereGeometry)
        sphereNode.name = "dot"

        sphereNode.position = SCNVector3(x: x, y: y, z: z)
        sphereNode.lines = [];
        sphereNode.ignoreTaps = false
        sphereNode.categoryBitMask = NodeBitMasks.dot

        //sphereNode.physicsBody = SCNPhysicsBody(type: .static, shape: nil)

        self.rootNode.addChildNode(sphereNode)

        return sphereNode
    }

There are no errors and as you might expect, any point positioned at (0,0,0) should remain static when rotating about while "looking at" this point.

Any pointers would be really appreciated as I can't make heads or tails of why it's behaving like this.


Solution

  • I assume you use the SCNView.allowsCameraControl = true to move the camera around. This built-in setting of SceneKit is actually only for debug purposes of your scene. It is not suited for anything when you want to have dedicated control over your camera movement.

    You should instead try to implement a camera orbit, see https://stackoverflow.com/a/25674762/3358138.

    I could reproduce your problem with the "wobbling" center of your scene, see Playground Gist https://gist.github.com/dirkolbrich/e2c247619b28a287c464abbc0595e23c.

    A camera orbit solves this "wobbling" and let’s the camera stay on center, see Playground Gist https://gist.github.com/dirkolbrich/9e4dffb3026d0540d6edf6877f27d1e4.