Highlight
RealityKit 2025 では、ARKit のデータを
AnchorStateEvents経由でアプリに直接公開できるようになり、ManipulationComponent.configureEntity()のわずか 1 行で 3D オブジェクトの掴み・回転・両手間の受け渡しまで実現できます。
核となる内容
これまで、机の上に置いて手で掴むことができ、なおかつ現実の物体に正しくオクルージョンされるような小さなゲームを作ろうとすると、開発者は ARKit セッション、RealityKit シーン、独自のジェスチャー認識、物理ステートの切り替えをすべて自前で管理する必要がありました。一連の作業を終えてみると、グルーコードのほうが業務ロジックよりはるかに多い、という状態に陥りがちです。今年の RealityKit は、こうした手間をフレームワーク側に取り込んできました。
Laurence は、空間パズルゲームを題材に新機能を一通り紹介しています。机の上に固定された宝箱があり、プレイヤーは周囲のオブジェクトを掴んで鍵を探し、見つけると宝箱が開いて小さな花火が打ち上がる、というものです。このゲームで使われている機能が、ちょうど RealityKit 2025 のアップデート項目をまるごとカバーしています。SpatialTrackingSession が直接 AnchorStateEvents を発行し、ManipulationComponent が掴み操作を引き受け、SceneUnderstandingFlags によって現実の部屋のメッシュが物理シミュレーションに参加し、EnvironmentBlendingComponent で仮想オブジェクトが現実の静的な物体にオクルージョンされ、MeshInstancesComponent で GPU インスタンシングを使って装飾を効率よく描画し、ImagePresentationComponent と VideoPlayerComponent で空間写真や没入型ビデオを扱えます。さらに今年から 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.Configurationでtracking: [.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なので、用途に応じてPlaneAnchorやImageAnchorといった具体的な型へキャストします。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は内部でInputTargetComponent、CollisionComponent、HoverEffectComponent、ManipulationComponentをまとめて取り付けてくれるので、手動セットアップが不要になります。releaseBehavior = .stayを指定すると手を離した位置にオブジェクトが留まります。デフォルトでは元の位置に戻ってしまうため、パズルのようなシナリオには向きません。- 掴むタイミングに合わせて
ManipulationEventsで物理モードを切り替えるのがコツです(09:28)。掴んだ瞬間はkinematic、離した瞬間はdynamicにして重力に任せます。
Scene Understanding を物理シミュレーションに参加させる。 SpatialTrackingSession.Configuration に sceneUnderstanding: [.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.Spatial3DImage の generate() で生成し、システムの Photos アプリと同じ仕上がりになります。
VideoPlayerComponent の拡張。 spatial video のフル空間スタイル、APMP 180/360/Wide-FOV、Apple Immersive Video に対応し、快適性も自動で調整されます。
Entity を Data から直接ロード。 Entity(from: data) を使うと、ネットワークから受け取ったデータからモデルを直接構築できるようになりました(23:35)。いったんディスクに書き出してから読み直す、という手順は不要です。
主な学び
-
ARKit と RealityKit の統合をデフォルトの起点にする。新規の空間プロジェクトでは、手動で管理する ARKit セッションをやめ、
SpatialTrackingSession+AnchorStateEventsを使いましょう。なぜやる価値があるか: 並行して動くステートをひとつ減らせ、アンカーのライフサイクルが entity のライフサイクルと自然に紐付きます。どう始めるか: 既存のARSession.runをSpatialTrackingSession.runに置き換え、DidAnchor/WillUnanchor/DidFailToAnchorの 3 つのイベントを購読します。 -
自作のドラッグジェスチャーを ManipulationComponent に置き換える。なぜやる価値があるか: 両手での受け渡し、慣性、ホバー時のビジュアル効果まで全部タダで付いてきます。同じものを自前で書こうとすると、ジェスチャー処理だけで数百行になります。どう始めるか: プロジェクト内で 3D オブジェクトを扱っている
DragGesture/SpatialTapGestureを洗い出し、それぞれをManipulationComponent.configureEntity(entity, collisionShapes:)に置き換え、ManipulationEvents.WillBegin/WillEndでPhysicsBodyComponent.modeを切り替えます。 -
Scene Understanding の collision / physics フラグを有効にする。なぜやる価値があるか: 仮想オブジェクトを現実の机の上に「本当に置く」ことができ、平面検出を手動でやってから衝突判定を組むより安定します。どう始めるか:
SpatialTrackingSession.ConfigurationにsceneUnderstanding: [.collision, .physics]を追加し、地面に落ちる対象にはPhysicsBodyComponent(mode: .dynamic)を付けます。 -
MeshInstancesComponent でシーンの装飾を描画する。なぜやる価値があるか: visionOS では entity をクローンすると draw call がすぐに増えてしまいますが、インスタンシングを使えば数十〜数百インスタンスでもフレームレートが目に見えて安定します。どう始めるか: 繰り返し使っている
Entity.clone(recursive:)をMeshInstancesComponentに書き換え、LowLevelInstanceData.withMutableTransformsを使って transform 行列をまとめて書き込みます。 -
availableViewingModes を確認してから desiredViewingMode を設定する。なぜやる価値があるか: spatial photo はあらゆる viewing mode をサポートしているわけではなく、無条件にセットすると静かにフォールバックされてしまいます。どう始めるか:
ImagePresentationComponentを初期化しているコードすべてにavailableViewingModes.contains(...)のガードを入れます。
関連 Session
- Better together: SwiftUI and RealityKit — SwiftUI ビュー内での ManipulationComponent の使い方を網羅
- Bring your SceneKit project to RealityKit — SceneKit プロジェクトを RealityKit へ移行する実践的な手順
- Discover Metal 4 — Metal 4 の新機能。RealityKit でカスタムレンダリングを行う際に役立ちます
- Combine Metal 4 machine learning and graphics — グラフィックスパイプラインに機械学習を組み込む方法
- Engage players with the Apple Games app — 新しい Games アプリの導線とプレイヤー発見の仕組み
コメント
GitHub Issues · utterances