Lab 043 – Visualize the Entity Spawner
Visualize and test the spawn shapes.
Continuing the work from Labs 016 and 042, I set up a way to visualize the spawn shapes.
Video demo of several volume-shaped entity spawners.
Lab Code
This lab loads some resources from a Reality Composer Pro scene, then uses them to construct these examples.
struct Lab043: View {
init() {
EntitySpawnerComponent.registerComponent()
EntitySpawnerSystem.registerSystem()
}
@State var collisionEvent: EventSubscription?
var body: some View {
RealityView { content in
guard let scene = try? await Entity(named: "SpawnerLabResource", in: realityKitContentBundle) else { return }
content.add(scene)
guard let baseTemplate = scene.findEntity(named: "Base"),
let shapeVisTemplate = scene.findEntity(named: "ShapeVis"),
let domeVis = scene.findEntity(named: "DomeVis"),
let subject = scene.findEntity(named: "Subject"),
let floor = scene.findEntity(named: "Floor")
else { return }
guard let baseMaterial = baseTemplate.components[ModelComponent.self]?.materials.first as? PhysicallyBasedMaterial,
let shapeVisMaterial = shapeVisTemplate.components[ModelComponent.self]?.materials.first as? PhysicallyBasedMaterial
else { return }
let boxSpawner = createSpawnerSetup(
position: [-2, 1, 2],
baseMaterial: baseMaterial,
shapeVisMaterial: shapeVisMaterial,
spawnShape: .box,
visualizationMesh: .generateBox(width: 1.1, height: 1.1, depth: 1.1)
)
scene.addChild(boxSpawner)
let planeSpawner = createSpawnerSetup(
position: [0, 1, 2],
baseMaterial: baseMaterial,
shapeVisMaterial: shapeVisMaterial,
spawnShape: .plane,
visualizationMesh: .generatePlane(width: 1.1, depth: 1.1)
)
scene.addChild(planeSpawner)
let circleSpawner = createSpawnerSetup(
position: [2, 1, 2],
baseMaterial: baseMaterial,
shapeVisMaterial: shapeVisMaterial,
spawnShape: .circle,
visualizationMesh: .generateCylinder(height: 0.01, radius: 0.55)
)
scene.addChild(circleSpawner)
let sphereSpawner = createSpawnerSetup(
position: [-2, 1, -2],
baseMaterial: baseMaterial,
shapeVisMaterial: shapeVisMaterial,
spawnShape: .sphere,
visualizationMesh: .generateSphere(radius: 0.55)
)
scene.addChild(sphereSpawner)
let upperDomeSpawner = createSpawnerSetup(
position: [0, 1, -2],
baseMaterial: baseMaterial,
shapeVisMaterial: shapeVisMaterial,
spawnShape: .domeUpper,
visualizationEntity: domeVis.clone(recursive: true)
)
scene.addChild(upperDomeSpawner)
let lowerDomeSpawner = createSpawnerSetup(
position: [2, 1, -2],
baseMaterial: baseMaterial,
shapeVisMaterial: shapeVisMaterial,
spawnShape: .domeLower,
visualizationEntity: domeVis.clone(recursive: true),
rotateVisualization: true
)
scene.addChild(lowerDomeSpawner)
// Disable any spheres that reach the floor
collisionEvent = content
.subscribe(to: CollisionEvents.Began.self, on: floor) { event in
event.entityB.isEnabled = false
event.entityB.components.set(PhysicsMotionComponent())
}
// Disable all template entities after using them
baseTemplate.isEnabled = false
shapeVisTemplate.isEnabled = false
domeVis.isEnabled = false
subject.isEnabled = false
}
.gesture(tap)
.modifier(DragGestureImproved())
}
private func createSpawnerSetup(
position: SIMD3<Float>,
baseMaterial: PhysicallyBasedMaterial,
shapeVisMaterial: PhysicallyBasedMaterial,
spawnShape: EntitySpawnerComponent.SpawnShape,
visualizationMesh: MeshResource? = nil,
visualizationEntity: Entity? = nil,
rotateVisualization: Bool = false
) -> Entity {
// Create the base platform as a parent entity
let base = ModelEntity(
mesh: .generateBox(width: 1.1, height: 0.1, depth: 1.1),
materials: [baseMaterial]
)
base.position = position
base.collision = CollisionComponent(shapes: [.generateBox(width: 1.1, height: 0.1, depth: 1.1)])
base.physicsBody = PhysicsBodyComponent(
massProperties: .init(mass: 1.0),
material: .default,
mode: .static
)
base.components.set(InputTargetComponent())
// Create walls for the base
let wallHeight: Float = 0.1
let wallThickness: Float = 0.05
let wallWidth: Float = 1.1
let wallTemplate = Entity()
wallTemplate.components.set(ModelComponent(
mesh: .generateBox(width: wallWidth, height: wallHeight, depth: wallThickness),
materials: [baseMaterial]
))
wallTemplate.components.set(CollisionComponent(
shapes: [.generateBox(width: wallWidth, height: wallHeight, depth: wallThickness)]
))
wallTemplate.components.set(PhysicsBodyComponent(
massProperties: .init(mass: 1.0),
material: .default,
mode: .static
))
let frontWall = wallTemplate.clone(recursive: true)
frontWall.position = [0, wallHeight/2, 0.55 - wallThickness/2]
base.addChild(frontWall)
let backWall = wallTemplate.clone(recursive: true)
backWall.position = [0, wallHeight/2, -0.55 + wallThickness/2]
base.addChild(backWall)
let leftWall = wallTemplate.clone(recursive: true)
leftWall.orientation = simd_quatf(angle: .pi/2, axis: [0, 1, 0])
leftWall.position = [-0.55 + wallThickness/2, wallHeight/2, 0]
base.addChild(leftWall)
let rightWall = wallTemplate.clone(recursive: true)
rightWall.orientation = simd_quatf(angle: -.pi/2, axis: [0, 1, 0])
rightWall.position = [0.55 - wallThickness/2, wallHeight/2, 0]
base.addChild(rightWall)
// Create spawn volume visualization
let shapeVis: Entity
if let visualizationEntity = visualizationEntity {
shapeVis = visualizationEntity
if rotateVisualization {
// Rotate 180 degrees for lower dome
shapeVis.orientation = simd_quatf(angle: .pi, axis: [1, 0, 0])
}
} else {
shapeVis = ModelEntity(
mesh: visualizationMesh!,
materials: [shapeVisMaterial]
)
}
shapeVis.position = [0, 1, 0]
shapeVis.components.remove(InputTargetComponent.self)
base.addChild(shapeVis)
// Create and setup spawner
let spawner = Entity()
spawner.position = [0, 1, 0]
var spawnerComponent = EntitySpawnerComponent()
spawnerComponent.SpawnShape = spawnShape
// Set appropriate dimensions based on spawn shape
switch spawnShape {
case .plane:
spawnerComponent.PlaneDimensions = [1, 1]
case .circle:
spawnerComponent.Radius = 0.5
case .box:
spawnerComponent.BoxDimensions = [1, 1, 1]
case .sphere, .domeUpper, .domeLower:
spawnerComponent.Radius = 0.5
}
spawnerComponent.Copies = 10
spawnerComponent.TargetEntityName = "Subject"
spawner.components[EntitySpawnerComponent.self] = spawnerComponent
base.addChild(spawner)
return base
}
var tap: some Gesture {
TapGesture()
.targetedToEntity(where: .has(PhysicsMotionComponent.self))
.onEnded { value in
value.entity.isEnabled = false
}
}
}Support our work so we can continue to bring you new examples and articles.
Download the Xcode project with this and many more labs from Step Into Vision.

Follow Step Into Vision