WWDC Quick Look 💓 By SwiftGGTeam
What’s new in RealityKit

What’s new in RealityKit

元の動画を見る

Highlight

RealityKit 2025 では、ARKit のデータを AnchorStateEvents 経由でアプリに直接公開できるようになり、ManipulationComponent.configureEntity() のわずか 1 行で 3D オブジェクトの掴み・回転・両手間の受け渡しまで実現できます。

核となる内容

これまで、机の上に置いて手で掴むことができ、なおかつ現実の物体に正しくオクルージョンされるような小さなゲームを作ろうとすると、開発者は ARKit セッション、RealityKit シーン、独自のジェスチャー認識、物理ステートの切り替えをすべて自前で管理する必要がありました。一連の作業を終えてみると、グルーコードのほうが業務ロジックよりはるかに多い、という状態に陥りがちです。今年の RealityKit は、こうした手間をフレームワーク側に取り込んできました。

Laurence は、空間パズルゲームを題材に新機能を一通り紹介しています。机の上に固定された宝箱があり、プレイヤーは周囲のオブジェクトを掴んで鍵を探し、見つけると宝箱が開いて小さな花火が打ち上がる、というものです。このゲームで使われている機能が、ちょうど RealityKit 2025 のアップデート項目をまるごとカバーしています。SpatialTrackingSession が直接 AnchorStateEvents を発行し、ManipulationComponent が掴み操作を引き受け、SceneUnderstandingFlags によって現実の部屋のメッシュが物理シミュレーションに参加し、EnvironmentBlendingComponent で仮想オブジェクトが現実の静的な物体にオクルージョンされ、MeshInstancesComponent で GPU インスタンシングを使って装飾を効率よく描画し、ImagePresentationComponentVideoPlayerComponent で空間写真や没入型ビデオを扱えます。さらに今年から RealityKit は tvOS にも対応し、すべての世代の Apple TV 4K で利用できるようになりました。

詳細

ARKit のデータを RealityKit にダイレクトに流す。 これまでは ARKit セッションと RealityKit シーンを並行して走らせる必要がありましたが、これからは SpatialTrackingSession でトラッキングを起動し、AnchorEntity で「どんなアンカーを探すか」を記述しておけば、ヒット時に AnchorStateEvents.DidAnchor 経由で ARKit のロウデータを直接受け取れます(04:33)。

// Set up SpatialTrackingSession
@State var spatialTrackingSession = SpatialTrackingSession()

RealityView { content in
    let configuration = SpatialTrackingSession.Configuration(
        tracking: [.plane]
    )
    if let unavailableCapabilities = await spatialTrackingSession.run(configuration) {
        // Handle errors
    }
}

ポイント:

  • SpatialTrackingSession.Configurationtracking: [.plane] を指定し、平面のみを追跡対象にしています。必要なものだけ有効にすることで消費電力を抑えられます。
  • run(configuration) は async メソッドで、戻り値の unavailableCapabilities には現在のデバイスでサポートされていない機能が入っています。ここでフォールバック処理を行います。
  • セッションは RealityView のクロージャ内で起動し、現在の immersive scene とライフサイクルを揃えます。

アンカーイベントを受け取ったら、ARKit の PlaneAnchor にキャストしてネイティブの transform を直接利用できます(05:48)。

didAnchor = content.subscribe(to: AnchorStateEvents.DidAnchor.self) { event in
    guard let anchorComponent =
        event.entity.components[ARKitAnchorComponent.self] else { return }
    guard let planeAnchor = anchorComponent.anchor as? PlaneAnchor else { return }

    let worldSpaceFromExtent =
        planeAnchor.originFromAnchorTransform *
        planeAnchor.geometry.extent.anchorFromExtentTransform

    gameRoot.transform = Transform(matrix: worldSpaceFromExtent)
    // Add game objects to gameRoot
}

ポイント:

  • event.entity.components[ARKitAnchorComponent.self] は RealityKit に新しく加わったブリッジ用のコンポーネントで、ARKit のロウアンカーを保持しています。
  • anchorComponent.anchor の型は ARAnchor なので、用途に応じて PlaneAnchorImageAnchor といった具体的な型へキャストします。
  • originFromAnchorTransform * anchorFromExtentTransform でゲームのルートノードを平面 extent の中心へ揃え、モデルが宙に浮く問題を防ぎます。

ManipulationComponent が掴み操作を引き受ける。 ManipulationComponent.configureEntity() の 1 行で、任意の entity に掴み・回転・両手での受け渡しを持たせられます(07:38)。

extension Entity {
    static func loadModelAndSetUp(modelName: String,
                                  in bundle: Bundle) async throws -> Entity {
        let entity = // Load model and assign PhysicsBodyComponent
        let shapes = // Generate convex shape that fits the entity model

        ManipulationComponent.configureEntity(entity, collisionShapes: [shapes])
        var manipulationComponent = ManipulationComponent()
        manipulationComponent.releaseBehavior = .stay
        entity.components.set(manipulationComponent)
        return entity
    }
}

ポイント:

  • configureEntity は内部で InputTargetComponentCollisionComponentHoverEffectComponentManipulationComponent をまとめて取り付けてくれるので、手動セットアップが不要になります。
  • releaseBehavior = .stay を指定すると手を離した位置にオブジェクトが留まります。デフォルトでは元の位置に戻ってしまうため、パズルのようなシナリオには向きません。
  • 掴むタイミングに合わせて ManipulationEvents で物理モードを切り替えるのがコツです(09:28)。掴んだ瞬間は kinematic、離した瞬間は dynamic にして重力に任せます。

Scene Understanding を物理シミュレーションに参加させる。 SpatialTrackingSession.ConfigurationsceneUnderstanding: [.collision, .physics] を加えるだけで(10:52)、現実の部屋のメッシュがそのまま物理ワールドに取り込まれ、落とした物体は本物の床できちんと受け止められます。

EnvironmentBlendingComponent で静的なオクルージョンを処理。 preferredBlendingMode: .occluded(by: .surroundings) を指定すると(11:56)、仮想エンティティが現実の静的な物体に正確にオクルージョンされます。一方で、人やペットなどの動的なオブジェクトはオクルージョンの対象になりません。このコンポーネントを付けたエンティティは背景レイヤーとして扱われ、他の仮想オブジェクトの後ろに常に描画されます。

MeshInstancesComponent で大量の装飾を描画する。 1 つの entity と transform 行列の配列だけで、同じモデルを数百体まとめて描画できます(14:20)。entity をクローンするやり方と比べて、メモリ使用量も draw call 数もはるかに節約できます。LowLevelInstanceData(instanceCount:) から書き込み可能な transform バッファを取得します。

ImagePresentationComponent は 3 種類の画像に対応。 通常の 2D 画像、spatial photo、そして 2D 画像から生成した spatial scene の 3 種類です。desiredViewingMode を設定する前に、必ず availableViewingModes.contains(.spatialStereo) でチェックする必要があります(17:57)。チェックを怠ると 2D にフォールバックします。spatial scene は ImagePresentationComponent.Spatial3DImagegenerate() で生成し、システムの Photos アプリと同じ仕上がりになります。

VideoPlayerComponent の拡張。 spatial video のフル空間スタイル、APMP 180/360/Wide-FOV、Apple Immersive Video に対応し、快適性も自動で調整されます。

Entity を Data から直接ロード。 Entity(from: data) を使うと、ネットワークから受け取ったデータからモデルを直接構築できるようになりました(23:35)。いったんディスクに書き出してから読み直す、という手順は不要です。

主な学び

  1. ARKit と RealityKit の統合をデフォルトの起点にする。新規の空間プロジェクトでは、手動で管理する ARKit セッションをやめ、SpatialTrackingSession + AnchorStateEvents を使いましょう。なぜやる価値があるか: 並行して動くステートをひとつ減らせ、アンカーのライフサイクルが entity のライフサイクルと自然に紐付きます。どう始めるか: 既存の ARSession.runSpatialTrackingSession.run に置き換え、DidAnchor / WillUnanchor / DidFailToAnchor の 3 つのイベントを購読します。

  2. 自作のドラッグジェスチャーを ManipulationComponent に置き換える。なぜやる価値があるか: 両手での受け渡し、慣性、ホバー時のビジュアル効果まで全部タダで付いてきます。同じものを自前で書こうとすると、ジェスチャー処理だけで数百行になります。どう始めるか: プロジェクト内で 3D オブジェクトを扱っている DragGesture / SpatialTapGesture を洗い出し、それぞれを ManipulationComponent.configureEntity(entity, collisionShapes:) に置き換え、ManipulationEvents.WillBegin/WillEndPhysicsBodyComponent.mode を切り替えます。

  3. Scene Understanding の collision / physics フラグを有効にする。なぜやる価値があるか: 仮想オブジェクトを現実の机の上に「本当に置く」ことができ、平面検出を手動でやってから衝突判定を組むより安定します。どう始めるか: SpatialTrackingSession.ConfigurationsceneUnderstanding: [.collision, .physics] を追加し、地面に落ちる対象には PhysicsBodyComponent(mode: .dynamic) を付けます。

  4. MeshInstancesComponent でシーンの装飾を描画する。なぜやる価値があるか: visionOS では entity をクローンすると draw call がすぐに増えてしまいますが、インスタンシングを使えば数十〜数百インスタンスでもフレームレートが目に見えて安定します。どう始めるか: 繰り返し使っている Entity.clone(recursive:)MeshInstancesComponent に書き換え、LowLevelInstanceData.withMutableTransforms を使って transform 行列をまとめて書き込みます。

  5. availableViewingModes を確認してから desiredViewingMode を設定する。なぜやる価値があるか: spatial photo はあらゆる viewing mode をサポートしているわけではなく、無条件にセットすると静かにフォールバックされてしまいます。どう始めるか: ImagePresentationComponent を初期化しているコードすべてに availableViewingModes.contains(...) のガードを入れます。

関連 Session

コメント

GitHub Issues · utterances