Eilon
Eilon

Reputation: 2982

SceneKit physics don't work properly in ARKit

I'm new to ARKit and I'm trying to make a relatively simple app where you create a "wall" and the user "throws" balls at it, and it bounces back.

I create the wall as a SCNPlane where the user is pointing the camera to like so:

private func createWall(withPosition position: SCNVector3) -> SCNNode {
    let wallGeometry = SCNPlane(width: 0.3, height: 0.3)
    wallGeometry.firstMaterial?.isDoubleSided = true
    wallGeometry.firstMaterial?.diffuse.contents = UIColor.green.withAlphaComponent(0.7)

    let parentNode = SCNNode(geometry: wallGeometry)

    let (position, _) = cameraPosition()
    parentNode.position = position

    parentNode.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: wallGeometry, options: nil))
    parentNode.physicsBody?.isAffectedByGravity = false

    self.wall = parentNode

    return parentNode
}

I get the direction and position of camera with this function:
cameraPosition():

func cameraPosition() -> (SCNVector3, SCNVector3) {
    guard let frame = sceneView.session.currentFrame else { return (SCNVector3(0, 0, -1), (SCNVector3(0, 0, 0))) }
    let matrix = SCNMatrix4(frame.camera.transform)
    let direction = SCNVector3(-matrix.m31, -matrix.m32, -matrix.m33)
    let location = SCNVector3(matrix.m41, matrix.m42, matrix.m43)
    return ((location + direction), direction)
}

// Helper function
func +(left: SCNVector3, right: SCNVector3) -> SCNVector3 {
    return SCNVector3(left.x + right.x, left.y + right.y, left.z + right.z)
}

I create instances of Ball() and throw them like this:

let (position, direction) = cameraPosition()

// throw ball
let ball = Ball()
ball.position = position //SCNVector3(0,0,0)
sceneView.scene.rootNode.addChildNode(ball)

let impulseModifier = Float(10)
ball.physicsBody!.applyForce(SCNVector3(direction.x*impulseModifier, direction.y*impulseModifier, direction.z*impulseModifier), asImpulse: true)

The Ball class:

class Ball: SCNNode {
    override init() {
        super.init()

        let sphere = SCNSphere(radius: 0.05)
        sphere.firstMaterial?.diffuse.contents = UIColor.red
        self.geometry = sphere

        self.physicsBody = SCNPhysicsBody(type: .dynamic, shape: SCNPhysicsShape(geometry: sphere, options: nil))        
    }
}

The problem is that many times, instead of the ball actually hitting the wall, it will just travel through it, as if the physics body does not function properly. I noticed that sometimes it will work better when I change the distance and angle between the camera and the wall, but the results are never consistent as far as I've tried.
I have also tried to change the wall position to: SCNVector3(0, 0, -1) to position it exactly 1 meter deep away from the world origin, and the results were somewhat better but still not consistent.

Where could the problem lay and why?
Thanks in advance!

Upvotes: 0

Views: 724

Answers (2)

Claus
Claus

Reputation: 5742

Most likely this is due to the ball being too small for the collision detection to work properly. Try setting continuousCollisionDetectionThreshold equal to half of the ball radius. In this case:

ball.physicsBody?.continuousCollisionDetectionThreshold = 0.025

Upvotes: 0

kim
kim

Reputation: 31

Set your wall physics to be kinematic so that the ball can react with it.

  • Static objects don't react with anything.

  • Kinematic objects react with other objects but other objects don't affect them.

  • Dynamic of course means it works in both directions.

Upvotes: 3

Related Questions