realitykit について
RealityKit と ARKit を使った iOS 向け拡張現実体験の開発を支援する Swift スキルです。シーン設定、エンティティ配置、操作方法に加え、デバイス対応確認やパフォーマンス上の注意点を扱います。
主な機能
- 01RealityView のシーン設定
- 02エンティティの作成と配置
- 03アンカーと面へのレイキャスト
- 04ジェスチャーによるエンティティ操作
- 05シーン更新とプラットフォームの注意点
活用シーン
- 01iOS アプリの AR シーン構築
- 02検出した面へのモデル配置
- 033D エンティティへのタッチ操作追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill realitykitインストール後、Claude Code で/realitykitを入力して読み込まれたか確認します。
同梱ファイルevals/evals.jsonreferences/realitykit-patterns.md
詳細
RealityKit
レンダリングにはRealityKit、ワールドトラッキングにはARKitを使用して、iOS向けのAR体験を構築します。RealityView、エンティティ管理、レイキャスティング、シーン理解、ジェスチャーベースの操作を扱います。対象はSwift 6.3/iOS 26以降です。
目次
セットアップ
プロジェクトの設定
- Info.plistに
NSCameraUsageDescriptionを追加します - iOSでは、
RealityViewCameraContentはデフォルトでARカメラビューを表示します(iOS 18以降、macOS 15以降)。ARを使用しない場合のフォールバックを明示するには、.virtualカメラモードを使用します - 基本的なAR機能にエンタイトルメントは不要です。ARがアプリの中核機能である場合は
arkit必須デバイス機能を追加します。それ以外の場合は、isSupportedでAR UIを制御します。
デバイス要件
AR UIを表示する前に、対象のAR構成のisSupported値を確認してください。
import ARKit
guard ARWorldTrackingConfiguration.isSupported else {
showUnsupportedDeviceMessage()
return
}
主な型
| 型 | プラットフォーム | 役割 |
|---|---|---|
RealityView | iOS 18以降、visionOS 1以降 | RealityKitコンテンツを表示するSwiftUIビュー |
RealityViewCameraContent | iOS 18以降、macOS 15以降 | iOSではARカメラビュー、macOSではARを使用せずに表示されるコンテンツ |
Entity | すべて | すべてのシーンオブジェクトの基底クラス |
ModelEntity | すべて | 表示可能な3Dモデルを持つエンティティ |
AnchorEntity | すべて | エンティティを現実世界のアンカーに固定するもの |
RealityViewの基本
RealityViewは、RealityKitを使用するためのSwiftUIのエントリーポイントです。
RealityViewCameraContentはiOS/macOS向けのコンテンツ型です。iOSではデフォルトでARカメラビューを使用し、要求された場合やAR/カメラへのアクセスが利用できない場合は、ARを使用しないモードとしてcontent.camera = .virtualを指定できます。
import ARKit
import SwiftUI
import RealityKit
struct ARExperienceView: View {
var body: some View {
RealityView { (content: RealityViewCameraContent) in
if !ARWorldTrackingConfiguration.isSupported {
content.camera = .virtual
}
let sphere = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(
color: .blue,
isMetallic: true
)]
)
sphere.position = [0, 0, -0.5] // 50cm in front of camera
content.add(sphere)
}
}
}
MakeとUpdateのパターン
SwiftUIの状態変化に対応するには、updateクロージャーを使用します。
struct PlacementView: View {
@State private var modelColor: UIColor = .red
var body: some View {
RealityView { content in
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(
color: .red,
isMetallic: false
)]
)
box.name = "colorBox"
box.position = [0, 0, -0.5]
content.add(box)
} update: { content in
if let box = content.entities.first(
where: { $0.name == "colorBox" }
) as? ModelEntity {
box.model?.materials = [SimpleMaterial(
color: modelColor,
isMetallic: false
)]
}
}
Button("Change Color") {
modelColor = modelColor == .red ? .green : .red
}
}
}
エンティティの読み込みと作成
USDZファイルからの読み込み
メインスレッドをブロックしないよう、3Dモデルは非同期で読み込みます。
RealityView { content in
if let robot = try? await ModelEntity(named: "robot") {
robot.position = [0, -0.2, -0.8]
robot.scale = [0.01, 0.01, 0.01]
content.add(robot)
}
}
コンポーネントの追加
エンティティはECS(Entity Component System)アーキテクチャを使用します。エンティティに動作を追加するには、コンポーネントを追加します。
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .red, isMetallic: false)]
)
// Make it respond to physics
box.components.set(PhysicsBodyComponent(
massProperties: .default,
material: .default,
mode: .dynamic
))
// Add collision shape for interaction
box.components.set(CollisionComponent(
shapes: [.generateBox(size: [0.1, 0.1, 0.1])]
))
// Enable input targeting for gestures
box.components.set(InputTargetComponent())
アンカリングと配置
AnchorEntity
検出したサーフェスやワールド座標にコンテンツを固定するには、AnchorEntityを使用します。
RealityView { content in
// Anchor to a horizontal surface
let floorAnchor = AnchorEntity(.plane(
.horizontal,
classification: .floor,
minimumBounds: [0.2, 0.2]
))
let model = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .orange, isMetallic: false)]
)
floorAnchor.addChild(model)
content.add(floorAnchor)
}
アンカーの対象
| 対象 | 説明 |
|---|---|
.plane(.horizontal, ...) | 水平面(床、テーブル) |
.plane(.vertical, ...) | 垂直面(壁) |
.plane(.any, ...) | 検出した任意の平面 |
.world(transform:) | ワールド座標空間内の固定位置 |
レイキャスティング
RealityKitのシーンクエリと、ARKitによる現実世界へのレイキャストは分けて扱います。
RealityViewCameraContent.ray(through:in:to:)は、RealityKitの座標空間におけるカメラレイを返します。画面上の点を仮想シーンに投影するものであり、物理サーフェスが検出されたことを示すものではありません。RealityViewCameraContent.hitTest(point:in:query:mask:)は、CollisionComponentの形状によってヒット可能になった仮想エンティティに対してヒットテストを行います。これらの形状は、エンティティの選択や対象を指定したジェスチャーに使用します。ARKitの平面検出には使用しません。- 検出した平面への簡単な配置には、
AnchorEntity(.plane(...))を使用します。 - 現実世界のサーフェスとの単発の交差判定が必要な場合は、ARKitの
ARRaycastQueryとARSession.raycast(_:)を使用し、その後AnchorEntity(raycastResult:)でアンカーを設定します。
let results = session.raycast(query)
if let result = results.first {
let anchor = AnchorEntity(raycastResult: result)
anchor.addChild(model)
content.add(anchor)
}
エンティティのヒットテストを、ARKitのサーフェスレイキャストの代わりとして扱わないでください。
ジェスチャーと操作
ジェスチャーでエンティティを操作するには、ヒット対象の形状にCollisionComponentを、入力対象の指定にInputTargetComponentを追加します。
エンティティのドラッグジェスチャー
struct DraggableARView: View {
var body: some View {
RealityView { content in
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .blue, isMetallic: true)]
)
box.position = [0, 0, -0.5]
box.components.set(CollisionComponent(
shapes: [.generateBox(size: [0.1, 0.1, 0.1])]
))
box.components.set(InputTargetComponent())
box.name = "draggable"
content.add(box)
}
.gesture(
DragGesture()
.targetedToAnyEntity()
.onChanged { value in
let entity = value.entity
guard let parent = entity.parent else { return }
entity.position = value.convert(
value.location3D,
from: .local,
to: parent
)
}
)
}
}
シーン理解
フレームごとの更新
SwiftUI のタイマーで RealityKit を動かすのではなく、継続的なシーン処理には SceneEvents.Update をサブスクライブします。サブスクリプションを維持し、event.deltaTime を使用してください。詳しくは[エンティティアニメーション](references/realitykit-patterns.md#entity-animations)を参照してください。
プラットフォームの境界
visionOS では、ARKit に ARKitSession、WorldTrackingProvider、PlaneDetectionProvider という異なる API が用意されています。これらの visionOS 固有の型は iOS では利用できません。iOS では、RealityKit が RealityViewCameraContent を通じてワールドトラッキングを自動的に処理します。
iOS のアーキテクチャや移行に関する注意点として、ARWorldTrackingConfiguration.isSupported で AR の利用可否を確認し、RealityViewCameraContent でコンテンツを表示し、AnchorEntity に配置した Entity/ModelEntity からシーンを構築してください。
引き継ぎ先: RealityKit のインタラクションには CollisionComponent と InputTargetComponent を使用し、エンティティのアクセシビリティメタデータには AccessibilityComponent を使用します。詳細な SwiftUI ジェスチャと VoiceOver/スイッチコントロールのポリシーについては、関連セクションを参照してください。
既存の SCNView/SCNNode による実装は、独立した SceneKit の経路として扱うか、明示的に RealityKit へ移行してください。シーングラフを混在させないでください。
よくある間違い
やってはいけないこと:AR の利用可否を確認しない
AR を表示する前に、「セットアップ」に記載された構成固有の利用可否チェックを使用してください。チェックに失敗した場合は、AR 以外のコンテンツまたは利用不可であることを明示する状態を表示してください。
やってはいけないこと:重いモデルを同期的に読み込む
大きな USDZ ファイルをメインスレッドで読み込むと、フレーム落ちやハングが発生します。RealityView の make クロージャは async なので、これを使用してください。
// WRONG -- synchronous load blocks the main thread
RealityView { content in
let model = try! Entity.load(named: "large-scene")
content.add(model)
}
// CORRECT -- async load
RealityView { content in
if let model = try? await ModelEntity(named: "large-scene") {
content.add(model)
}
}
やってはいけないこと:インタラクティブなエンティティに衝突コンポーネントと入力ターゲットコンポーネントを設定し忘れる
インタラクティブなエンティティには、[ジェスチャとインタラクション](#gestures-and-interaction)で示す両方のコンポーネントが必要です。設定しないと、タップやドラッグがそのまま通過します。
やってはいけないこと:更新クロージャ内で新しいエンティティを作成する
update クロージャは、SwiftUI の状態が変化するたびに実行されます。ここでエンティティを作成すると、レンダリングのたびにコンテンツが重複します。
// WRONG -- duplicates entities on every state change
RealityView { content in
// empty
} update: { content in
let sphere = ModelEntity(mesh: .generateSphere(radius: 0.05))
content.add(sphere) // Added again on every update
}
// CORRECT -- create in make, modify in update
RealityView { content in
let sphere = ModelEntity(mesh: .generateSphere(radius: 0.05))
sphere.name = "mySphere"
content.add(sphere)
} update: { content in
if let sphere = content.entities.first(
where: { $0.name == "mySphere" }
) as? ModelEntity {
// Modify existing entity
sphere.position.y = newYPosition
}
}
やってはいけないこと:カメラの権限を無視する
iOS の RealityKit ではカメラへのアクセスが必要です。ユーザーが権限を拒否すると、説明がないままビューが黒い画面になります。
// WRONG -- no permission handling
RealityView { content in
// Black screen if camera denied
}
// CORRECT -- check and request permission
struct ARContainerView: View {
@State private var cameraAuthorized = false
var body: some View {
Group {
if cameraAuthorized {
RealityView { content in
// AR content
}
} else {
ContentUnavailableView(
"Camera Access Required",
systemImage: "camera.fill",
description: Text("Enable camera in Settings to use AR.")
)
}
}
.task {
let status = AVCaptureDevice.authorizationStatus(for: .video)
if status == .authorized {
cameraAuthorized = true
} else if status == .notDetermined {
cameraAuthorized = await AVCaptureDevice
.requestAccess(for: .video)
}
}
}
}
レビューチェックリスト
- Info.plist に
NSCameraUsageDescriptionを設定している - AR ビューを表示する前に、デバイスが AR に対応しているか確認している
- カメラの権限を要求し、拒否された場合は代替 UI で対応している
- AR がアプリの主な目的である場合、
arkitの必須デバイス機能を追加している -
makeクロージャ内で 3D モデルを非同期に読み込んでいる -
makeでエンティティを作成し、updateで変更している(updateでは作成していない) - インタラクションの前提条件と関連セクションへの引き継ぎが、「ジェスチャ」セクションに従っている
- エンティティのヒットテスト、カメラレイ、ARKit のサーフェスレイキャストが、レイキャストの使い分けに従っている
- フレームごとの処理に
SceneEvents.Updateのサブスクリプションを使用している(SwiftUI のタイマーは使用していない) - 大きなシーンでは
Entity.load(named:)ではなく、ModelEntity(named:)による非同期読み込みを使用している - アンカーエンティティが用途に適したサーフェスタイプを対象にしている
-
updateクロージャで検索できるようにエンティティ名を設定している
参考資料
- 物理演算、アニメーション、ライティング、ECS、アクセシビリティ、パフォーマンスのパターンについては、references/realitykit-patterns.md をお読みください。
- RealityKit フレームワーク
- RealityView
- RealityViewCameraContent
- RealityViewCamera
- Entity
- ModelEntity
- AnchorEntity
- ARKit フレームワーク
- iOS での ARKit
- デバイスのサポートとユーザー許可の確認
- ARWorldTrackingConfiguration
- ARRaycastQuery
- ARSession.raycast(_:)
- ファイルからエンティティを読み込む