3
votes

I am attempting to implement a first-person space shooter in Scenekit, and I am having the (familiar, I know) problem of getting the physics simulation match the actual position and transform of the SCNNodes the physics simulation is supposed to represent.

The enemy drone ship is created using this function, which places the node in a SCNnode called SectorObjectNode which contains all game objects external to the ship(enemies, stars, etc) and its torpedoes (both of which live in the scene's root node:

func spawnDrone(_ sender: UIButton) {

    let humonshipScene = SCNScene(named: "Humon.scn")
    let humonShip = humonshipScene?.rootNode.childNodes[0]
    self.enemyDrone = humonShip
    let droneShape = SCNBox(width: 10, height: 5, length: 5, chamferRadius: 0)
    let dronePhysicsShape = SCNPhysicsShape(geometry: droneShape, options: nil)
    self.enemyDrone?.physicsBody = SCNPhysicsBody(type: .dynamic, shape: dronePhysicsShape)
    self.enemyDrone?.physicsBody?.isAffectedByGravity = false
    self.enemyDrone?.physicsBody?.friction = 0
    self.enemyDrone?.physicsBody?.categoryBitMask = 0b00000010
    self.enemyDrone?.physicsBody?.contactTestBitMask = 0b00000010
    self.enemyDrone?.name = "drone"
    self.enemyDrone?.pivot = SCNMatrix4MakeTranslation(0.5, 0.5, 0.5)
    self.enemyDrone?.position = SCNVector3Make(0, 0, -30)
    self.enemyDrone?.scale = SCNVector3Make(1, 1, 1)
    let actualPosition = self.scene.rootNode.convertPosition((self.enemyDrone?.position)!, from: self.enemyDrone)
    self.enemyDrone?.position = self.scene.rootNode.convertPosition(actualPosition, to: self.sectorObjectsNode)
    self.sectorObjectsNode.addChildNode(self.enemyDrone!)
}

The sectorObjectsNode is rotated in reaction to onScreen joystick (thereby rotating the "universe" around the ship to simulate motion) using this code:

func turnShip() {
    self.rotate(self.sectorObjectsNode, around: SCNVector3Make(1, 0, 0), by: CGFloat(self.yThrust))
    self.rotate(self.sectorObjectsNode, around: SCNVector3Make(0, 1, 0), by: CGFloat(self.xThrust))

}

func rotate(_ node: SCNNode, around axis: SCNVector3, by angle: CGFloat) {
    let rotation = SCNMatrix4MakeRotation(Float(angle), axis.x, axis.y, axis.z)
    let newTransform = SCNMatrix4Mult(node.worldTransform, rotation)

    // Set the new transform
    if let parent = node.parent {
        node.transform = parent.convertTransform(newTransform, from: nil)
    } else {
        node.transform = newTransform
    }
}

But this code causes the physics simulation to reset ( The grey box in the center of the screen is the physics bounding box for the drone as depicted by the engine when sceneView.debugOptions is set to .showPhysicsShapes), with the following results:

Physics bounding Box not following object

I've tried capturing the drone's presentation position before rotation and then applying it after the two rotate functions, but this causes the ship to move down and to the left. I'm stymied as to how to get the physics simulation of the drone (which I'm using pretty exclusively for collision detection) to stick to the actual position of the enemyDrone node.

1

1 Answers

2
votes

As per usual, the issue was RTFM. I set the physics body to be the wrong type:

self.enemyDrone?.physicsBody = SCNPhysicsBody(type: .dynamic, shape: dronePhysicsShape)

Needed to be changed to

self.enemyDrone?.physicsBody = SCNPhysicsBody(type: .kinematic, shape: dronePhysicsShape)