RealityKit Basics: Modify Component Values
How do we change the values of our components?
Overview
We learned how to create Entities and Components. What if we want to change the value of components? There are several approaches we can take, depending on the component and use case.
Let’s start with a simple example. We can add an opacity component to make our subject transparent.
subjectEntity.components.set(OpacityComponent(opacity: 0.25))If we want to remove the opacity, we can either set the value to 1.0 or simply remove the component. Entities without the opacity component with render fully based on their material values.
subjectEntity.components.set(OpacityComponent(opacity: 1.0))
// or
subjectEntity.components.remove(OpacityComponent.self)Note: I’ve seen some issues with entities that have an opacity component value set to 1.0, so I prefer to remove the component.
What about a more complex component, such as a material? This can take a few more steps. Let’s assume we want to keep most of the material values, but we want to change a single value on it. We can get the material from the model component on our entity. Make sure to cast it to the material type you’re using. Then we can adjust the value–the baseColor in this case.
Most importantly, we need to re-add the component to the entity. I can’t stress this enough. When we get a component from an entity, we get a copy of it. The entity still has the component. Changes to our copy will do nothing until we add it back to the entity. Each entity in RealityKit can only have a single component attached. Adding it back will overwrite the existing version. We don’t have to worry about removing the old one first.
if var material = subjectEntity.components[ModelComponent.self]?.materials.first as? PhysicallyBasedMaterial {
material.baseColor.tint = UIColor(color)
subjectEntity.components[ModelComponent.self]?.materials[0] = material
}What if we need to modify a component value over time? For example, accumulation of a duration for a timer. That is best suited to a system. Let’s use the Breath component and system from Lab 010 as an example. The component has duration and accumulatedTime, both of which which are needed to calculate the scale for each frame. We get the component from the entity and read the values. We perform the calculations we need to, then write the new values to the component and add it to the entity.
// Get the component
guard var breath = entity.components[BreathComponent.self] else { continue }
let duration = breath.duration
// Accumulate time for this entity and set the new value on the component
breath.accumulatedTime += Float(context.deltaTime)
// Calculate the phase of the sine wave (0 to 2Ï€), wrapping by duration
let phase = (breath.accumulatedTime / duration) * 2.0 * .pi
// Compute the scale
let scale = 1 + 0.5 * sin(phase)
// Apply the scale to the entity
entity.transform.scale = .init(repeating: scale)
// Reset accumulated time if a full cycle has passed
if breath.accumulatedTime >= duration {
breath.accumulatedTime = 0.0
}
entity.components[BreathComponent.self] = breathWe could reset the component by setting a new instance to the entity. Example: We’re using a 4 second duration and want to switch to a 2 second duration. This discards any accumulatedTime value because we ignore the existing version of the component and overwrite it with a new one.
var breath = BreathComponent()
breath.duration = 2.0
subjectEntity.components.set(breath)Video Demo
Example Code
struct Example067: View {
init() {
BreathComponent.registerComponent()
BreathSystem.registerSystem()
}
@State var subjectEntity = Entity()
var body: some View {
RealityView { content in
var material = PhysicallyBasedMaterial()
material.baseColor.tint = .stepRed
material.roughness = 0.5
material.metallic = 0.0
let shape = MeshResource.generateSphere(radius: 0.2)
let model = ModelComponent(mesh: shape, materials: [material])
subjectEntity.components.set(model)
subjectEntity.name = "Subject"
content.add(subjectEntity)
}
.toolbar {
ToolbarItem(placement: .bottomOrnament, content: {
HStack {
ColorButton(color: .stepRed, subjectEntity: $subjectEntity)
ColorButton(color: .stepGreen, subjectEntity: $subjectEntity)
ColorButton(color: .stepBlue, subjectEntity: $subjectEntity)
Divider()
.padding(.horizontal, 12)
Button(action: {
subjectEntity.components.set(OpacityComponent(opacity: 0.25))
}, label: {
Image(systemName: "circle.dotted")
})
Button(action: {
subjectEntity.components.remove(OpacityComponent.self)
}, label: {
Image(systemName: "circle.fill")
})
Divider()
.padding(.horizontal, 12)
Button(action: {
subjectEntity.components.remove(BreathComponent.self)
}, label: {
Image(systemName: "stop.circle")
})
Button(action: {
var breath = BreathComponent()
breath.duration = 2.0
subjectEntity.components.set(breath)
}, label: {
Image(systemName: "2.circle.fill")
})
Button(action: {
var breath = BreathComponent()
breath.duration = 4.0
subjectEntity.components.set(breath)
}, label: {
Image(systemName: "4.circle.fill")
})
}
})
}
}
}fileprivate struct ColorButton: View {
@State var color: Color
@Binding var subjectEntity: Entity
var body: some View {
Button(action: {
if var material = subjectEntity.components[ModelComponent.self]?.materials.first as? PhysicallyBasedMaterial {
material.baseColor.tint = UIColor(color)
subjectEntity.components[ModelComponent.self]?.materials[0] = material
}
}, label: {
color
.frame(width: 44, height: 44)
.clipShape(.circle)
})
.buttonStyle(.plain)
}
}fileprivate struct BreathComponent: Component, Codable {
/// The time it will take for a full cycle of the breath animation
public var duration: Float = 4.0
/// Store accumation time used for the breath animation
public var accumulatedTime: Float = 0
public init() {
}
}
// This was used by Lab 010 to learn my way around components and systems
fileprivate class BreathSystem: System {
// Define a query to return all entities with a BreathComponent.
private static let query = EntityQuery(where: .has(BreathComponent.self))
// init is required even when not used
required public init(scene: RealityFoundation.Scene) {
// Perform required initialization or setup.
}
public func update(context: SceneUpdateContext) {
for entity in context.entities(
matching: Self.query,
updatingSystemWhen: .rendering
) {
// Get the component
guard var breath = entity.components[BreathComponent.self] else { continue }
let duration = breath.duration
// Accumulate time for this entity and set the new value on the component
breath.accumulatedTime += Float(context.deltaTime)
// Calculate the phase of the sine wave (0 to 2Ï€), wrapping by duration
let phase = (breath.accumulatedTime / duration) * 2.0 * .pi
// Compute the scale
let scale = 1 + 0.5 * sin(phase)
// Apply the scale to the entity
entity.transform.scale = .init(repeating: scale)
// Reset accumulated time if a full cycle has passed
if breath.accumulatedTime >= duration {
breath.accumulatedTime = 0.0
}
entity.components[BreathComponent.self] = breath
}
}
}Support our work so we can continue to bring you new examples and articles.
Download the Xcode project with this and many more examples from Step Into Vision.
Some examples are provided as standalone Xcode projects. You can find those here.

Follow Step Into Vision