I'm trying to get collision detection between different materials within an scn node. I have a cube with 6 different materials/ colors and balls coming at the cube. I'd like to detect same and different colored collisions but don't know how to apply different categories to each material.
import UIKit
import SceneKit
class GameViewController: UIViewController, SCNPhysicsContactDelegate {
Can I create categories to assign to different sides of the cube later?
let ballCategory = 0
let cubeCategory = 1
let ballGreen = 2
let ballRed = 3
let ballBlue = 4
let ballYellow = 5
let ballPurple = 6
let ballOrange = 7
let cubeGreen = 8
let cubeRed = 9
let cubeBlue = 10
let cubeYellow = 11
let cubePurple = 12
let cubeOrange = 13
setup stuff
var scnView: SCNView!
var scnScene = SCNScene()
var cameraNode: SCNNode!
var cubeNode = SCNNode()
var ball = SCNNode()
var randomColor: UIColor?
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
scnScene.physicsWorld.contactDelegate = self
scnView = self.view as? SCNView
view.scene = scnScene
view.isPlaying = true
}
setupView()
setupScene()
setupCamera()
spawnCube()
}
Will physics world did begin contact work?
func physicsWorld(world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
print("Physics called")
if (contact.nodeA == cubeNode || contact.nodeA == ball) && (contact.nodeB == cubeNode || contact.nodeB == ball) {
print("contact")
}
if (contact.nodeA.physicsBody!.categoryBitMask == 2 && contact.nodeB.physicsBody!.categoryBitMask == 8) || (contact.nodeB.physicsBody!.categoryBitMask == 2 && contact.nodeA.physicsBody!.categoryBitMask == 8) {
print("green contact")
}
if (contact.nodeA.physicsBody!.categoryBitMask == 3 && contact.nodeB.physicsBody!.categoryBitMask == 9) || (contact.nodeB.physicsBody!.categoryBitMask == 3 && contact.nodeA.physicsBody!.categoryBitMask == 9) {
print("green contact")
}
if (contact.nodeA.physicsBody!.categoryBitMask == 4 && contact.nodeB.physicsBody!.categoryBitMask == 10) || (contact.nodeB.physicsBody!.categoryBitMask == 4 && contact.nodeA.physicsBody!.categoryBitMask == 10) {
print("green contact")
}
if (contact.nodeA.physicsBody!.categoryBitMask == 5 && contact.nodeB.physicsBody!.categoryBitMask == 11) || (contact.nodeB.physicsBody!.categoryBitMask == 5 && contact.nodeA.physicsBody!.categoryBitMask == 11) {
print("green contact")
}
if (contact.nodeA.physicsBody!.categoryBitMask == 6 && contact.nodeB.physicsBody!.categoryBitMask == 12) || (contact.nodeB.physicsBody!.categoryBitMask == 6 && contact.nodeA.physicsBody!.categoryBitMask == 12) {
print("green contact")
}
if (contact.nodeA.physicsBody!.categoryBitMask == 7 && contact.nodeB.physicsBody!.categoryBitMask == 13) || (contact.nodeB.physicsBody!.categoryBitMask == 7 && contact.nodeA.physicsBody!.categoryBitMask == 13) {
print("green contact")
}
}
func setupView() {
scnView = self.view as! SCNView
scnView.showsStatistics = false
scnView.allowsCameraControl = false
scnView.autoenablesDefaultLighting = true
}
func setupScene() {
scnScene = SCNScene()
scnView.scene = scnScene
scnScene.background.contents = UIColor.black
}
func setupCamera() {
cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 10)
scnScene.rootNode.addChildNode(cameraNode)
}
Cube setup, I'm getting an error when I try to add a category to the different sides/ materials
func spawnCube() {
var geometry:SCNGeometry
geometry = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.05)
cubeNode = SCNNode(geometry: geometry)
let greenMaterial = SCNMaterial()
greenMaterial.diffuse.contents = UIColor.green
greenMaterial.locksAmbientWithDiffuse = true
let redMaterial = SCNMaterial()
redMaterial.diffuse.contents = UIColor.red
redMaterial.locksAmbientWithDiffuse = true
let blueMaterial = SCNMaterial()
blueMaterial.diffuse.contents = UIColor.blue
blueMaterial.locksAmbientWithDiffuse = true
let yellowMaterial = SCNMaterial()
yellowMaterial.diffuse.contents = UIColor.yellow
yellowMaterial.locksAmbientWithDiffuse = true
let purpleMaterial = SCNMaterial()
purpleMaterial.diffuse.contents = UIColor.purple
purpleMaterial.locksAmbientWithDiffuse = true
let orangeMaterial = SCNMaterial()
orangeMaterial.diffuse.contents = UIColor.orange
orangeMaterial.locksAmbientWithDiffuse = true
geometry.materials = [greenMaterial, redMaterial, blueMaterial,
yellowMaterial, purpleMaterial, orangeMaterial]
cubeNode.physicsBody?.mass = 10000
cubeNode.physicsBody?.restitution = 0
cubeNode.physicsBody?.damping = 0
cubeNode.physicsBody = SCNPhysicsBody.kinematic()
cubeNode.physicsBody?.categoryBitMask = Int(cubeCategory)
cubeNode.physicsBody?.contactTestBitMask = Int(ballCategory)
cubeNode.physicsBody?.isAffectedByGravity = false
cubeNode.position = SCNVector3(x: 0, y: 0, z: 0)
scnScene.rootNode.addChildNode(cubeNode)
}
Ball setup, I'm also getting an error when I try to add a different category to different ball colors
func spawnBall() {
var geometry:SCNGeometry
geometry = SCNSphere(radius: 0.25)
let ball = SCNNode(geometry: geometry)
let randomNumberForColor = Int(arc4random_uniform(6))
if randomNumberForColor == 1 {
randomColor = UIColor.green
}
if randomNumberForColor == 2 {
randomColor = UIColor.red
}
if randomNumberForColor == 3 {
randomColor = UIColor.blue
}
if randomNumberForColor == 4 {
randomColor = UIColor.yellow
}
if randomNumberForColor == 5 {
randomColor = UIColor.purple
}
if randomNumberForColor == 6 {
randomColor = UIColor.orange
}
if randomNumberForColor == nil {
randomColor = UIColor.green
}
let greenMaterial = SCNMaterial()
greenMaterial.diffuse.contents = randomColor
greenMaterial.locksAmbientWithDiffuse = true;
geometry.materials = [greenMaterial]
ball.physicsBody?.mass = 0.00001
ball.physicsBody?.restitution = 1
ball.physicsBody?.damping = 0
ball.physicsBody = SCNPhysicsBody.dynamic()
ball.physicsBody?.categoryBitMask = Int(ballCategory)
ball.physicsBody?.contactTestBitMask = Int(cubeCategory)
ball.physicsBody?.isAffectedByGravity = false
randomX()
randomY()
randomZ()
ball.position = SCNVector3(x: Float(randomNumX!), y: Float(randomNumY!), z: Float(randomNumZ!))
scnScene.rootNode.addChildNode(ball)
let force = SCNVector3(x: Float(-randomNumX!)/2, y: Float(-randomNumY!)/2, z: Float(-randomNumZ!)/2)
ball.physicsBody?.applyForce(force, at: cubeNode.position, asImpulse: true)
}
func randomX() {
randomNumX = Int(arc4random_uniform(10)) - 5
}
func randomY() {
randomNumY = Int(arc4random_uniform(20)) - 10
}
func randomZ() {
randomNumZ = Int(arc4random_uniform(10)) - 5
}
}
Maybe there's a way to simplify this and just detect ball and cube collisions with a qualifier of matching color?
Thanks!
The SCNBox class automatically creates SCNGeometryElementobjects as needed to handle the number of materials. In your case, you are adding 6 different materials to geometry.materials, so you automatically have 6 different GeometryElements, one for each side of the cube.
From the SCNPhysicsContact, you can get the contact point between the two physics bodies.
With that point, you can then do a Hit Test, which will give you an Array of Hit Test Result's. In this case the array will have 2 elements, meaning 2 Results, one for the cube and one for the ball.
Go through those 2 elements and determine which one is the cube (do that by getting the 'node' property of the result and see if the node's name is 'cube' or 'ball', of course you will need to set the name property of the nodes when you create them).
Now that you know which one is the result for the Cube, get the Geometry Index property of the Result. Use that index to get the color of the face which was collided by the ball.
The geometry elements will have the same order as the materials in your array of materials, so for example if you got Geometry Element index 0, that is the first material in your materials array, and so on.