I am migrating my SceneKit games to RealityKit, and I noticed some changes in physics simulation. When 2 bodies overlap, RealityKit resolves the collision by moving the bodies apart abruptly and quickly. But SceneKit’s behavior is far more gentle.
Below I have a minimum reproducible code. You can create an empty Xcode project (with Storyboard), and replace the ViewController.swift with this code below. Or you can download the sample project here:
import UIKit
import RealityKit
import SceneKit
class MySCNScene: SCNScene {
override init() {
super.init()
let floor = SCNFloor()
floor.firstMaterial?.diffuse.contents = UIColor.darkGray
let floorNode = SCNNode(geometry: floor)
floorNode.physicsBody = SCNPhysicsBody(type: .static, shape: nil)
rootNode.addChildNode(floorNode)
let camera = SCNNode()
camera.camera = SCNCamera()
camera.position = SCNVector3(0, 10, 0)
camera.eulerAngles = SCNVector3(-Float.pi/2, 0, 0)
rootNode.addChildNode(camera)
for i in 0..<100 {
let box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0)
let node = SCNNode(geometry: box)
node.position = SCNVector3(x: .random(in: -1...1), y: .random(in: 0.5...2.0), z: .random(in: -1...1))
node.physicsBody = SCNPhysicsBody(type: .dynamic, shape: SCNPhysicsShape(geometry: box, options: nil))
rootNode.addChildNode(node)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class MyEntity: Entity {
required init() {
super.init()
let floorMesh = MeshResource.generatePlane(width: 50, depth: 50)
let floorEntity = ModelEntity(mesh: floorMesh, materials: [SimpleMaterial(color: .darkGray, isMetallic: false)])
floorEntity.generateCollisionShapes(recursive: true)
floorEntity.physicsBody = PhysicsBodyComponent(mode: .static)
floorEntity.position = [0, 0, 0]
addChild(floorEntity)
for i in 0...100 {
let mesh = MeshResource.generateBox(size: 1)
let material = SimpleMaterial(color: .blue, isMetallic: true)
let box = ModelEntity(mesh: mesh, materials: [material])
box.generateCollisionShapes(recursive: true)
let physicsBody = PhysicsBodyComponent(
massProperties: .init(shape: .generateBox(size: [1, 1, 1]), mass: 1),
material: .default,
mode: .dynamic)
box.physicsBody = physicsBody
box.position = [.random(in: -1...1), .random(in: 0.5...2.0), .random(in: -1...1)]
addChild(box)
}
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let scnView = SCNView(frame: CGRectMake(0, 0, 300, 300))
scnView.scene = MySCNScene()
scnView.autoenablesDefaultLighting = true
view.addSubview(scnView)
let arView = ARView(
frame: CGRectMake(0, 400, 300, 300),
cameraMode: .nonAR,
automaticallyConfigureSession: false)
arView.environment.background = .color(.gray)
let anchor = AnchorEntity(world: .zero)
let camera = PerspectiveCamera()
camera.look(at: .zero, from: [0, 10, 0], upVector: [0, 0, -1], relativeTo: nil)
anchor.addChild(camera)
arView.scene.addAnchor(anchor)
anchor.addChild(MyEntity())
view.addSubview(arView)
}
}
When you run the app, you will see 2 views, with SCNView on top, and ARView on bottom.
In each view, I add 100 boxes at random positions and they will overlap. SceneKit gently resolves the collision, leaving all of them on the screen. RealityKit pushes away the boxes like an explosion, which is bad for my game.
You can download a video of the result here:
Note: I don’t want to change the linearDamping or angularDamping since it would mess up with normal movement in gravity. I just want the collision/interpenetration to be resolved more gently