realitykit-ar について
iOS開発者向けにRealityKitとARKitの使い方を解説するスキルです。RealityViewによるARシーンの構築、3Dエンティティの配置と操作、シーン理解を扱います。
主な機能
- 01RealityViewによるARコンテンツの表示
- 023Dエンティティの読み込みと生成
- 03平面やワールド座標へのエンティティのアンカー
- 04レイキャスティングによるオブジェクト配置
- 05ジェスチャーによるエンティティの選択とドラッグ
- 06シーン更新イベントの処理
活用シーン
- 01検出した面への3Dモデル配置
- 02タップやドラッグに対応するARオブジェクトの構築
- 03シーン更新に応じた継続処理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill realitykit-arインストール後、Claude Code で/realitykit-arを入力して読み込まれたか確認します。
同梱ファイルreferences/realitykit-patterns.md
詳細
RealityKit + ARKit
RealityKit によるレンダリングと ARKit によるワールドトラッキングを使用して、iOS 向けの AR エクスペリエンスを構築します。RealityView、エンティティ管理、レイキャスティング、シーン理解、ジェスチャーベースのインタラクションについて説明します。Swift 6.2/iOS 26 以降を対象としています。
内容
- セットアップ
- RealityView の基本
- エンティティの読み込みと作成
- アンカリングと配置
- レイキャスティング
- ジェスチャーとインタラクション
- シーン理解
- よくある間違い
- 確認チェックリスト
- 参考資料
セットアップ
プロジェクトの設定
- Info.plist に
NSCameraUsageDescriptionを追加します - iOS では、RealityKit はデフォルトで
RealityViewCameraContentを介してデバイスのカメラを使用します(iOS 18 以降、macOS 15 以降) - iOS で基本的な AR を利用するために、追加の機能は必要ありません
デバイス要件
AR 機能には、A9 チップ以降を搭載したデバイスが必要です。AR UI を表示する前に、必ず対応状況を確認してください。
import ARKit
guard ARWorldTrackingConfiguration.isSupported else {
showUnsupportedDeviceMessage()
return
}
主な型
| 型 | プラットフォーム | 役割 |
|---|---|---|
RealityView | iOS 18 以降、visionOS 1 以降 | RealityKit コンテンツを表示する SwiftUI ビュー |
RealityViewCameraContent | iOS 18 以降、macOS 15 以降 | デバイスのカメラを通して表示されるコンテンツ |
Entity | すべて | すべてのシーンオブジェクトの基底クラス |
ModelEntity | すべて | 表示可能な 3D モデルを持つエンティティ |
AnchorEntity | すべて | エンティティを現実世界のアンカーに固定します |
RealityView の基本
RealityView は RealityKit の SwiftUI エントリーポイントです。iOS では RealityViewCameraContent を提供し、デバイスのカメラを通して AR をレンダリングします。
import SwiftUI
import RealityKit
struct ARExperienceView: View {
var body: some View {
RealityView { content in
// content is RealityViewCameraContent on iOS
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)
}
}
メッシュのプログラムによる生成
// Box
let box = ModelEntity(
mesh: .generateBox(size: [0.1, 0.2, 0.1], cornerRadius: 0.005),
materials: [SimpleMaterial(color: .gray, isMetallic: true)]
)
// Sphere
let sphere = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(color: .blue, roughness: 0.2, isMetallic: true)]
)
// Plane
let plane = ModelEntity(
mesh: .generatePlane(width: 0.3, depth: 0.3),
materials: [SimpleMaterial(color: .green, isMetallic: false)]
)
コンポーネントの追加
エンティティは ECS(エンティティ・コンポーネント・システム)アーキテクチャを使用します。エンティティに動作を与えるには、コンポーネントを追加します。
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:) | ワールド空間内の固定位置 |
レイキャスティング
RealityViewCameraContent を使用して、SwiftUI ビューの座標と RealityKit のワールド空間との間で変換します。SpatialTapGesture と組み合わせると、検出したサーフェス上でユーザーがタップした位置にオブジェクトを配置できます。
ジェスチャーとインタラクション
エンティティのドラッグジェスチャー
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
)
}
)
}
}
タップして選択
.gesture(
SpatialTapGesture()
.targetedToAnyEntity()
.onEnded { value in
let tappedEntity = value.entity
highlightEntity(tappedEntity)
}
)
シーン理解
フレームごとの更新
シーン更新イベントを購読して、継続的に処理します。
RealityView { content in
let entity = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(color: .yellow, isMetallic: false)]
)
entity.position = [0, 0, -0.5]
content.add(entity)
_ = content.subscribe(to: SceneEvents.Update.self) { event in
let time = Float(event.deltaTime)
entity.position.y += sin(Float(Date().timeIntervalSince1970)) * time * 0.1
}
}
visionOS に関する注意
visionOS では、ARKit は ARKitSession、WorldTrackingProvider、PlaneDetectionProvider を使用する別の API 群を提供します。これらの visionOS 固有の型は iOS では利用できません。iOS では、RealityKit が RealityViewCameraContent を通じてワールドトラッキングを自動的に処理します。
よくある間違い
やってはいけないこと:AR の対応状況を確認しない
すべてのデバイスが AR に対応しているわけではありません。フィードバックなしに黒いカメラ画面を表示すると、ユーザーを困惑させます。
// WRONG -- no device check
struct MyARView: View {
var body: some View {
RealityView { content in
// Fails silently on unsupported devices
}
}
}
// CORRECT -- check support and show fallback
struct MyARView: View {
var body: some View {
if ARWorldTrackingConfiguration.isSupported {
RealityView { content in
// AR content
}
} else {
ContentUnavailableView(
"AR Not Supported",
systemImage: "arkit",
description: Text("This device does not support 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)
}
}
やってはいけないこと:インタラクティブなエンティティにコリジョンコンポーネントと入力ターゲットコンポーネントを追加し忘れる
ジェスチャーが機能するのは、CollisionComponent と InputTargetComponent の両方を持つエンティティだけです。これらがないと、タップやドラッグはそのまま通り抜けます。
// WRONG -- entity ignores gestures
let box = ModelEntity(mesh: .generateBox(size: 0.1))
content.add(box)
// CORRECT -- add collision and input components
let box = ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .red, isMetallic: false)]
)
box.components.set(CollisionComponent(
shapes: [.generateBox(size: [0.1, 0.1, 0.1])]
))
box.components.set(InputTargetComponent())
content.add(box)
やってはいけないこと:update クロージャ内で新しいエンティティを作成する
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 で対応している
- 3D モデルを
makeクロージャ内で非同期に読み込んでいる - エンティティは
make内で作成し、update内で変更している(update内では作成していない) - インタラクティブなエンティティに
CollisionComponentとInputTargetComponentの両方がある - コリジョンシェイプがエンティティの見た目のサイズと一致している
- フレームごとのロジックに
SceneEvents.Updateのサブスクリプションを使用している(SwiftUI タイマーは使用していない) - 大規模なシーンでは
Entity.load(named:)ではなく、ModelEntity(named:)による非同期読み込みを使用している - アンカーエンティティが用途に適したサーフェスタイプを対象にしている
-
updateクロージャ内で検索できるよう、エンティティ名を設定している
参考資料
- 発展的なパターン(物理演算、アニメーション、ライティング、ECS):
references/realitykit-patterns.md - RealityKit フレームワーク
- RealityView
- RealityViewCameraContent
- Entity
- ModelEntity
- AnchorEntity
- ARKit フレームワーク
- iOS の ARKit
- ARWorldTrackingConfiguration
- ファイルからエンティティを読み込む