mcpField
R
realitykit

realitykit

github.com/dpearson2699開発1.2k3.2k 回インストール13 週間前に更新
リポジトリ

realitykit について

RealityKit と ARKit を使った iOS 向け拡張現実体験の開発を支援する Swift スキルです。シーン設定、エンティティ配置、操作方法に加え、デバイス対応確認やパフォーマンス上の注意点を扱います。

主な機能

  1. 01RealityView のシーン設定
  2. 02エンティティの作成と配置
  3. 03アンカーと面へのレイキャスト
  4. 04ジェスチャーによるエンティティ操作
  5. 05シーン更新とプラットフォームの注意点

活用シーン

  1. 01iOS アプリの AR シーン構築
  2. 02検出した面へのモデル配置
  3. 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以降です。

目次

セットアップ

プロジェクトの設定
  1. Info.plistにNSCameraUsageDescriptionを追加します
  2. iOSでは、RealityViewCameraContentはデフォルトでARカメラビューを表示します(iOS 18以降、macOS 15以降)。ARを使用しない場合のフォールバックを明示するには、.virtualカメラモードを使用します
  3. 基本的なAR機能にエンタイトルメントは不要です。ARがアプリの中核機能である場合はarkit必須デバイス機能を追加します。それ以外の場合は、isSupportedでAR UIを制御します。
デバイス要件

AR UIを表示する前に、対象のAR構成のisSupported値を確認してください。

import ARKit

guard ARWorldTrackingConfiguration.isSupported else {
    showUnsupportedDeviceMessage()
    return
}
主な型
型プラットフォーム役割
RealityViewiOS 18以降、visionOS 1以降RealityKitコンテンツを表示するSwiftUIビュー
RealityViewCameraContentiOS 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 クロージャで検索できるようにエンティティ名を設定している

参考資料

コメント