Skip to content

Instantly share code, notes, and snippets.

@ynagatomo
Created November 16, 2023 08:30
Show Gist options
  • Save ynagatomo/9e89c0ca9bf1c408b05ded887e852920 to your computer and use it in GitHub Desktop.
Save ynagatomo/9e89c0ca9bf1c408b05ded887e852920 to your computer and use it in GitHub Desktop.
A sample of Head-up Display Component for visionOS
// ContentView.swift
import SwiftUI
import RealityKit
import RealityKitContent
struct ContentView: View {
@State private var showImmersiveSpace = false
@State private var immersiveSpaceIsShown = false
@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
var body: some View {
VStack {
Model3D(named: "Scene", bundle: realityKitContentBundle)
.padding(.bottom, 50)
Text("Hello, world!")
Toggle("Show Immersive Space", isOn: $showImmersiveSpace)
.toggleStyle(.button)
.padding(.top, 50)
}
.padding()
.onChange(of: showImmersiveSpace) { _, newValue in
Task {
if newValue {
switch await openImmersiveSpace(id: "ImmersiveSpace") {
case .opened:
immersiveSpaceIsShown = true
case .error, .userCancelled:
fallthrough
@unknown default:
immersiveSpaceIsShown = false
showImmersiveSpace = false
}
} else if immersiveSpaceIsShown {
await dismissImmersiveSpace()
immersiveSpaceIsShown = false
}
}
}
}
}
#Preview(windowStyle: .automatic) {
ContentView()
}
// HUDComponent.swift
import ARKit
import RealityKit
import SwiftUI
import simd
/// HUDComponent provides a Head Up Display
/// - position: HUD position relative to the device (AVP)
/// - looking: if true (default), the HUD always looks at the device
public struct HUDComponent: Component, Codable {
let position: SIMD3<Float>
let looking: Bool
public init(at position: SIMD3<Float>, looking: Bool = true) {
self.position = position
self.looking = looking
}
}
/// HUDSystem
public struct HUDSystem: System {
static let query = EntityQuery(where: .has(HUDComponent.self))
private let arkitSession = ARKitSession()
private let worldTrackingProvider = WorldTrackingProvider()
public init(scene: RealityKit.Scene) {
setUpSession()
}
private func setUpSession() {
Task {
do {
try await arkitSession.run([worldTrackingProvider])
} catch {
print("Error: \(error)")
}
}
}
public func update(context: SceneUpdateContext) {
let entities = context.scene.performQuery(Self.query).map({ $0 })
guard !entities.isEmpty,
let deviceAnchor = worldTrackingProvider.queryDeviceAnchor(atTimestamp:
CACurrentMediaTime()) else { return }
let cameraTransform = Transform(matrix: deviceAnchor.originFromAnchorTransform)
for entity in entities {
guard let hud = entity.components[HUDComponent.self] else { return }
if hud.looking {
entity.look(at: cameraTransform.translation,
from: entity.position(relativeTo: nil),
relativeTo: nil,
forward: .positiveZ)
}
entity.transform.translation
= simd_act(cameraTransform.rotation, hud.position) + cameraTransform.translation
}
}
}
#Preview {
RealityView { content, attachments in
HUDSystem.registerSystem()
HUDComponent.registerComponent()
if let entity = attachments.entity(for: "previewTag") {
let hudComponent = HUDComponent(at: SIMD3<Float>(0.5, 0.5, -1.0))
entity.components[HUDComponent.self] = hudComponent
content.add(entity)
}
} attachments: {
Attachment(id: "previewTag") {
Text("Preview")
.font(.system(size: 100))
.background(.pink)
}
}
.previewLayout(.sizeThatFits)
}
// HUDTestApp.swift
import SwiftUI
@main
struct HUDTestApp: App {
init() {
HUDSystem.registerSystem()
HUDComponent.registerComponent()
}
var body: some Scene {
WindowGroup {
ContentView()
}
ImmersiveSpace(id: "ImmersiveSpace") {
ImmersiveView()
}
}
}
// ImmersiveView.swift
import SwiftUI
import RealityKit
import RealityKitContent
struct ImmersiveView: View {
var body: some View {
RealityView { content, attachments in
// [HUD-01]
if let entity = attachments.entity(for: "hud01") {
let hudComponent = HUDComponent(at: SIMD3<Float>(0.5, 0.5, -1.0))
entity.components[HUDComponent.self] = hudComponent
content.add(entity)
}
// [HUD-02]
if let entity = attachments.entity(for: "hud02") {
let hudComponent = HUDComponent(at: SIMD3<Float>(-0.5, 0.5, -1.0), looking: false)
entity.components[HUDComponent.self] = hudComponent
content.add(entity)
}
} attachments: {
Attachment(id: "hud01") {
HUDPanelView(title: "HUD-01")
}
Attachment(id: "hud02") {
HUDPanelView(title: "HUD-02")
}
}
}
}
#Preview {
ImmersiveView()
.previewLayout(.sizeThatFits)
}
struct HUDPanelView: View {
let title: String
var body: some View {
VStack {
Text(title)
.font(.title)
.padding()
HStack {
Button(action: {}, label: { Image(systemName: "star") })
Button(action: {}, label: { Image(systemName: "list.bullet") })
Button(action: {}, label: { Image(systemName: "list.bullet") })
Button(action: {}, label: { Image(systemName: "slider.horizontal.3") })
}
.font(.title2)
.padding()
Text("Status: 50%")
}
// .font(.system(size: 100))
.padding()
.glassBackgroundEffect(in: .rect(cornerRadius: 20.0))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment