WWDC Quick Look 💓 By SwiftGGTeam
Get started with building apps for spatial computing

Get started with building apps for spatial computing

元の動画を見る

ハイライト

visionOS の空間コンピューティングプラットフォームは、SwiftUI、RealityKit、ARKit というおなじみの 3 つのフレームワークを基盤にしています。Window、Volume、Full Space という 3 種類のコンテナにより、開発者は同じコードベースで 2D ウィンドウから完全没入型体験まで構築できます。

主要内容

3 種類の空間コンテナ

visionOS の中核設計は 3 種類のコンテナを中心にしています(01:27):

Window(ウィンドウ)

Window は SwiftUI の Scene で、サイズ変更や再レイアウトができ、macOS のウィンドウに近い挙動をします。一方で 2D と 3D コンテンツの混在に対応しており、ひとつのウィンドウ内にボタンと 3D モデルを同時に配置できます。

Volume(ボリューム)

Volume は Shared Space で 3D コンテンツを表示する固定境界付きコンテナです(02:07)。盤面や地球儀のように、さまざまな角度から見られる 3D コンテンツに適しています。Volume も SwiftUI Scene であり、3D の描画には RealityKit を使います。

Full Space(全空間)

Full Space はアプリが視野全体を占有し、ほかのアプリを非表示にします(02:47)。Full Space では、Skeletal Hand Tracking などの ARKit API を利用できます。Full Space には 3 種類の没入スタイルがあります:

  • Mixed:現実環境(passthrough)の上に仮想コンテンツを重ねる
  • Full:現実環境を完全に隠し、仮想コンテンツだけを表示する
  • Progressive:最初は現実環境を表示し、ユーザーが Digital Crown で没入度を調整できる

自然なインタラクション

visionOS のインタラクションは目と両手を基盤にしています(04:30):

  • ボタンを見て指で軽くタップすると選択できる
  • 3D 空間内のボタンに直接手を伸ばして触れられる
  • システムは tap、long press、drag、rotate、zoom などのジェスチャをサポートする
  • SwiftUI のジェスチャ API は RealityKit Entity とシームレスに統合される

ARKit の Skeletal Hand Tracking により、ユーザーの手を仮想のクラブにしてボウリングを行うような、より複雑なジェスチャも実現できます(05:36)。

プライバシー優先のアーキテクチャ

visionOS のプライバシー設計は厳格です(07:47)。アプリはセンサーデータへ直接アクセスできず、システムが生データをイベントや視覚的なヒントに変換してからアプリへ渡します。たとえば:

  • システムはユーザーの目と手の 3D 位置を把握しているが、アプリには touch イベントだけを渡す
  • ユーザーが特定のビューを注視すると、システムは hover 効果を描画するが、ユーザーがどこを見ているかはアプリに伝えない
  • シーン理解(壁や家具の検出)や Skeletal Hand Tracking へのアクセスが必要な場合、システムは先にユーザーの許可を求める

詳細

開発ツールチェーン

Xcode は visionOS 向けに完全な開発ツールを提供します(09:05):

Xcode Preview

Preview Canvas は 3D プレビューに対応しており、コード編集中に RealityKit シーンの描画結果やアニメーション効果を直接確認できます(09:34)。Object Mode では 3D レイアウトをすばやくプレビューし、コンテンツがビュー境界内に収まっているか確認できます。

Simulator

Simulator ではキーボード、マウス、ゲームコントローラを使ってシーン内を移動し、観察できます。3 種類のシミュレーションシーンが用意され、それぞれ昼と夜のライティングがあります(11:03)。

RealityKit Trace

Instruments 15 には RealityKit Trace テンプレートが追加され、GPU、CPU、システム消費電力への影響を分析し、フレームのボトルネックを追跡し、送信された三角形の総数やシミュレーションされた RealityKit Entity 数を確認できます(12:24)。

Reality Composer Pro

新しい 3D コンテンツ編集ツールで、次に対応します:

  • パーティクルシステム(雲、雨、火花などの効果)
  • 空間オーディオのプレビュー
  • 物理ベースのマテリアル(PBR)
  • MaterialX のカスタムマテリアルノードグラフ(14:32

Window を作成する

Window は WindowGroup で作成し、2D と 3D コンテンツを混在させられます:

import SwiftUI
import RealityKit

@main
struct HelloWorldApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
            
            // ウィンドウに 3D コンテンツを埋め込む
            Model3D(named: "Satellite") { model in
                model
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            } placeholder: {
                ProgressView()
            }
        }
    }
}

キーポイント:

  • Model3DImage に似ており、RealityKit で描画される 3D モデルを読み込んで表示する
  • ウィンドウ内の 3D コンテンツは z 軸方向に浮き出し、奥行き感を加える
  • DragGesture で 3D Entity を直接ドラッグできる

Volume を作成する

Volume は windowStyle.volumetric に設定して作成します:

@main
struct EarthApp: App {
    var body: some Scene {
        WindowGroup(id: "earth") {
            EarthView()
        }
        .windowStyle(.volumetric)
        .defaultSize(width: 0.6, height: 0.6, depth: 0.6, in: .meters)
    }
}

キーポイント:

  • .windowStyle(.volumetric) は WindowGroup を Volume にする
  • defaultSize はボリュームの幅、高さ、奥行きを指定し、単位には .points または .meters を使える
  • Volume のコンテンツは境界内に収める必要がある
  • Shared Space に適しており、ほかのウィンドウと共存できる

RealityView 和 Attachments

RealityView は SwiftUI 内で RealityKit Entity を管理する中核ビューです:

import SwiftUI
import RealityKit

struct GlobeView: View {
    var body: some View {
        RealityView { content, attachments in
            // make closure: Entity を作成してルート Entity に追加
            if let earth = try? await Entity(named: "Earth") {
                content.add(earth)
            }
            
            // attachment をシーンに追加
            if let pin = attachments.entity(for: "pin") {
                pin.position = [0, 0.5, 0]
                content.add(pin)
            }
        } update: { content, attachments in
            // update closure: 状態が変化したときに呼び出される
        } attachments: {
            // attachments closure: SwiftUI ビューを定義
            Attachment(id: "pin") {
                Image(systemName: "mappin")
                    .font(.largeTitle)
                    .foregroundColor(.red)
            }
        }
    }
}

キーポイント:

  • make closure はビュー作成時に一度実行され、Entity の初期化に使う
  • update closure は SwiftUI の状態が変化したときに呼び出される
  • attachments は SwiftUI ビューを 3D シーンに配置できる Entity へ変換する
  • 各 attachment には一意の id が必要

Full Space を作成する

Full Space は ImmersiveSpace Scene 型で作成します:

@main
struct SpaceApp: App {
    @State private var immersiveStyle: ImmersionStyle = .full
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        
        ImmersiveSpace(id: "outerSpace") {
            OuterSpaceView()
        }
        .immersionStyle(selection: $immersiveStyle, in: .full, .mixed, .progressive)
    }
}

struct ContentView: View {
    @Environment(\.openImmersiveSpace) var openImmersiveSpace
    @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
    
    var body: some View {
        Button("Enter Space") {
            Task {
                await openImmersiveSpace(id: "outerSpace")
            }
        }
    }
}

キーポイント:

  • ImmersiveSpace は入室可能な Full Space シーンを定義する
  • .immersionStyle は没入スタイルを設定し、デフォルトは .mixed
  • openImmersiveSpace 環境値は空間を開くために使う
  • dismissImmersiveSpace 環境値は空間を閉じるために使う
  • 没入体験に入るかどうかをユーザーが選べるボタンを用意するのがおすすめ

ジェスチャと 3D Entity のインタラクション

SwiftUI のジェスチャは RealityKit Entity を直接ターゲットにできます:

Model3D(named: "Satellite") { model in
    model
        .resizable()
        .aspectRatio(contentMode: .fit)
}
.placeholder {
    ProgressView()
}
.gesture(
    DragGesture()
        .targetedToEntity(named: "Satellite")
        .onChanged { value in
            // ドラッグ値に基づいて Entity を移動
            value.entity.position = value.convert(value.location3D, from: .local, to: .parent)
        }
)

キーポイント:

  • .targetedToEntity(named:) はジェスチャを特定の Entity に結び付ける
  • value.location3D は 3D 空間内のドラッグ位置を取得する
  • convert(_:from:to:) は座標空間間で位置を変換する

既存 App から移行する

visionOS は 3 種類の移行パスをサポートします(15:40):

  1. 互換モード:iPad/iPhone App は変更なしで動作し、システムがウィンドウの拡大縮小や回転を自動処理する
  2. 再コンパイル:Xcode で visionOS ターゲットを追加して再コンパイルすると、ネイティブの余白、サイズ、再レイアウトが得られる
  3. 新規設計:visionOS App テンプレートを使い、Window または Volume から始めて空間コンピューティングの能力を活用する

重要ポイント

Model3D で既存 App に奥行き感を加える

  • 何をするか:既存の SwiftUI App のビューに Model3D を追加し、3D 製品モデルを表示する
  • 取り組む価値:RealityKit コードを書かなくても、Image と同じような簡単さで UI に空間的な奥行きを加えられる
  • 始め方:USDZ モデルを Xcode プロジェクトへドラッグし、Model3D(named: "モデル名") で読み込む

回転して確認できる 3D 商品表示を作る

  • 何をするか:Volume で 3D 商品モデルを表示し、ユーザーがさまざまな角度から確認できるようにする
  • 取り組む価値:Volume は 3D コンテンツ表示に向いており、ユーザーはジェスチャで回転や拡大縮小ができ、2D 画像より直感的に理解できる
  • 始め方WindowGroup を作成し、.windowStyle(.volumetric) を設定して、その中の RealityView で商品モデルを読み込む

段階的に没入する瞑想 App を設計する

  • 何をするか:App 起動時は Shared Space にコントロールパネルを表示し、開始をタップしたら Full Space に入り、Progressive 没入スタイルでユーザー自身が没入度を調整できるようにする
  • 取り組む価値:Progressive 没入はユーザーに制御権を与え、軽い没入から完全な没入へ段階的に移行できるため、不快感を減らせる
  • 始め方ImmersiveSpace でシーンを作成し、.immersionStyle(selection: $style, in: .progressive) を設定する

RealityView Attachments で 3D 地図マーカーを作る

  • 何をするか:3D 地球儀や地図上で、SwiftUI ビューをマーカー(pin)として使う
  • 取り組む価値:Attachments により、慣れた SwiftUI コードで 3D シーン内の UI 要素を作成でき、新しい UI フレームワークを学ぶ必要がない
  • 始め方RealityViewattachments closure でマーカービューを定義し、make closure で attachments.entity(for: "id") を取得して配置する

関連セッション

コメント

GitHub Issues · utterances