Created
May 24, 2025 22:12
-
-
Save MatthewWaller/d273d8a0cb3f70796b7fffb12617182f to your computer and use it in GitHub Desktop.
ReailtyKit clock hand entity system, useful for visionOS, in Swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import RealityKit | |
// First, let's create a component to mark our clock hands | |
struct ClockHandComponent: Component { | |
var handType: HandType | |
enum HandType { | |
case hour | |
case minute | |
case second | |
var rotationSpeed: Float { | |
switch self { | |
case .second: | |
return -(2 * .pi / 60) // Negative for clockwise rotation | |
case .minute: | |
return -(2 * .pi / 3600) | |
case .hour: | |
return -(2 * .pi / 43200) | |
} | |
} | |
} | |
} | |
// Create the system to handle rotation | |
class ClockSystem: System { | |
required init(scene: Scene) { } | |
static let query = EntityQuery(where: .has(ClockHandComponent.self)) | |
func update(context: SceneUpdateContext) { | |
for entity in context.entities(matching: Self.query, updatingSystemWhen: .rendering) { | |
guard let clockHand = entity.components[ClockHandComponent.self] else { continue } | |
// Calculate rotation based on current time | |
let elapsedTime = Float(context.deltaTime) | |
let rotationAmount = clockHand.handType.rotationSpeed * elapsedTime | |
// Create rotation quaternion for this frame | |
let rotation = simd_quatf(angle: rotationAmount, axis: SIMD3<Float>(0, 0, 1)) | |
// Apply rotation | |
entity.transform.rotation = entity.transform.rotation * rotation | |
} | |
} | |
} | |
// Extension to help set up the clock | |
// You'll want to create your own TimepieceObject which has properties for the handnames as shown below. | |
extension Entity { | |
func setupAsClock(forTimepiece timepiece: TimepieceObject) { | |
if let secondHand = self.findEntity(named: timepiece.secondHandName) { | |
secondHand.components[ClockHandComponent.self] = ClockHandComponent(handType: .second) | |
} | |
if let minuteHand = self.findEntity(named: timepiece.minuteHandName) { | |
minuteHand.components[ClockHandComponent.self] = ClockHandComponent(handType: .minute) | |
} | |
if let hourHand = self.findEntity(named: timepiece.hourHandName) { | |
hourHand.components[ClockHandComponent.self] = ClockHandComponent(handType: .hour) | |
} | |
// Set initial rotations based on current time | |
setInitialHandPositions(forTimepiece: timepiece) | |
} | |
private func setInitialHandPositions(forTimepiece timepiece: TimepieceObject) { | |
let calendar = Calendar.current | |
let components = calendar.dateComponents([.hour, .minute, .second], from: Date()) | |
let totalSeconds = Float(components.second ?? 0) | |
let totalMinutes = Float(components.minute ?? 0) * 60 + totalSeconds | |
let totalHours = Float(components.hour ?? 0) * 3600 + totalMinutes | |
if let hand = self.findEntity(named: timepiece.secondHandName) { | |
let angle = -(totalSeconds / 60) * 2 * .pi // Negative for clockwise | |
switch timepiece { | |
case .steampunkClock, .woodenDeskClock: | |
hand.transform.rotation = simd_quatf(angle: angle, axis: SIMD3<Float>(0, 0, 1)) | |
case .grandfatherClock: | |
hand.transform.rotation = simd_quatf(angle: -angle, axis: SIMD3<Float>(0, 1, 0)) | |
default: | |
break | |
} | |
} | |
if let hand = self.findEntity(named: timepiece.minuteHandName) { | |
let angle = -(totalMinutes / 3600) * 2 * .pi // Negative for clockwise | |
switch timepiece { | |
case .steampunkClock, .woodenDeskClock: | |
hand.transform.rotation = simd_quatf(angle: angle, axis: SIMD3<Float>(0, 0, 1)) | |
case .grandfatherClock: | |
hand.transform.rotation = simd_quatf(angle: -angle, axis: SIMD3<Float>(0, 1, 0)) | |
default: | |
break | |
} | |
} | |
if let hand = self.findEntity(named: timepiece.hourHandName) { | |
let angle = -(totalHours / 43200) * 2 * .pi // Negative for clockwise | |
switch timepiece { | |
case .steampunkClock, .woodenDeskClock: | |
hand.transform.rotation = simd_quatf(angle: angle, axis: SIMD3<Float>(0, 0, 1)) | |
case .grandfatherClock: | |
hand.transform.rotation = simd_quatf(angle: -angle, axis: SIMD3<Float>(0, 1, 0)) | |
default: | |
break | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment