4
votes

I'm looking for the best way (performace-wise) to detect contact between two objects that do not collide (not bounce off each other) in a SceneKit physics world.

I saw that SpriteKit has a contactTestBitMask and a collisionBitMask for physics bodies while SceneKit only has the latter. So there must to be another preferred way to get notified when objects have contact in SceneKit. I guess that calling contactTestBetweenBody:andBody:options: in each frame for each object is not the best way to do it?

UPDATE With iOS 9.0, Apple has added contactTestBitMask to SCNPhysicsBody. So this question will become obsolete soon.

3
Did you find a solution to this? Having a physics body act as a sensor seems like a fairly basic bit of functionality.Graham Nicol
I went with the accepted answer below (physicsField)Dorian Roy

3 Answers

12
votes

This question deserves a full tutorial. In short, each physics body has a categoryBitMask and collisionBitMask.

categoryBitMask This is the bit that represents the body.

collisionBitMask This is the bit that represents which bodies collide with it.

By default (when assigned a physicsBody) all bodies collide with the exception of a kinematicBody, which act special. Objects don't collide with it. But don't get confused, it can collide with other objects when manually moved (such as by dragging it with a finger).

How to handle!!!

Assign your bodies whether it be static or dynamic. Assign there category and collision mask. If the mask match up they collide. How about an example!

These bodies collide with each other

bodyA.physicsBody.categoryBitMask = 4;
bodyB.physicsBody.categoryBitMask = 8;

bodyA.physicsBody.collisionBitMask = 8;
bodyB.physicsBody.collisionBitMask = 4;

These don't collide

bodyA.physicsBody.categoryBitMask = 4;
bodyB.physicsBody.categoryBitMask = 8;

bodyA.physicsBody.collisionBitMask = 0;
bodyB.physicsBody.collisionBitMask = 0;

Noticed I used categories by power of two. This is because the contactDelegate uses bitwise AND to compare. Read up on bitwise if you don't understand. It will really help.

These bodies collide without physics

EXAMPLE: You have a hero that runs through a ghost and you want to know that it happened without either one being effected. Such as bouncing off each other.

bodyA.physicsBody.categoryBitMask = 4;
bodyB.physicsBody.categoryBitMask = 8;

bodyA.physicsBody.collisionBitMask = 0;
bodyB.physicsBody.collisionBitMask = 0;

So the following code is designed so that bodyA(your hero) runs through bodyB(ghost).

bodyB.physicsField.categoryBitMask = 4

Noticed that the above is a physicsField.catagoryBitMask. It is assigned a 4 so that it matches up with bodyA.

let collisionField = SCNPhysicsField.customFieldWithEvaluationBlock(){ 
    (_, _, _, _, _) in 
    WHAT U WANT TO DO ON CONTACT
}

bodyB.physicsField = collisionField

The physicsField is not the shape of your geometry unfortunately. Its default is the shape of its bounding box or sphere/elliptical if you set the physicsField property usesEllipsoidalExtent to "true/yes"

The area of effect can be changed using the physicsFields halfExtent property like so...

bodyB.physicsField.halfExtent = SCNVector3Make(.5, .5, .5)

The above will extend the field of a box the size of (2, 2, 2) to (2.5, 2.5, 2.5).

EDIT: If they still collide, try removing the physicsbody from bodyB (this will remove all physics simulation from the node). I don't think it will be a problem as long as the catagory and collision bitmask are assigned properly.

This is the best method I could find. All coding is from my head, with the support of Apple docs, so may not be exactly right but I hope this helps!

2
votes

I have found the best way is to just set the mass so small on a kinematic body that the other one will pass through it.

sensorBox.physicsBody.mass = 0.00000000001; // super tiny mass makes it a "sensor"

0
votes

Don't forget your scene's physics world can call a "contactTestBetweenBody: andBody: withOptions... this works great.

Usage

[myScene.physicsWorld contactTestBetweenBody:(SCNPhysicsBody *) andBody:(SCNPhysicsBody *) options:(NSDictionary *)];