2
votes

I have a simple 3d area that contains 4 walls, each is a SCNNode with a simple SCNBox geometry, of rectangular shape, and matching SCNPhysicsBody attached. The SCNPhysicsBody uses a SCNPhysicsShape.ShapeType.boundingBox, and is set to static type. Here is a code snippet:

        let size = (self.levelNode.boundingBox.max - self.levelNode.boundingBox.min) * self.levelNode.scale

        //x //z
        let geometryA = SCNBox(width: CGFloat(size.x), height: CGFloat(1 * self.levelNode.scale.x), length: 0.01, chamferRadius: 0)
        let geometryB = SCNBox(width: CGFloat(size.z), height: CGFloat(1 * self.levelNode.scale.x), length: 0.01, chamferRadius: 0)

        geometryA.firstMaterial?.diffuse.contents = UIColor(red: 0.0, green: 0.2, blue: 1.0, alpha: 0.65)
        geometryB.firstMaterial?.diffuse.contents = UIColor(red: 0.0, green: 0.2, blue: 1.0, alpha: 0.65)


        let nodeA = SCNNode(geometry: geometryA)
        nodeA.position += self.levelNode.position
        nodeA.position += SCNVector3(0, 0.25 * self.levelNode.scale.y, -size.z/2)
        nodeA.name = "Boundary-01"

        let nodeB = SCNNode(geometry: geometryA)
        nodeB.position += self.levelNode.position
        nodeB.position += SCNVector3(0, 0.25 * self.levelNode.scale.y, size.z/2)
        nodeB.name = "Boundary-03"

        let nodeC = SCNNode(geometry: geometryB)
        nodeC.position += self.levelNode.position
        nodeC.position += SCNVector3(-size.x/2, 0.25 * self.levelNode.scale.y, 0)
        nodeC.eulerAngles = SCNVector3(0, -Float.pi/2, 0)
        nodeC.name = "Boundary-02"

        let nodeD = SCNNode(geometry: geometryB)
        nodeD.position += self.levelNode.position
        nodeD.position += SCNVector3(size.x/2, 0.25 * self.levelNode.scale.y, 0)
        nodeD.eulerAngles = SCNVector3(0, Float.pi/2, 0)
        nodeD.name = "Boundary-04"

        let nodes = [nodeA, nodeB, nodeC, nodeD]

        for node in nodes {
            //
            let shape = SCNPhysicsShape(geometry: node.geometry!, options: [
                SCNPhysicsShape.Option.type : SCNPhysicsShape.ShapeType.boundingBox])
            let body = SCNPhysicsBody(type: .static, shape: shape)
            node.physicsBody = body
            node.physicsBody?.isAffectedByGravity = false
            node.physicsBody?.categoryBitMask = Bitmask.boundary.rawValue
            node.physicsBody?.contactTestBitMask = Bitmask.edge.rawValue
            node.physicsBody?.collisionBitMask = 0

            scene.rootNode.addChildNode(node)
            node.physicsBody?.resetTransform()
        }

Inside this area, I spawn entities at a regular time interval. Each also has a SCNBox geometry, that is a cube shape this time, smaller than the walls, and same parameters for the physics body as above.

To simplify the behaviour of my entities inside this game area, I am calculating their paths to travel, then applying a SCNAction to the relevant node to move them. The SCNAction moves both the node and physics body attached to it.

I am using the SCNPhysicsWorld contact delegate to detect when an entity reaches one of the boundary walls. I then calculate a random trajectory for it from that wall in another direction, clear its actions, and apply a new move SCNAction.

This is where it gets interesting...

When this 'world' is at 1:1 scale. The contacts are detected as normal both in a standard SCNScene, and a scene projected using ARKit. The visible contact, i.e. the visible change in direction of the entity appears to be close to the boundary as expected. When I check the contact.penetrationDistance of each contact their values are e.g. 0.00294602662324905.

BUT when I change the scale of this 'world' to something smaller, say the equivalent of 10cm width, in ARKit, the simulation falls apart.

The contacts between an entity and a boundary node have a comparatively huge visible gap between them when the contact is detected. Yet the contact.penetrationDistance is of the same magnitude as before.

I switched on the ARSCNView debug options to show the physics shapes in the render, and they all appear to be the correct proportions, matching the bounding box of their node.

As you can see from the code example above, the boundary nodes are generated after I have scaled the level, during my AR user setup. They are added to the root node of the scene, not as a child of the level node. The same code is being used to generate the entities.

Previously I had tried using the resetTransform() function on the physics bodies but this did not produce a reliable scaling of the physics bodies, after I had scaled the level, so I decided to generate the nodes for the boundaries and entities after the level has been scaled.

In Apple's documentation, it does state that if the SCNPhysicsBody is not a custom shape, that it will adopt the scale of the node geometry applied to it. I am not affected by this as I am generating the geometries and their respective nodes, after the scaling has been applied to the level.

One of assumptions at the moment is that the physics simulation falls apart at such a small scale. But I am not relying on the simulation of forces to move the bodies ...

Is there a more appropriate way to scale the physics world?

Or, am I staring a bug in the SCNPhysicsWorld, that is something beyond my control, at the moment.

One solution I did think about was to run the entire simulation at 1:1 scale but hidden, then apply those movements to the smaller entities. As you can imagine, that will affect the performance of the entire scene...

1
Do you mean that you are changing the scale of the node after it has been created and has had its SCNPhysicsBody attached?drewster
@drewster I am creating the nodes at the desired scale that I want, then applying the physics bodies to them, then adding them to the scene. In the code above for my boundaries, I am calculating the size of the level using its bounding box min and max, and applying its scale, as it is in the scene.nightbird

1 Answers

0
votes

The penetration distance of the first contact is a negative value, suggesting there is a gap. This gap does not appear to scale as you scale down the size of the simulation.

As a way to resolve the above excess, I have implemented an additional check on the contacts in the Contact Delegate to not take the first contact detected for a particular category, but rather ensure the penetrationDistance value is positive, so ensuring that there is overlap between the two objects, before triggering a change in direction of the entity which connected with a boundary.