WWDC Quick Look 💓 By SwiftGGTeam
Discover RealityKit APIs for iOS, macOS, and visionOS

Discover RealityKit APIs for iOS, macOS, and visionOS

元の動画を見る

Highlight

RealityKit には今年、hover effects のカスタマイズ、SpatialTrackingSession によるハンドトラッキング、force effects と joints による物理シミュレーション、動的ライトとシャドウ、portal crossing、クロスプラットフォーム対応が追加されました。宇宙船ゲームを通じて一連の API をデモします。

主要内容

visionOS で 3D モデルを表示する際、最も一般的なインタラクション不足は、ユーザーが物体を見ても視覚的フィードバックがないことです。RealityKit の従来の HoverEffectComponent はデフォルトの spotlight 効果のみで、アプリの美術方向に合わせられませんでした。今年は highlight と shader の 2 スタイルが追加——highlight は mesh 全体を均一に着色し tint と strength を調整可能、shader は Shader Graph マテリアルに接続し「視線で舷窓が光る」ような細かい効果が可能です。

視覚的フィードバックだけでは不十分——3D インタラクティブアプリでは手の動きをゲーム入力にマッピングする必要があります。昨年はハンドトラッキングに ARKit を直接呼び出し、コード量が多く権限管理も自分で行う必要がありました。今年 RealityKit に SpatialTrackingSession が導入——1 行で権限申請、その後 anchor entity で指先位置を追跡、「指をつまんでスロットルを制御」が数行で実現。Force effects と joints が物理シミュレーション能力を補完:組み込み 4 種の力場(radial、vortex、drag、turbulence)に加え ForceEffectProtocol でカスタム引力場;組み込み 5 種の関節(fixed、ball、revolute、prismatic、distance)に加え PhysicsCustomJoint で軸ごとの線形・角運動制約。動的ライトとシャドウで距離感を把握しやすく、portal crossing で宇宙船がポータルを通過して別シーンへ、EnvironmentLightingConfigurationComponent で門を越える際のライティング遷移を滑らかに。最後に、これらの API は iOS と macOS でも利用可能——入力方式と RealityView の camera mode を差し替えるだけで移植できます。

詳細

Hover Effects カスタマイズ(04:23

Highlight スタイルが最も手早く導入できます。HighlightHoverEffectStyle を作成し color と strength を設定、HoverEffectComponent に包んで entity に割り当てます:

// Add a highlight HoverEffectComponent

let highlightStyle = HoverEffectComponent.HighlightHoverEffectStyle(color: .lightYellow,
                                                                    strength: 0.8)
let hoverEffect = HoverEffectComponent(.highlight(highlightStyle))
spaceship.components.set(hoverEffect)
  • HighlightHoverEffectStylecolor(色調)と strength(0~1、強度)を受け取り、mesh 全体を均一にハイライト
  • .highlight(highlightStyle) でデフォルト spotlight ではなく highlight スタイルを指定
  • components.set() で直接代入——既存コンポーネントの確認は不要

Shader スタイルはより柔軟。Reality Composer Pro の Shader Graph で HoverState ノードを設定し、コードは 1 行で十分:

// Add a shader effect

let hoverEffect = HoverEffectComponent(.shader(.default))
spaceship.components.set(hoverEffect)
  • .shader(.default) は Shader Graph に設定済みの HoverState ノードを使用
  • HoverState ノードは intensity 値(0→1 アニメーション)を提供し、Mix ノードに接続して emissive color を制御可能

SpatialTrackingSession ハンドトラッキング(08:04

2 つの anchor entity で人差し指先と親指先をそれぞれ追跡。カスタム System の update で距離を計算、スロットル値にマッピングし、宇宙船の前進方向に力を加えます:

// Control acceleration with left hand

class HandTrackingSystem: System {

    func update(context: SceneUpdateContext) {
        let indexTipPosition = indexTipEntity.position(relativeTo: nil)
        let thumbTipPosition = thumbTipEntity.position(relativeTo: nil)

        let distance = distance(indexTipPosition, thumbTipPosition)

        let throttle = computeThrottle(with: distance)

        let force = spaceship.transform.forward * throttle
        spaceship.addForce(force, relativeTo: nil)
    }
}
  • position(relativeTo: nil) で anchor entity のワールド空間位置を取得
  • distance() は SIMD3 のグローバル関数で 2 点間の距離を計算
  • computeThrottle(with:) はカスタム関数——距離が短いほどスロットルが大きい
  • addForce(_:relativeTo:) で宇宙船ローカル座標の forward 方向に力を加え、物理エンジンが運動を処理

Force Effects カスタム力場(10:50

組み込み 4 種の力場では「距離に応じて減衰する引力」を表現できない場合、ForceEffectProtocol を実装:

// Adding a gravity force effect

struct Gravity: ForceEffectProtocol {

    var parameterTypes: PhysicsBodyParameterTypes { [.position, .distance] }
    var forceMode: ForceMode = .force

    func update(parameters: inout ForceEffectParameters) {
        guard let distances = parameters.distances,
              let positions = parameters.positions else { return }

        for i in 0..<parameters.physicsBodyCount {
            let force = computeForce(distances[i], positions[i])
            parameters.setForce(force, index: i)
        }
    }
}
  • parameterTypes で position と distance を宣言——物理エンジンが事前計算
  • forceMode.force に設定——出力は力(加速度やインパルスではない)
  • update 内で影響を受ける physics body をすべて走査し、力ベクトルを個別に計算・設定

力場を有効化する際は空間減衰範囲とコリジョンマスクを指定:

// Activating the gravity force effect

let gravity = ForceEffect(effect: Gravity(),
                          spatialFalloff: SpatialForceFalloff(bounds: .sphere(radius: 8.0)),
                          mask: .asteroids)

planet.components.set(ForceEffectComponent(effects: [gravity]))
  • spatialFalloff で半径 8 m 以内の物体のみに影響
  • mask.asteroids コリジョングループの物体のみに適用
  • 小惑星には PhysicsMotionComponent で初速度を設定し軌道運動を形成(13:11

PhysicsCustomJoint カスタム関節(16:19

宇宙船が貨物舱を牽引する際、回転幅を制限し平移を禁止:

// Add a custom joint

guard let hookEntity = spaceship.findEntity(named: "Hook") else { return }
let hookOffset: SIMD3<Float> = hookEntity.position(relativeTo: spaceship)

let hookPin = spaceship.pins.set(named: "Hook", position: hookOffset)
let trailerPin = trailer.pins.set(named: "Trailer", position: .zero)

var joint = PhysicsCustomJoint(pin0: hookPin, pin1: trailerPin)

joint.angularMotionAroundX = .range(-.pi * 0.05 ... .pi * 0.05)
joint.angularMotionAroundY = .range(-.pi * 0.2 ... .pi * 0.2)
joint.angularMotionAroundZ = .range(-.pi * 0.2 ... .pi * 0.2)

joint.linearMotionAlongX = .fixed
joint.linearMotionAlongY = .fixed
joint.linearMotionAlongZ = .fixed

try joint.addToSimulation()
  • pins.set(named:position:) で entity 上のアンカー位置を定義——関節が 2 つの pin を接続
  • angularMotionAroundX/Y/Z で軸ごとに回転範囲を設定——X 軸はより厳しく(±0.05π)上下の揺れを抑制
  • linearMotionAlongX/Y/Z = .fixed で平移を完全禁止——硬い接続をシミュレート
  • addToSimulation() で関節を有効化——例外処理が必要

動的ライトとシャドウ(19:12

宇宙船のヘッドライトにスポットライトとシャドウを追加:

// Add a spotlight with shadow

guard let lightEntity = spaceship.findEntity(named: "HeadLight") else { return }

lightEntity.components.set(SpotLightComponent(color: .yellow,
                                              intensity: 10000.0,
                                              attenuationRadius: 6.0))

lightEntity.components.set(SpotLightComponent.Shadow())
  • SpotLightComponent で色、強度(ルーメン)、減衰半径を設定
  • SpotLightComponent.Shadow() を別コンポーネントとして追加——デフォルトでは動的ライトを受けるすべての物体がシャドウを投射
  • 特定の物体のシャドウ投射を禁止するには DynamicLightShadowComponent(castsShadow: false) を追加(20:01

Portal Crossing ポータル通過(21:36

ポータルの通過モードと宇宙船の通過コンポーネントを設定:

// Enable portal crossing

portal.components.set(PortalComponent(target: portalWorld,
                                      clippingMode: .plane(.positiveZ),
                                      crossingMode: .plane(.positiveZ)))

spaceship.components.set(PortalCrossingComponent())
  • crossingMode: .plane(.positiveZ) で通過平面を設定——ポータルジオメトリの平面と一致させる必要あり
  • clippingMode: .plane(.positiveZ) でクリッピング平面も設定
  • PortalCrossingComponent() を通過させたい entity に追加——未追加の entity はポータル面でクリップされる

宇宙船が門を越えるとライティングが急変——EnvironmentLightingConfigurationComponent で滑らかに遷移(24:33):

// Configure environmental lighting on the spaceship

var lightingConfig = EnvironmentLightingConfigurationComponent()

let distance: Float = computeShipDistanceFromPortal()
lightingConfig.environmentLightingWeight = mapDistanceToWeight(distance)

spaceship.components.set(lightingConfig)
  • environmentLightingWeight の範囲 0~1:1 = 環境プローブライティングを完全使用、0 = 不使用
  • 宇宙船がポータルに近づくにつれ 1 から 0 へ段階的に低下、通過後は門内 IBL のみ
  • ポータルなしでも環境ライティング重みの制御に使用可能

クロスプラットフォーム対応(27:21

iOS の RealityView では world tracking カメラを使用:

// World tracking camera

RealityView { content in

#if os(iOS)

    content.camera = .worldTracking

#endif

}
  • .worldTracking で AR ワールドトラッキングとカメラ映像背景を有効化
  • 入力はハンドトラッキングからマルチタッチへ——左スライダーでスロットル、右仮想ジョイスティックでピッチとロール

重要ポイント

  1. 3D モデルに hover effects を追加してインタラクションを示唆:ユーザーが 3D 物体を見ても視覚的フィードバックがないと「タップできるのか?」と迷う。highlight スタイルは 2 行で導入、shader スタイルは細かい効果向け。始め方:すべてのインタラクティブ entity に HoverEffectComponent(.highlight(...)) を追加、美術要件があれば shader スタイルへ切り替え。

  2. ARKit ハンドトラッキングの代わりに SpatialTrackingSession を使用:ARKit 方式は session と権限を自分で管理し、コード量が多い。SpatialTrackingSession は権限申請と anchor 管理を 1 つの session に統合。始め方SpatialTrackingSession を作成、HandAnchor から anchor entity を作成、カスタム Systemupdate で位置を読み取りジェスチャーを計算。

  3. 物理シーンでは手動位置計算より force effects を優先:フレームごとの transform 更新はバグ(衝突、貫通)を起こしやすい。force effects なら物理エンジンが一括処理。組み込み 4 種で一般的な要件をカバー、不足時は ForceEffectProtocol の update 関数 1 つで対応。始め方:「毎フレーム transform を設定」を addForce + ForceEffectComponent に置き換え、エンジンに運動を任せる。

  4. クロスプラットフォーム移植は入力と RealityView camera mode の変更のみ:hover effects、force effects、joints、ライティング、portal のコア API は iOS/macOS で署名が一致。変更点:visionOS の ImmersiveSpace を iOS の RealityView + .worldTracking に、ハンドトラッキングをマルチタッチに。始め方#if os(iOS) でプラットフォーム差分を分離、コアロジックは変更なし。

関連セッション

コメント

GitHub Issues · utterances