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.

Follow Step Into Vision