How to make overlapping physics bodies to resolve collision more "gently" in RealityKit (like in SceneKit)

1 day ago 1
ARTICLE AD BOX

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: https://drive.google.com/file/d/108rE0r0yRoQjuj_dw8jc6JnbkPdmba_o/view?usp=sharing

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: https://drive.google.com/file/d/1KwzLUYBxEKpIW_SPFCK7czb8w0qJkTBi/view?usp=sharing

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

Read Entire Article