mcpField
R
realitykit

realitykit

github.com/dpearson2699开发1.2k3.2k 次安装13 周前更新
代码仓库

关于 realitykit

这项 Swift 技能介绍如何用 RealityKit 和 ARKit 构建 iOS 增强现实体验,面向开发者。内容涵盖场景设置、实体放置与交互,并说明设备支持检查和性能注意事项。

主要功能

  1. 01RealityView 场景设置
  2. 02实体创建与放置
  3. 03锚点与表面射线检测
  4. 04基于手势的实体交互
  5. 05场景更新与平台说明

适用场景

  1. 01为 iOS 应用构建 AR 场景
  2. 02将模型放置在检测到的表面上
  3. 03为 3D 实体添加触控交互

安装

复制下面的命令,在终端中运行。

$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 界面的显示。
设备要求

在显示 AR 界面之前,检查确切 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)
        }
    }
}
创建和更新模式

使用 update 闭包响应 SwiftUI 状态变化:

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(实体组件系统)架构。添加组件可赋予实体行为:

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
                    )
                }
        )
    }
}

场景理解

逐帧更新

订阅 SceneEvents.Update 来持续处理场景,而不是用 SwiftUI 计时器驱动 RealityKit。保持订阅处于活动状态,并使用 event.deltaTime;请参阅实体动画。

平台边界

在 visionOS 上,ARKit 提供了不同的 API,包括 ARKitSession、 WorldTrackingProvider 和 PlaneDetectionProvider。这些 visionOS 专用 类型在 iOS 上不可用。在 iOS 上,RealityKit 通过 RealityViewCameraContent 自动处理世界跟踪。

对于 iOS 架构或迁移说明,请使用 ARWorldTrackingConfiguration.isSupported 检查 AR 支持情况,使用 RealityViewCameraContent 承载内容,并通过放置在 AnchorEntity 中的 Entity/ModelEntity 构建场景。

交接: CollisionComponent + InputTargetComponent 负责 RealityKit 交互;AccessibilityComponent 负责实体辅助功能元数据;详细的 SwiftUI 手势和 VoiceOver/Switch Control 策略由相关章节处理。

将现有的 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)
    }
}
不要:忘记为可交互实体添加碰撞和输入目标组件

可交互实体需要手势和交互中所示的两个组件;缺少 这些组件时,点击和拖动事件会穿透。

不要:在更新闭包中创建新实体

每次 SwiftUI 状态变化时都会运行 update 闭包。在其中创建实体会导致内容在每次 渲染时重复。

// 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 计时器)
  • 大型场景使用 ModelEntity(named:) 异步加载,而不是 Entity.load(named:)
  • 锚点实体根据使用场景选择合适的表面类型
  • 设置实体名称,以便在 update 闭包中查找实体

参考资料

评论

支持 Markdown