I created a ball and hope it collides with another sphere.
But it's not worked.
enum ColliderType: Int {
case ball = 1
case food = 2
}
create a ball and a food object
let ballNode: SCNNode!
override func viewDidLoad() {
.......
let ball = SCNSphere(radius: 1.0)
ball.materials.first?.diffuse.contents = UIColor.red
ballNode = SCNNode(geometry: ball)
ballNode.name = "ball"
ballNode.position = SCNVecter3(10, 1, 10)
ballNode.physicsBody = SCNPhysicsBody(type: .kinematic, shape:
SCNPhysicsShape(geometry: SCNSphere(radius: 1.0), options: nil))
ballNode.physicsBody?.categoryBitMask = ColliderType.ball.rawValue
ballNode.physicsBody?.contactTestBitMask = ColliderType.food.rawValue
ballNode.physicsBody?.collisionBitMask = ColliderType.food.rawValue
scnScene.rootNode.addChildNode(ballNode)
let food = SCNSphere(radius: 0.5)
food.materials.first?.diffuse.contents = UIColor.green
let foodNode = SCNNode(geometry: food)
foodNode.position = SCNVector3(0, 0.5, 0)
foodNode.name = "food"
foodNode.physicsBody = SCNPhysicsBody(type: .static, shape:
SCNPhysicsShape(geometry: SCNSphere(radius: 0.5), options: nil))
foodNode.physicsBody?.categoryBitMask = ColliderType.food.rawValue
foodNode.physicsBody?.collisionBitMask = ColliderType.ball.rawValue
foodNode.physicsBody?.contactTestBitMask = ColliderType.ball.rawValue
scnScene.rootNode.addChildNode(foodNode)
}
Here's the collision code:
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
var contactNode: SCNNode!
if contact.nodeA.name == "ball" {
contactNode = contact.nodeB
} else {
contactNode = contact.nodeA
}
if contactNode.physicsBody?.categoryBitMask == ColliderType.food.rawValue {
print("aaaa")
}
And if I create the food object with scene editor, it's worked.
I don't know what's wrong with my code.
You are using contactTestBitMask for one node and collisionBitMask for the other. SCNPhysicsContact is governed by contactTestBitMask.
Also, I do not see whether physicsBody is assigned to the ball node.
Related
Why this 2 object pass through each other and not interact, what I'm doing wrong?
on each object I applied the property physicsBody which should allow to the physic engine to work.
I add the square object using a tap gesture, creating the anchor with name "base" and in the render add the object base.
for the ball I use a raycast query to position the ball over the plane.
all working fine, only the dynamic looks wrong, they pass each other.
// my square base
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
if let nome = anchor.name, nome == "base" {
let geometry = SCNPlane(width: 1, height: 1)
let material = SCNMaterial()
material.diffuse.contents = UIColor(red: 90/255, green: 200/255, blue: 250/255, alpha: 0.50)
geometry.materials = [material]
// physics
let physSchape = SCNPhysicsShape(geometry: geometry, options: nil)
let planePhysic = SCNPhysicsBody(type: .static, shape: physSchape)
planePhysic.restitution = 0.0
planePhysic.friction = 1.0
let nodo = SCNNode(geometry: geometry)
nodo.eulerAngles.x = -.pi / 2
nodo.physicsBody = planePhysic
node.addChildNode(nodo)
}
}
and this ball:
func addBall(recognizer: UITapGestureRecognizer){
let tapLocation = recognizer.location(in: view)
guard let query = view.raycastQuery(from: tapLocation, allowing: .existingPlaneGeometry, alignment: .horizontal) else {return}
// ottengo posizione real world
guard let translation = view.castRay(for: query).first?.worldTransform.translation else {return}
let x = translation.x
let y = translation.y
let z = translation.z
let ballGeometry = SCNSphere(radius: 0.1)
let mat = SCNMaterial()
mat.diffuse.contents = UIImage(named: "ball")
ballGeometry.materials = [mat]
//Physics
let physSchape = SCNPhysicsShape(geometry: ballGeometry, options: nil)
let ballPhysic = SCNPhysicsBody(type: .dynamic, shape: physSchape)
ballPhysic.mass = 0.2
ballPhysic.friction = 0.8
let nodeBall = SCNNode(geometry: ballGeometry)
nodeBall.position = SCNVector3(x,y+0.3,z)
nodeBall.physicsBody = ballPhysic
view.scene.rootNode.addChildNode(nodeBall)
}
they should interact each other , but actually my ball pass through the base.
If you're trying to simulate two objects' collision, first of all, you must implement the protocol called SCNPhysicsContactDelegate and its delegate:
weak var contactDelegate: SCNPhysicsContactDelegate? { get set }
for using its three optional instance methods:
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact)
func physicsWorld(_ world: SCNPhysicsWorld, didUpdate contact: SCNPhysicsContact)
func physicsWorld(_ world: SCNPhysicsWorld, didEnd contact: SCNPhysicsContact)
And, of course, category bit mask and collision bit mask are required.
If you need a more detailed info, please look at THIS POST.
I created two nodes: a sphere and a box:
var sphere = SCNNode(geometry: SCNSphere(radius: 0.005))
//I get the box node from scn file
let boxScene = SCNScene(named: "art.scnassets/world.scn")!
var boxNode: SCNNode?
I want two nodes or physicsBody's to interact, so I created a category for categoryBitMask and contactTestBitMask:
struct CollisionCategory: OptionSet {
let rawValue: Int
static let box = CollisionCategory(rawValue: 1)
static let sphere = CollisionCategory(rawValue: 2)
}
Here I set the box node as a physics body:
self.boxScene.rootNode.enumerateChildNodes { (node, _) in
if node.name == "box" {
boxNode = node
let boxBodyShape = SCNPhysicsShape(geometry: SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0.1), options: nil)
let physicsBody = SCNPhysicsBody(type: .static, shape: boxBodyShape)
boxNode!.physicsBody = physicsBody
boxNode!.physicsBody?.categoryBitMask = CollisionCategory.box.rawValue
boxNode!.physicsBody?.contactTestBitMask = CollisionCategory.sphere.rawValue
boxNode!.physicsBody?.collisionBitMask = boxNode!.physicsBody!.contactTestBitMask
}
}
Here I set the sphere node in the render function, which you can move around the view:
func setUpSphere() {
let sphereBodySphere = SCNPhysicsShape(geometry: SCNSphere(radius: 0.005))
let physicsBody = SCNPhysicsBody(type: .kinematic, shape: sphereBodySphere)
sphere.physicsBody = physicsBody
sphere.physicsBody?.categoryBitMask = CollisionCategory.sphere.rawValue
sphere.physicsBody?.contactTestBitMask = CollisionCategory.box.rawValue
sphere.geometry?.firstMaterial?.diffuse.contents = UIColor.blue
sphere.physicsBody?.collisionBitMask = sphere.physicsBody!.contactTestBitMask
previousPoint = currentPosition
}
///It Adds a sphere and changes his position
func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
guard let pointOfView = sceneView.pointOfView else { return }
let mat = pointOfView.transform
let dir = SCNVector3(-1 * mat.m31, -1 * mat.m32, -1 * mat.m33)
let currentPosition = pointOfView.position + (dir * 0.185)
if buttonPressed {
if let previousPoint = previousPoint {
sphere.position = currentPosition
sceneView.scene.rootNode.addChildNode(sphere)
}
}
}
I added the protocol SCNPhysicsContactDelegate to the ViewController,
and I set in ViewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
sceneView.scene.physicsWorld.contactDelegate = self
///I correctly see the shapes of the sphere and the box physics bodies using
sceneView.debugOptions = .showPhysicsShapes
createBox()
setUpSphere()
sceneView.scene = boxScene
sceneView.scene.physicsWorld.contactDelegate = self
}
Then I added that function:
func physicsWorld(_ world: SCNPhysicsWorld, didEnd contact: SCNPhysicsContact) {
print("Collision!")
}
This is what happens.
When the two nodes collide nothing happens, so I can't know if the two bodies are touching. Could the problem be about .kinematic, .static or about the function render()?
I followed step by step different tutorials about collisions in ARKit: Tutorial 1, Tutorial 2.
I have no idea why it doesn't work like expected.
Is there something wrong in my code?
Download file code link: https://ufile.io/20sla
willRenderScene is called uptown 60 times a second for every time the scene is going to be rendered. Since you're recreating the physics body every time it's probably messing up the physics engine determine collisions.
Try changing your code to only create the physics body once during setup.
I am trying to detect the collision between the circle called player and the balls called enemyCircle but nothing happen
func didBeginContact(contact: SKPhysicsContact) {
if let nodeA = contact.bodyA.node as? SKShapeNode, let nodeB = contact.bodyB.node as? SKShapeNode {
if nodeA.fillColor != nodeB.fillColor {
print("not same color")
}else {
print("same color")
}
}
}
private func drawPlayer(radius: CGFloat) {
player = SKShapeNode(circleOfRadius: radius)
player.physicsBody = SKPhysicsBody(circleOfRadius: radius)
player.physicsBody?.affectedByGravity = false
player.physicsBody?.isDynamic = false
player.physicsBody?.pinned = true
player.physicsBody?.categoryBitMask = bodyType.player.rawValue
player.physicsBody?.collisionBitMask = bodyType.enemy.rawValue
player.physicsBody?.contactTestBitMask = bodyType.enemy.rawValue
player.strokeColor = SKColor.purple
player.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
player.glowWidth = 2.0
world.addChild(player)
}
private func drawEnemy(radius: CGFloat, color: SKColor) {
let enemyCircle = SKShapeNode(circleOfRadius: radius)
enemyCircle.fillColor = color
enemyCircle.glowWidth = 0.5
enemyCircle.strokeColor = color
enemyCircle.physicsBody = SKPhysicsBody(circleOfRadius: radius)
enemyCircle.physicsBody?.affectedByGravity = false
enemyCircle.physicsBody?.isDynamic = false
enemyCircle.physicsBody?.collisionBitMask = bodyType.player.rawValue
enemyCircle.physicsBody?.categoryBitMask = bodyType.enemy.rawValue
let nextEnemyPosition = determineNextEnemyPosition()
let enemyPosition = spawnAtRandomPosition(edge: nextEnemyPosition)
enemyCircle.position = enemyPosition
world.addChild(enemyCircle)
numberOfEnemies += 1
enemyCircle.name = String(numberOfEnemies)
enemy.append(enemyCircle)
runToCenter(enemy: enemyCircle)
}
enum bodyType: UInt32 {
case enemy = 1
case player = 2
}
and this
class GameScene: SKScene, SKPhysicsContactDelegate
and i put this in didMove func
self.physicsWorld.contactDelegate = self
can someone explain me the issues here
Note:: I made both not dynamic as I don't want anyone to move the other or change it, just be notified when it touches so I stop the game
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.
}
}
I think you're having to reset the "catagoryBitMask" value in the delegate function because you're trying to set the "catagoryBitMask" and "collisionBitMask" values when the physicsBody is still nil.
rollingBallNode.physicsBody?.categoryBitMask = collisionRollingBall
rollingBallNode.physicsBody?.collisionBitMask = collsionTarget
rollingBallNode.physicsBody = sphereBody
Try putting that 3rd line 1st.
I already build an application with collision detection using delegate SCNPhysicsContactDelegate. Right now I'm using Swift 2.0 and Xcode 7 and I try to detect collision with SCNPhysicsContactDelegate delegate but it doesn't work.
What I'm doing wrong? Below the code:
let CubeType = 1
let PlayerType = 2
class GameViewController: UIViewController, SCNSceneRendererDelegate,SCNPhysicsContactDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let scnView = self.view as! SCNView
scnView.scene = SCNScene()
scnView.scene?.physicsWorld.contactDelegate = self
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 5)
scnView.scene?.rootNode.addChildNode(cameraNode)
scnView.autoenablesDefaultLighting = true
scnView.allowsCameraControl = true
scnView.showsStatistics = true
scnView.backgroundColor = UIColor.blackColor()
let g = SCNBox(width: 0.5, height: 0.5, length: 0.5, chamferRadius: 0)
let n = SCNNode(geometry: g)
n.physicsBody = SCNPhysicsBody(type: .Kinematic, shape: SCNPhysicsShape(geometry: g, options: nil))
n.physicsBody?.categoryBitMask = CubeType
n.physicsBody?.collisionBitMask = PlayerType
n.position = SCNVector3Make(5, 0, 0)
scnView.scene?.rootNode.addChildNode(n)
let playerGeometry = SCNSphere(radius: 1)
let playerNode = SCNNode(geometry: playerGeometry)
playerNode.position = SCNVector3Zero
playerNode.physicsBody = SCNPhysicsBody(type: .Kinematic, shape: SCNPhysicsShape(geometry: playerGeometry, options:nil))
playerNode.physicsBody?.categoryBitMask = PlayerType
playerNode.physicsBody?.collisionBitMask = CubeType
scnView.scene?.rootNode.addChildNode(playerNode)
let moveAction = SCNAction.moveTo(n.position, duration: 3)
playerNode.runAction(moveAction)
}
func physicsWorld(world: SCNPhysicsWorld, didBeginContact contact: SCNPhysicsContact) {
print("-> didBeginContact")
}
func physicsWorld(world: SCNPhysicsWorld, didEndContact contact: SCNPhysicsContact) {
print("-> didEndContact")
}
func physicsWorld(world: SCNPhysicsWorld, didUpdateContact contact: SCNPhysicsContact) {
print("-> didupdatecontact")
}
The .contactTestBitMask property is missing for both nodes:
n.physicsBody?.contactTestBitMask = PlayerType
playerNode.physicsBody?.contactTestBitMask = CubeType