visionOSでImmersiveSpaceにオブジェクトを表示してみる
前回の記事では3DオブジェクトをWindowやVolumeで表示したが、今回はより没入感のあるImmersiveSpaceにオブジェクトを表示してみる。
Immersive Spaceを表示する
テンプレートからプロジェクトを作成して、App構造体にImmersiveSpaceを追加しておく。今回は完全な没入型にしたかったので、FullImmersionStyleを指定。
@main
struct ImmersiveSpaceSampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
ImmersiveSpace(id: "ImmersiveSpace") {
ImmersiveView()
}
.immersionStyle(selection: .constant(.full), in: .full)
}
}
ContentViewには@Environmentでimmersive spaceをopen/dismissできる環境変数を用意して、ToggleのonChangeで呼ぶことで、immersive spaceを切り替えることができる。
struct ContentView: View {
@State var showImmersiveSpace = false
@Environment(\\.openImmersiveSpace) var openImmersiveSpace
@Environment(\\.dismissImmersiveSpace) var dismissImmersiveSpace
var body: some View {
Toggle("Show ImmersiveSpace", isOn: $showImmersiveSpace)
.toggleStyle(.button)
.onChange(of: showImmersiveSpace) { _, newValue in
Task {
if newValue {
await openImmersiveSpace(id: "ImmersiveSpace")
} else {
await dismissImmersiveSpace()
}
}
}
}
}
補足
openImmersiveSpaceはOpenImmersiveSpaceActionのインスタンスを取得して、構造体を関数のように呼び出せるcallAsFunction(id:)を実行することで、idで指定したimmersive spaceを表示する。なお、この処理は非同期的なのでawaitを付けてTask内で実行する必要がある。
Skyboxテクスチャとモデルを用意する
今回はHello Worldのサンプルコードからアセットをお借りして、宇宙空間(Starfield)を背景として、月のオブジェクトを表示したいので、WorldAssetsパッケージをローカルから追加しておく。
ImmersiveSpaceにオブジェクトを表示する
ImmersiveSpaceではWindowやVolumeも表示できるが、今回はRealityKitのコンテンツをSwiftUIで表示できるRealityViewを使う。
まずはStarfieldのテクスチャリソースを読み込んでマテリアルを作成する。Skyboxとなる球体のEntityも作成して、マテリアルを付与する。そしてRealityViewのcontentに追加することでSkyboxを表示できる。 ※entity.scaleをマイナスにしているのは、テクスチャを内側にするため
同様にMoonのモデルを読み込んで、scaleとpositionを設定して、RealityViewのcontentに追加して、全体をZStackで囲めば、宇宙空間に浮かぶ月を表示できる。
struct ImmersiveView: View {
var body: some View {
ZStack {
RealityView { content in
guard let resource = try? TextureResource.load(named: "Starfield") else {
fatalError("Unable to load texture.")
}
var material = UnlitMaterial()
material.color = .init(texture: .init(resource))
let entity = Entity()
entity.components.set(ModelComponent(
mesh: .generateSphere(radius: 1000),
materials: [material]
))
entity.scale *= SIMD3(repeating: -1)
content.add(entity)
}
RealityView { content in
guard let model = try? await ModelEntity(named: "Moon", in: worldAssetsBundle) else {
fatalError("Unable to load model.")
}
model.scale = SIMD3(repeating: 1)
model.position = SIMD3(x: 0, y: 0, z: 0)
content.add(model)
}
}
}
}
参考
この記事が気に入ったらサポートをしてみませんか?