初心者のためのVision Proアプリ構築チュートリアル
概要
このチュートリアルでは、VisionOS で何ができるかを駆け足で紹介します。 Swift や iOS の開発経験がほとんどない方を想定し、Vision Pro 開発ならではのポイントを共有します。
以下のトピックについて紹介します。
- 環境構築
- VisionOS プロジェクトの作成方法
- SwiftUI、Immersive Spaceの使用方法
- 3Dモデルのインポート方法
- Reality Composer Proの使用方法
- チュートリアルクリア後のネクストアクション
このチュートリアルをクリアすると、このようなVision Pro向けのミニアプリが作成できます。
さぁ!始めましょう!!!
環境構築
2024 年 2 月の時点では、最新のXcodeでもVision Pro向けアプリの開発に対応しました!! アプリを公開する際には料金を支払う必要がありますが、開発だけなら、開発者アカウントも不要です!
プロジェクトの作成
Xcodeを起動したら、Create New Projectを選択してください。
その後、テンプレートの中からvisionOSのAppを選択してください。
次のページで、プロジェクトの名前を決めます。私の場合は、MyFirstApp
としました。(⚠︎Appを名前につけるのはオススメしません。アップアップしてるのがバレます。)
このチュートリアルでは各所オプションは以下のように設定してください。
- Initial Scene:Window
- Immersive Space Renderer:RealityKit
- Immersive Space:Mixed
※各オプションの意味については、記事内で解説します。
コードを理解しよう!
プロジェクトが作成されると、以下のように表示されるはずです。
うわ、、、めちゃくちゃコードとファイルあるじゃん、、とビビらないでください。各ファイルが何をするのものなのかを見て、色々と試してみましょう。意外とわかりやすいんです。
実際に、注意が必要なコード、ファイルは以下の4つのみです。
- あなたの決めたプロジェクト名App.swift または、MyFirstAppApp.swift
- ContentView.swift ※
- ImmersiveView.swift ※
- Pakages/RealityKitContent
※これらのコードは、この記事にコピーしています。テンプレートの内容が変更されていた場合はこのコピーを参考にしてください。
Pakages/RealityKitContentは後ほどReality Composer Proで開いて確認します。
この状態で、左上の実行ボタンを押すと、シュミレーターでビルドされます。最初に、2Dのパネルが表示されShow ImmersiveSpaceのトグルを動かすと部屋の中に2つの球体が表示されます。
ContentView() とは?
ContentView.swift は、最初に表示される2Dのウィンドウに関して記述されたファイルです。 SwiftUIという UI フレームワークを使用した Swift ファイルです。
これまでに SwiftUI コードを読んだことがない場合、一見難しく思えるかもしれません。ですが、一度説明を受けると、実際には非常に簡単に読むことができます!
以下のコードをコピーしました。
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 ImmersiveSpace", isOn: $showImmersiveSpace)
.font(.title)
.frame(width: 360)
.padding(24)
.glassBackgroundEffect()
}
.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
}
}
}
}
}
このコードの意味を紐解いてみましょう。注意する必要があるのは、これらの 3 行だけです。
Model3D(named: "Scene", bundle: realityKitContentBundle)
.padding(.bottom, 50)
Text("Hello, world!")
.offset(z: 100)
Toggle("Show ImmersiveSpace", isOn: $showImmersiveSpace)
.font(.title)
.frame(width: 360)
.padding(24)
.glassBackgroundEffect()
これらは、このビューを定義しています。
Model3D() とは?
Model3D(named: "Scene", bundle: realityKitContentBundle)
これは、Vision VisionOSのための新しいSwiftUIの機能です。基本的な機能としては、2DのSwiftUIビュー内に3Dモデルを埋め込むことができます。このコードでは、realityKitContentバンドルの中のSceneというid(ファイル名)を持った3Dモデルをインポートしています。
新しいText() の機能 ?
Text("Hello, world!")
これはきっと」どこかで見たことがあるでしょう!そう!テキストです!
VisionOS向けアプリ開発の新しい点の1つは、テキストなどの SwiftUI コンポーネントに奥行きを追加できることです。これにより、多くのクールなインタラクションを作成できるようになります。たとえば、ユーザーがボタンを見ると、ボタンが少し浮き上がるとか!
Z 軸 (奥行き) にオフセットを追加するだけの場合、その仕組みは以下のとおりです。
Text("Hello, world!")
.offset(z: 100)
※Windowの平面城が0になります。
WindowからImmersive Spaceへの移行
Toggle("Show ImmersiveSpace", isOn: $showImmersiveSpace)
最後の行は、ユーザーによって切り替が可能なトグルを作成しています。ユーザーが変数を切り替えると、 showImmersiveSpace 変数に書き込まれます。ただし、この行は変数を書き込むだけであり、実際のWindowからImmersive Spaceへの移行への移行処理は .onChange の内部で行われます。
.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
}
}
}
View の .onChange 修飾子は showImmersiveSpace が変わるたびに呼び出されます。TrueかFalseかを確認し、新しVisionOSの関数である openImmersiveSpace もしくは dismissImmersiveSpace を呼び出してimmersive spaceを閉じるか開くかをの処理を行います。
一般に、.onChange を使用すると、SwiftUI ビュー内で @State としてマークされた変数の変更を監視できます。
それでは、immersive space がどのように作成されるのかを見てみましょう!
ImmersiveView() とは?
ImmersiveView は ContentViewと似たSwiftUI viewですが、1つの大きな違いがあります。それは、3DコンテンツをレンダリングするためにRealityKitというゲームエンジンを利用する点です。
以下にコードをコピーしました。一緒に見てみましょう!
import SwiftUI
import RealityKit
import RealityKitContent
struct ImmersiveView: View {
var body: some View {
RealityView { content in
// Add the initial RealityKit content
if let scene = try? await Entity(named: "Immersive", in: realityKitContentBundle) {
content.add(scene)
}
}
}
}
ここでの VisionOS の主な新機能はRealityViewです。このビューを使用すると、物理システム、シーンレンダリング、ECS、アニメーションなどを含む、SwiftUI ビュー内で RealityKit の能力を最大限に活用できます。要望があれば、RealityKit + VisionOS に関するチュートリアルを書くかも。。?
それでは、RealityView の中身を見てみましょう!
sceneをロードする方法
Entity(named: "Immersive", in: realityKitContentBundle)
この例では、 realityKitContentBundle から* Immersive*という名前のエンティティを読み込みます。エンティティとは、ゲームの中の単なるオブジェクトで、モデルやモデルのコレクションであることがあります。 (これは、Unity の GameObject や Unreal Engine の Actor と似ています。)
この例は、 ContentView() 内で使用した Model3D での呼び出しと同じ処理になります。主な違いは、 Model3D では RealityKit を使わずに動作するため、RealityKitの全機能にアクセスできない点です。
if let scene = try? await Entity(named: "Immersive", in: realityKitContentBundle) {.
ここでは、そのエンティティが実際に realityKitContentBundle の中にあるかどうかを確かめるために if let を使います。これは基本的に、モデルが正しくロードされている場合はそれを scene という変数に割り当て、そうでない場合は else でコンテンツを実行することを示しています。
また、エンティティのロードは非同期であるため、ここでは try? await も用いられています。
content.add(scene)
if let で、realityKitContentBundle からエンティティが正しくロードされた場合、この新しく作成されたシーン変数が アプリ内の content に追加されます。 content は RealityView によって提供される変数です。 content に追加されたエンティティのみがレンダリングされます。
アプリがどのように起動するか?
VisionOS アプリを起動した際、 あなたの決めたプロジェクト名App.swift がアプリの初期ビューを定義するファイルになります。もし、作成した別のViewで起動したい場合は、ContentView() の部分を他のものに変更するだけでOKです。
Body
このテンプレートでは、アプリ内のBodyに ContentView() と ContentView() という2つのViewが定義されています。
これは、一度に 1 つのビューを組み込む通常の IOS 開発とは少し異なります。body内に複数のビューがある場合、最初のビューのみがユーザーに表示されます。ここで 2 つのビューを追加した理由は、起動後に ContentView() から に ImmersiveView() に移行するミニアプリだからです。なので、事前にここで両方を宣言しておいた方が簡単です。
Window View と Volume View
ここで、作成したViewの外側でラップされている WindowGroup と ImmersiveSpace が何を意味するのかをみてみましょう。
Vision OSには以下の3種類のViewがあります。
- Windows: 3Dモデルを埋め込むことが可能な2Dパネル。
- Volumes: RealityKitやUnityを使用して作成した3DコンテンツなどのSceneを表示できる領域。中に収めるシーンがボリュームよりも大きい場合は、ボリューム内に入りきった部分のみが表示されます。
- Spaces:制限のない領域。環境全体を制御したり、完全な VR ゲームを作成したりすることもできます。
※ Apple公式ページより
この3 つのタイプの感覚を掴むと、今回のコード達の理解が更に容易になるはずです。
単なる 2D パネルである ContentView() は、これが 2D であることを示すために WindowGroup によってラップされています。表示される 2 つの球体を表す ImmersiveView() については、2 つの球体を表示するために無制限のスペースが必要なので、ImmersiveSpace でラップされています。
OK!!! ここまで来れば全てのコードが理解できたと思います!早速遊んでみましょう!
Reality Composer Pro
ここまで、私たちが見ているのは SwiftUI Viewだけです。多くのViewは、 realityKitContentBundle と呼ばれるバンドルから 3D Sceneをロードします。そのバンドルの中身を見てみましょう!
Packagesフォルダの配下にあるRealityKitContent/Packagesファイルを開いて、右上のOpen in Reality Composer Pro をクリックしてください。
Reality Composer Proが起動し以下のような画面が確認できるはずです。
Unity または Blender を使用したことがある方には、非常に見慣れた画面かもしれません。 キーボードの WSAD を使用して移動することができ、Q と E を使用して上下に移動できます。
以下のプロジェクト ブラウザを見ると、 Immersive.usda と Scene.usda という2 つの見慣れた .usda ファイルが表示されます。これらは、ContentView() でモデルをロードし、ImmersiveView() でシーンをロードするときに使用した名前です。
Entity(named: "Immersive", in: realityKitContentBundle)
Model3D(named: "Scene", bundle: realityKitContentBundle)
それでは、遊んでみましょう。Scene.usdaをダブルクリックし、右上の + ボタンをクリックすると、使用できる 3D モデルが選択できます。それをダブルクリックもしくは、シーンにドラッグ アンド ドロップします。
すると、左側のSceneファイルのリスト内に既存の球体モデル( GridMaterial )と同列に3Dモデルが配置されます。
もう一方を一度削除すると、おもちゃの飛行機がルート内の唯一のモデルになります。この状態で、保存するとプロジェクト ブラウザのアイコンがおもちゃの飛行機に変わります。
保存されたことが確認できた後、Xcode に戻ってアプリを再構築します。これでおもちゃの飛行機が見えてきます!
Immersive.usda も同様に開いて遊んでみてください。
Immasiveファイルを指定し、3Dオブジェクトを追加。
この時、右のパネルで各モデルの位置を調整できます。
それぞれ配置。
できました。こんな感じです。
まとめ
ここまで読んでいただきありがとうございました!作るだけならサクサクできるのですが、今回のチュートリアル記事作成には結構時間がかかってしまいました。。。
日本語でも多くの方が、記事を作成してくださっていますので、是非検索してトライしてみてください。
また、さらに詳しく知りたい場合は、WWDC トークをチェックし、SwiftUI と ARKit をカバーする iOS チュートリアルをいくつか見ることをオススメします。
個人的には、VisionOS プラットフォームに非常に可能性を感じて興奮しています。 ついに電脳コイルの時代が来たか!!!!と。笑
今回のような初心者向けチュートリアルに興味がある場合は、是非私のX等々にリプやDMください!!
今後、以下の分野に関するチュートリアルを作成する予定です。
- SwiftUI + VisionOS
- RealityKit + VisionOS
- ARKit + VisionOS
しかし、このチュートリアルを書くのにかかった時間を考えると…、悩ましいです。。。
【monoDuki合同会社について】
monoDuki合同会社は、鹿児島の「モノ好き」達が世の中をもっと面白くしようと、「XR事業」と「スタートアップ支援事業」の2つに注力し、世の中に対して新たな可能性を提供します。