Collisions & Physics: Hello Physics Simulation Component

We can use this component to create localized physics rules.

Overview

The first thing to know is that this component should always be added to parent of the physics entities. Adding it directly to an entity with other physics components may result in some odd behaviors. Let’s load a scene from Reality Composer Pro, then add this component to the root entity.

guard let scene = try? await Entity(named: "PhysicsSimulationBasics", in: realityKitContentBundle) else { return }
content.add(scene)

if let root = scene.findEntity(named: "Root") {
    var simulation = PhysicsSimulationComponent()
    simulation.gravity = [0, 9.81, 0]
    root.components.set(simulation)
}

This scene has two static physics objects and a blue sphere with dynamic physics.

Let’s look at an example of customizing gravity. By default, gravity in RealityKit is defined as -9.81 on the y axis. This is an approximation of earth gravity. If we invert the number, dynamic bodies will “fall” up.

var simulation = PhysicsSimulationComponent()
simulation.gravity = [0, 9.81, 0] // gravity is now up!
root.components.set(simulation)

Playing with gravity can bring a lot of fun and delight to users. This component has a few more features.

  • clock: we can define custom clocks, for example if we need to evaluate physics more or less often
  • collision options: we can tailor how collisions are reported
  • solver options: we can adjust the number of iterations for solving physics. This can be helpful if your scene has very fast moving objects.

This component is required for some other features, such as Physics Joints.

Video Demo

Example Code

struct Example062: View {

    @State var gravity: SIMD3<Float> = [0, 9.8, 0]

    var body: some View {
        RealityView { content in
            guard let scene = try? await Entity(named: "PhysicsSimulationBasics", in: realityKitContentBundle) else { return }

            content.add(scene)
            scene.position.y = -0.4

            if let root = scene.findEntity(named: "Root") {
                var simulation = PhysicsSimulationComponent()
                simulation.gravity = gravity
                root.components.set(simulation)
            }
        } update: { content in
            // Update the simulation gravity when the value changes
            var simulation = PhysicsSimulationComponent()
            simulation.gravity = gravity
            content.entities.first?.findEntity(named: "Root")?.components.set(simulation)
        }
        .ornament(attachmentAnchor: .scene(.trailingFront), ornament: {
            VStack {
                Button(action:  {
                    gravity = [0, 9.81, 0]
                }, label: {
                    Label("Up Strong", systemImage: "chevron.up.2")
                        .frame(maxWidth: .infinity, alignment: .leading)
                })
                Button(action:  {
                    gravity = [0, 1, 0]
                }, label: {
                    Label("Up Weak", systemImage: "chevron.up")
                        .frame(maxWidth: .infinity, alignment: .leading)
                })
                Button(action:  {
                    gravity = [0, -1, 0]
                }, label: {
                    Label("Down Weak", systemImage: "chevron.down")
                        .frame(maxWidth: .infinity, alignment: .leading)
                })

                Button(action:  {
                    gravity = [0, -9.81, 0]
                }, label: {
                    Label("Down Strong", systemImage: "chevron.down.2")
                        .frame(maxWidth: .infinity, alignment: .leading)
                })
            }
            .padding()
            .glassBackgroundEffect()
        })
    }
}

Support our work so we can continue to bring you new examples and articles.

Download the Xcode project with this and many more examples from Step Into Vision.
Some examples are provided as standalone Xcode projects. You can find those here.

Questions or feedback?