RealityKit & Reality Composer Pro

Mar 27 2024 · Swift 5.10, visionOS 1.1, Xcode 15.3

Lesson 02: Creating 3D Models & Animations in RealityKit

Demo

Episode complete

Play next episode

Next

Heads up... You've reached locked video content where the transcript will be shown as obfuscated text.

Building to Vision Pro

You can start with the app in the Starter folder or continue the build from lesson 1.

Updating the Code

You may be familiar with the wooden labyrinth toys, where you roll a metal ball by tilting the game. In this lesson, you’ll extend the code to put the ball on a surface and tilt to move left and right.

Toggle("Show ImmersiveSpace", systemImage: "mountain.2", isOn: $showImmersiveSpace)
//content.add(scene)
if let _ = try? await Entity(named: "Immersive", in: realityKitContentBundle) { //.. }
@State var mazeA = Entity()
// dimensions
let mazeX: Float = 1.0
let mazeY: Float = 0.1
let mazeZ: Float = 0.1
mazeA = ModelEntity(mesh: .generateBox(width: mazeX, height: mazeY, depth: mazeZ), materials: [SimpleMaterial()])
mazeA.position.y = 0.9
mazeA.position.z = -1.5025
content.add(mazeA)
mazeA.components.set(CollisionComponent(shapes: [.generateBox(width: mazeX, height: mazeY, depth: mazeZ)]))
mazeA.components[PhysicsBodyComponent.self] = .init(
  PhysicsBodyComponent(
    massProperties: .default,
    material: .generate(
      staticFriction: 0.8,
      dynamicFriction: 0.0,
      restitution: 0.0
    ),
    mode: .kinematic
  )
)
// change ball constructor:
let ball = ModelEntity(
  mesh: .generateSphere(radius: 0.05),
  materials: [SimpleMaterial(color: .white, isMetallic: true)]
)

// change PhysicsBodyComponent massProperties:
massProperties: .init(mass: 50.0),

// change the material .generate:
staticFriction: 10.0,
dynamicFriction: 50.0,
restitution: 0.0

// add a CollisionComponent
ball.components.set(CollisionComponent(shapes: [.generateSphere(radius: 0.05)]))
@State var rotationA: Angle = .zero
.gesture(DragGesture()
  .targetedToAnyEntity()
)
.onChanged { value in
  rotationA.degrees = value.translation.height / 20
  mazeA.transform = Transform(roll: Float(rotationA.radians))
  // Keep starting distance between models
  mazeA.position.y = 0.9
  mazeA.position.z = -1.5
}
mazeA.components.set(InputTargetComponent())
func occludedBlock(width: Float, height: Float, depth: Float, posX: Float, posY: Float ) -> Entity {
  let entity = ModelEntity(mesh: .generateBox(width: width, height: height, depth: depth))
  entity.components.set(CollisionComponent(shapes: [.generateBox(width: width, height: height, depth: depth)]))
  entity.components[PhysicsBodyComponent.self] = .init(mode: .static)
  entity.position.x = posX
  entity.position.y = posY
  return entity
}
let blockRight = occludedBlock(
  width: mazeY,
  height: mazeY * 2,
  depth: mazeY * 2,
  posX: mazeX / 2 + mazeY / 2,
  posY: mazeY
)
mazeA.addChild(blockRight)

let blockLeft = occludedBlock(
  width: mazeY,
  height: mazeY * 2,
  depth: mazeY * 2,
  posX: -(
    mazeX / 2 + mazeY / 2
  ),
  posY: mazeY
)
mazeA.addChild(blockLeft)
entity.components[OpacityComponent.self] = .init(opacity: 0.0)
RealityView { content, attachments in
} attachments: {
}
Attachment(id: "maze-attach") {
  VStack {
    Text("Maze")
      .font(.largeTitle)
    Text("Drag to tilt the maze.")
      .font(.title)
  }
  .padding(.all, 20)
  .frame(maxWidth: 250, maxHeight: 250)
  .glassBackgroundEffect()
}
if let mazeAttachment = attachments.entity(for: "maze-attach") {
  mazeAttachment.position = [mazeX / 2, 0, 2 * mazeZ ]
  mazeA.addChild(mazeAttachment)
}
let fulcrum = ModelEntity(mesh: .generateCone(height: 0.2, radius: 0.1), materials: [SimpleMaterial()])
fulcrum.position.y = 0.75
fulcrum.position.z = -1.5
content.add(fulcrum)
See forum comments
Cinema mode Download course materials from Github
Previous: Instruction Next: Conclusion