I have been trying to set up a simple Scenekit scene with some physics so I could learn about how SCNPhysicsContactDelegate, categoryBitMask, collisionBitMask and the physicsWorld func work. Not sure if I need to set up a contactTestBitMask as well.
Learning about contact detection sent me down a long path of bitwise operators and the concept of bit masking. Adding in binary is fun! However, this is all still very foggy and I am trying to cobble together several tutorials I've found in both SpriteKit and SceneKit. This is the most comprehensive but it is in Obj-C and I don't understand it how to translate to Swift.
Here is what I have created. Any insights would be much appreciated. Can you see what I have set up incorrectly? I would like to have a simple Print statement occur when the red rolling ball hits the blue target. The floor, ramp and target are .static, while the rolling ball is .dynamic.
import UIKit
import SceneKit
class ViewController: UIViewController, SCNPhysicsContactDelegate {
//category bit masks for ball node and target node
// ball = 0001 -> 1 and target = 0010 ->2
let collisionRollingBall: Int = 1 << 0
let collsionTarget: Int = 1 << 1
//declare variables
var sceneView: SCNView!
var cameraNode: SCNNode!
var groundNode: SCNNode!
var lightNode: SCNNode!
var rampNode: SCNNode!
var rollingBallNode: SCNNode!
var targetNode: SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
//set up sceneview and scene. Define the physicsworld contact delegate as self
sceneView = SCNView(frame: self.view.frame)
sceneView.scene = SCNScene()
sceneView.scene!.physicsWorld.contactDelegate = self
self.view.addSubview(sceneView)
//add floor
let groundGeometry = SCNFloor()
groundGeometry.reflectivity = 0
let groundMaterial = SCNMaterial()
groundMaterial.diffuse.contents = UIColor.greenColor()
groundGeometry.materials = [groundMaterial]
groundNode = SCNNode(geometry: groundGeometry)
//add ramp
let rampGeometry = SCNBox(width: 4, height: 1, length: 18, chamferRadius: 0)
rampNode = SCNNode(geometry: rampGeometry)
rampNode.position = SCNVector3(x: 0, y: 2.0, z: 1.0)
rampNode.rotation = SCNVector4(1, 0, 0, 0.26)
//add rolling ball
let rollingBallGeometry = SCNSphere(radius: 0.5)
let sphereMaterial = SCNMaterial()
sphereMaterial.diffuse.contents = UIColor.redColor()
rollingBallGeometry.materials = [sphereMaterial]
rollingBallNode = SCNNode(geometry: rollingBallGeometry)
rollingBallNode.position = SCNVector3(0, 6, -6)
//add target box
let targetBoxGeometry = SCNBox(width: 4, height: 1, length: 4, chamferRadius: 0)
let targetMaterial = SCNMaterial()
targetMaterial.diffuse.contents = UIColor.blueColor()
targetBoxGeometry.materials = [targetMaterial]
targetNode = SCNNode(geometry: targetBoxGeometry)
targetNode.position = SCNVector3(x: 0, y: 0.5, z: 11.5)
targetNode.rotation = SCNVector4(-1,0,0,0.592)
//add a camera
let camera = SCNCamera()
self.cameraNode = SCNNode()
self.cameraNode.camera = camera
self.cameraNode.position = SCNVector3(x: 13, y: 5, z: 12)
let constraint = SCNLookAtConstraint(target: rampNode)
self.cameraNode.constraints = [constraint]
constraint.gimbalLockEnabled = true
//add a light
let spotLight = SCNLight()
spotLight.type = SCNLightTypeSpot
spotLight.castsShadow = true
spotLight.spotInnerAngle = 70.0
spotLight.spotOuterAngle = 90.0
spotLight.zFar = 500
lightNode = SCNNode()
lightNode.light = spotLight
lightNode.position = SCNVector3(x: 0, y: 25, z: 25)
lightNode.constraints = [constraint]
//define physcis bodies
let groundShape = SCNPhysicsShape(geometry: groundGeometry, options: nil)
let groundBody = SCNPhysicsBody(type: .Static, shape: groundShape)
groundNode.physicsBody = groundBody
let rampShape = SCNPhysicsShape(geometry: rampGeometry, options: nil)
let rampBody = SCNPhysicsBody(type: .Static, shape: rampShape)
rampNode.physicsBody = rampBody
let sphereShape = SCNPhysicsShape(geometry: rollingBallGeometry, options: nil)
let sphereBody = SCNPhysicsBody(type: .Dynamic, shape: sphereShape)
rollingBallNode.physicsBody?.categoryBitMask = collisionRollingBall
rollingBallNode.physicsBody?.collisionBitMask = collsionTarget
rollingBallNode.physicsBody = sphereBody
let targetShape = SCNPhysicsShape(geometry: targetBoxGeometry, options: nil)
let targetBody = SCNPhysicsBody(type: .Static, shape: targetShape)
targetNode.physicsBody?.categoryBitMask = collsionTarget
targetNode.physicsBody?.collisionBitMask = collisionRollingBall
targetNode.physicsBody = targetBody
//add nodes to view
sceneView.scene?.rootNode.addChildNode(groundNode)
sceneView.scene?.rootNode.addChildNode(rampNode)
sceneView.scene?.rootNode.addChildNode(rollingBallNode)
sceneView.scene?.rootNode.addChildNode(targetNode)
sceneView.scene?.rootNode.addChildNode(self.cameraNode)
sceneView.scene?.rootNode.addChildNode(lightNode)
}
func physicsWorld(world: SCNPhysicsWorld, didBeginContact contact: SCNPhysicsContact) {
print("contact")
// let contactMask = contact.nodeA.categoryBitMask |
//contact.nodeB.categoryBitMask
//if contactMask == collsionTarget | collisionRollingBall {
// print("The ball hit the target")
// }
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
contact.nodeA.categoryBitMask
tocontact.nodeA.physicsBody!.categoryBitMask
and everything worked. Not sure why that made a difference but it did. Do you know why? – brickm