Can anyone provide or point me to example code to fade in / out spotlights over 1 second?
Did not find anything on this topic in the docs: https://developer.apple.com/documentation/realitykit/spotlight
Hi @mesqueeb,
The following code demonstrates one approach to fading a spotlight in and out. It works by increasing and decreasing the intensity
property of a SpotLightComponent
.
More specifically, RealityKit's Entity Component System is used to create a SpotLightFaderComponent
which stores information about the spotlight fade in/out animation, such as its current state and duration. A SpotLightFaderSystem
is then responsible for updating the intensity
poperty of the SpotLightComponent
every frame in correspondance with the animation data held in SpotLightFaderComponent
.
enum FaderState {
case fadingIn
case fadingOut
case stopped
}
class SpotLightFaderComponent: Component {
private(set) var state: FaderState = .stopped
let targetIntensity: Float
let fadeInDuration: Float
let fadeOutDuration: Float
init(targetIntensity: Float, fadeInDuration: Float, fadeOutDuration: Float) {
self.state = .stopped
self.targetIntensity = targetIntensity
self.fadeInDuration = fadeInDuration
self.fadeOutDuration = fadeOutDuration
}
func fadeIn() { state = .fadingIn }
func fadeOut() { state = .fadingOut }
func stop() { state = .stopped }
}
struct SpotLightFaderSystem: System {
private let query = EntityQuery(where: .has(SpotLightFaderComponent.self) && .has(SpotLightComponent.self))
init(scene: RealityKit.Scene) { }
func update(context: SceneUpdateContext) {
for spotlightFaderEntity in context.entities(matching: self.query, updatingSystemWhen: .rendering) {
guard let spotlightFader = spotlightFaderEntity.components[SpotLightFaderComponent.self],
let spotlight = spotlightFaderEntity.components[SpotLightComponent.self] else { continue }
// Get the current spotlight intensity
var intensity = spotlight.intensity
switch spotlightFader.state {
case .fadingIn:
// Increase spotlight intensity up to target intensity when fading in
if intensity < spotlightFader.targetIntensity {
// (deltaTime / fadeInDuration) gives the percentage of the total fadeInDuration that has elapsed since last frame,
// so multiplying it by targetIntensity gives the intensity increase over the last frame
intensity += (Float(context.deltaTime) / spotlightFader.fadeInDuration) * spotlightFader.targetIntensity
}
if intensity >= spotlightFader.targetIntensity {
intensity = spotlightFader.targetIntensity
spotlightFader.stop()
}
case .fadingOut:
// Decrease spotlight intensity down to zero when fading out
if intensity > 0 {
// (deltaTime / fadeOutDuration) gives the percentage of the total fadeOutDuration that has elapsed since last frame,
// so multiplying it by targetIntensity gives the intensity decrease over the last frame
intensity -= (Float(context.deltaTime) / spotlightFader.fadeOutDuration) * spotlightFader.targetIntensity
}
if intensity <= 0 {
intensity = 0
spotlightFader.stop()
}
case .stopped:
// Do nothing when fading animation is stopped
continue
}
// Update the spotlight intensity
spotlightFaderEntity.components[SpotLightComponent.self]?.intensity = intensity
}
}
}
To create a spotlight that can fade in and out using the code above, add an entity with both a SpotLightComponent
and a SpotLightFaderComponent
to your RealityView
. You can use the fadeInDuration
and fadeOutDuration
properties of the SpotLightFaderComponent
to control the duration of the fade in and fade out animations, and use the targetIntensity
property to set the default intensity the spotlight returns to when fading back in. To initiate a spotlight fade out animation, call the fadeOut()
method on the SpotLightFaderComponent
. Likewise, call the fadeIn()
method to fade the spotlight back in.
@State var spotlightFader:SpotLightFaderComponent?
var body: some View {
RealityView { content, attachments in
// Register the spotlight fader system
SpotLightFaderSystem.registerSystem()
// Create an entity with a spotlight component
let spotlightEntity = Entity()
let spotlight = SpotLightComponent()
spotlightEntity.components.set(spotlight)
// Create a spotlight fader component and add it to the same entity
let spotlightFader = SpotLightFaderComponent(targetIntensity: spotlight.intensity, fadeInDuration: 1, fadeOutDuration: 1)
spotlightEntity.components.set(spotlightFader)
self.spotlightFader = spotlightFader
// Create a ground plane entity to help visualize the spotlight
let groundPlaneEntity = ModelEntity(mesh: .generatePlane(width: 5, depth: 5), materials: [SimpleMaterial()])
// Point the spotlight toward the ground
spotlightEntity.transform.rotation = simd_quatf(angle: -90 * (.pi / 180), axis: [1,0,0])
spotlightEntity.position = [0, 2, -1.5]
// Add the spotlight and the ground plane to the scene
content.add(spotlightEntity)
content.add(groundPlaneEntity)
// Add fade in/out buttons to scene
guard let fadeInButtonEntity = attachments.entity(for: "fadeInButton"),
let fadeOutButtonEntity = attachments.entity(for: "fadeOutButton") else { return }
fadeInButtonEntity.position = [0.1, 1, -0.5]
fadeOutButtonEntity.position = [-0.1, 1, -0.5]
content.add(fadeInButtonEntity)
content.add(fadeOutButtonEntity)
} attachments: {
Attachment(id: "fadeInButton") {
Button("Fade In") {
guard let spotlightFader = self.spotlightFader else { return }
spotlightFader.fadeIn()
}
}
Attachment(id: "fadeOutButton") {
Button("Fade Out") {
guard let spotlightFader = self.spotlightFader else { return }
spotlightFader.fadeOut()
}
}
}
}