初心者のためのVision Proアプリ構築チュートリアル

初心者のためのVision Proアプリ構築チュートリアル
vale

概要

このチュートリアルでは、VisionOS で何ができるかを駆け足で紹介します。 Swift や iOS の開発経験がほとんどない方を想定し、Vision Pro 開発ならではのポイントを共有します。

以下のトピックについて紹介します。

  • 環境構築
  • VisionOS プロジェクトの作成方法
  • SwiftUI、Immersive Spaceの使用方法
  • 3Dモデルのインポート方法
  • Reality Composer Proの使用方法
  • チュートリアルクリア後のネクストアクション

このチュートリアルをクリアすると、このようなVision Pro向けのミニアプリが作成できます。

さぁ!始めましょう!!!

環境構築

2024 年 2 月の時点では、最新のXcodeでもVision Pro向けアプリの開発に対応しました!! アプリを公開する際には料金を支払う必要がありますが、開発だけなら、開発者アカウントも不要です!

スクリーンショット2024-02-1816.00.18.png

プロジェクトの作成

Xcodeを起動したら、Create New Projectを選択してください。 スクリーンショット2024-02-1816.02.52.png

その後、テンプレートの中からvisionOSのAppを選択してください。

スクリーンショット2024-02-1816.03.02.png

次のページで、プロジェクトの名前を決めます。私の場合は、MyFirstAppとしました。(⚠︎Appを名前につけるのはオススメしません。アップアップしてるのがバレます。) このチュートリアルでは各所オプションは以下のように設定してください。

  • Initial Scene:Window
  • Immersive Space Renderer:RealityKit
  • Immersive Space:Mixed

※各オプションの意味については、記事内で解説します。

スクリーンショット2024-02-1814.23.48.png

コードを理解しよう!

プロジェクトが作成されると、以下のように表示されるはずです。

スクリーンショット2024-02-1814.26.29.png

うわ、、、めちゃくちゃコードとファイルあるじゃん、、とビビらないでください。各ファイルが何をするのものなのかを見て、色々と試してみましょう。意外とわかりやすいんです。

実際に、注意が必要なコード、ファイルは以下の4つのみです。

  • あなたの決めたプロジェクト名App.swift または、MyFirstAppApp.swift
  • ContentView.swift ※
  • ImmersiveView.swift ※
  • Pakages/RealityKitContent

※これらのコードは、この記事にコピーしています。テンプレートの内容が変更されていた場合はこのコピーを参考にしてください。

Pakages/RealityKitContentは後ほどReality Composer Proで開いて確認します。

この状態で、左上の実行ボタンを押すと、シュミレーターでビルドされます。最初に、2Dのパネルが表示されShow ImmersiveSpaceのトグルを動かすと部屋の中に2つの球体が表示されます。

スクリーンショット2024-02-1814.43.36.png

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()

これらは、このビューを定義しています。

スクリーンショット2024-02-1814.45.36.png

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になります。

スクリーンショット2024-02-1815.03.32.png

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 ゲームを作成したりすることもできます。

スクリーンショット2024-02-1819.07.59.png ※ 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 をクリックしてください。

スクリーンショット2024-02-1819.26.12.png

Reality Composer Proが起動し以下のような画面が確認できるはずです。

スクリーンショット2024-02-1819.26.29.png

Unity または Blender を使用したことがある方には、非常に見慣れた画面かもしれません。 キーボードの WSAD を使用して移動することができ、Q と E を使用して上下に移動できます。

以下のプロジェクト ブラウザを見ると、 Immersive.usda と Scene.usda という2 つの見慣れた .usda ファイルが表示されます。これらは、ContentView() でモデルをロードし、ImmersiveView() でシーンをロードするときに使用した名前です。

  • Entity(named: "Immersive", in: realityKitContentBundle)
  • Model3D(named: "Scene", bundle: realityKitContentBundle)

スクリーンショット2024-02-1819.37.21.png

それでは、遊んでみましょう。Scene.usdaをダブルクリックし、右上の + ボタンをクリックすると、使用できる 3D モデルが選択できます。それをダブルクリックもしくは、シーンにドラッグ アンド ドロップします。

スクリーンショット2024-02-1819.44.09.png

すると、左側のSceneファイルのリスト内に既存の球体モデル( GridMaterial )と同列に3Dモデルが配置されます。 スクリーンショット2024-02-1819.44.29.png

もう一方を一度削除すると、おもちゃの飛行機がルート内の唯一のモデルになります。この状態で、保存するとプロジェクト ブラウザのアイコンがおもちゃの飛行機に変わります。 スクリーンショット2024-02-1819.44.58.png

保存されたことが確認できた後、Xcode に戻ってアプリを再構築します。これでおもちゃの飛行機が見えてきます!

Immersive.usda も同様に開いて遊んでみてください。 スクリーンショット2024-02-1819.47.33.png

Immasiveファイルを指定し、3Dオブジェクトを追加。

スクリーンショット2024-02-1819.48.00.png

この時、右のパネルで各モデルの位置を調整できます。

スクリーンショット2024-02-1820.03.13.png

それぞれ配置。

スクリーンショット2024-02-1819.49.45.png

できました。こんな感じです。

スクリーンショット2024-02-1819.52.30.png

まとめ

ここまで読んでいただきありがとうございました!作るだけならサクサクできるのですが、今回のチュートリアル記事作成には結構時間がかかってしまいました。。。

日本語でも多くの方が、記事を作成してくださっていますので、是非検索してトライしてみてください。 また、さらに詳しく知りたい場合は、WWDC トークをチェックし、SwiftUI と ARKit をカバーする iOS チュートリアルをいくつか見ることをオススメします。

個人的には、VisionOS プラットフォームに非常に可能性を感じて興奮しています。 ついに電脳コイルの時代が来たか!!!!と。笑

今回のような初心者向けチュートリアルに興味がある場合は、是非私のX等々にリプやDMください!! 今後、以下の分野に関するチュートリアルを作成する予定です。

  • SwiftUI + VisionOS
  • RealityKit + VisionOS
  • ARKit + VisionOS

しかし、このチュートリアルを書くのにかかった時間を考えると…、悩ましいです。。。


Contact

まず、話を聞かせてください。

何から手を付けるか決まっていない段階で構いません。

お問い合わせ→