Lab 108 – Cycle
In this lab, we cycle through the scenes created in Labs 105-107.
This lab is part of a series of experiments for an upcoming project we’re considering. Today we take the scenes from labs 105-107 and load them all. Then we hide 2 of the 3 scenes at any given time. A simple task timer cycles between these. We also show the portal content of each lab on the currently active screen.
Video Demo
A video shows movement around an immersive space as three scenes cycle into existence.
Lab Code
struct Lab108: View {
@State private var activeSceneIndex = 0
private let sceneNames = ["Vapor", "Mist", "Droplet"]
var body: some View {
RealityView { content in
guard let vapor = try? await Entity(named: "Vapor", in: realityKitContentBundle) else { return }
vapor.name = "Vapor"
await configurePortal(on: vapor, contentName: "Mist")
content.add(vapor)
guard let mist = try? await Entity(named: "Mist", in: realityKitContentBundle) else { return }
mist.name = "Mist"
mist.isEnabled = false
await configurePortal(on: mist, contentName: "Droplet")
content.add(mist)
guard let droplet = try? await Entity(named: "Droplet", in: realityKitContentBundle) else { return }
droplet.name = "Droplet"
droplet.isEnabled = false
await configurePortal(on: droplet, contentName: "DropletInner", allowsCrossing: true)
content.add(droplet)
} update: { content in
for (index, sceneName) in sceneNames.enumerated() {
content.entities.first { $0.name == sceneName }?.isEnabled = index == activeSceneIndex
}
}
.task {
while !Task.isCancelled {
try? await Task.sleep(for: .seconds(3))
activeSceneIndex = (activeSceneIndex + 1) % sceneNames.count
}
}
}
private func configurePortal(
on rootEntity: Entity,
contentName: String,
allowsCrossing: Bool = false
) async {
let portalContentRoot = Entity()
portalContentRoot.components.set(WorldComponent())
rootEntity.addChild(portalContentRoot)
if let screenSurface = rootEntity.findEntity(named: "ScreenSurface") {
var portalMaterial = PortalMaterial()
portalMaterial.faceCulling = .none
if var modelComponent = screenSurface.components[ModelComponent.self] {
modelComponent.materials = modelComponent.materials.map { _ in portalMaterial }
screenSurface.components.set(modelComponent)
screenSurface.components.set(PortalComponent(
target: portalContentRoot,
clippingMode: .plane(.negativeY),
crossingMode: allowsCrossing ? .plane(.negativeY) : .disabled
))
}
}
guard let portalScene = try? await Entity(named: contentName, in: realityKitContentBundle) else { return }
if allowsCrossing {
portalScene.components.set(PortalCrossingComponent())
} else {
portalScene.scale = .init(repeating: 0.5)
portalScene.position = [0, 4, -9]
portalScene.orientation = simd_quatf(angle: -6, axis: [1, 0, 0])
}
portalContentRoot.addChild(portalScene)
}
}Support our work so we can continue to bring you new examples and articles.

Follow Step Into Vision