ハイライト
RealityKit は visionOS 向けに Model3D、RealityView、volumetric window、ImmersiveSpace などの API を追加し、開発者が SwiftUI の馴染みのある構文で 2D ウィンドウ、体積ウィンドウ、没入空間に 3D コンテンツを配置し、Entity-Component-System アーキテクチャで動作をカスタマイズできます。
主要内容
3D コンテンツ統合の 3 つの方法
2D ウィンドウに 3D を埋め込む: 最も簡単な方法。Model3D ビューで USD を読み込み、resizable と scaledToFit 修飾子を使います(03:21)。モデルは非同期読み込みで、placeholder をカスタマイズできます。
Volumetric window(体積ウィンドウ): 3D モデル専用のウィンドウで、任意角度からの閲覧と実寸スケールを両立します。幅 1 メートルのモデルは常に 1 メートルで表示されます(05:01)。
ImmersiveSpace(没入空間): App は空間内の任意位置に 3D 要素を置け、ウィンドウ境界を超えられます。完全没入体験に適しています(07:05)。
Entity-Component-System アーキテクチャ
Entity はコンテナオブジェクトで、単体ではレンダリングも物理シミュレーションも行いません。Component を追加して振る舞いを与えます(09:36)。
よく使う Component:
ModelComponent: 3D メッシュとマテリアルを描画TransformComponent: 位置、回転、スケールを制御HoverEffectComponent: 注視時にハイライトInputTargetComponent+CollisionComponent: ジェスチャ入力を受け取るSpatialAudioComponent: 空間オーディオ
System は定期的に実行されるロジックを含み、特定条件に合う Entity に作用します。ECS アーキテクチャは振る舞いとデータを分離し、拡張しやすくします。
RealityView
RealityView は SwiftUI 内の 3D ビューコンテナで、3 つのクロージャを提供します(12:27):
make: Entity を非同期読み込み・初期化update: SwiftUI 状態変化に応じて Entity を更新subscribe: RealityKit イベント(アニメーション完了、物理衝突など)を購読
座標系の約束: 原点は RealityView 中心、Y 軸上、Z 軸はユーザー方向、X 軸は右、1 単位 = 1 メートル。RealityView は SwiftUI と RealityKit 座標空間を変換する convert 関数を提供します。
詳細
2D ウィンドウで 3D モデルを表示する Model3D
(03:40)
import SwiftUI
import RealityKit
struct GlobeModule: View {
var body: some View {
Model3D(named: "Globe") { model in
model
.resizable()
.scaledToFit()
} placeholder: {
ProgressView()
}
}
}
キーポイント:
Model3Dは USD/USDZ を非同期読み込み対応で読み込むresizableとscaledToFitで利用可能空間に合わせるplaceholderは読み込み中にProgressViewを表示- USD ファイルをプロジェクトリソースとして追加する
Volumetric window
(05:52)
import SwiftUI
import RealityKit
struct WorldApp: App {
var body: some SwiftUI.Scene {
WindowGroup(id: "planet-earth") {
Model3D(named: "Globe")
}
.windowStyle(.volumetric)
.defaultSize(width: 0.8, height: 0.8, depth: 0.8, in: .meters)
}
}
キーポイント:
.windowStyle(.volumetric)で体積ウィンドウになる.defaultSizeは実世界単位(メートル)で寸法を定義- 体積ウィンドウ内のモデルは実寸比を保ち、任意角度から見られる
openWindow環境値で開く
ImmersiveSpace
(07:31)
import SwiftUI
import RealityKit
struct WorldApp: App {
var body: some SwiftUI.Scene {
ImmersiveSpace(id: "objects-in-orbit") {
RealityView { content in
// 3D コンテンツをついか
}
}
}
}
キーポイント:
ImmersiveSpaceは新しい Scene タイプopenImmersiveSpace環境値で非同期に開く- コンテンツは空間内の任意位置に配置可能
- 複数の immersion style(mixed、progressive、full)がある
RealityView の非同期読み込みとエンティティ配置
(12:54)
import SwiftUI
import RealityKit
struct Orbit: View {
var body: some View {
RealityView { content in
async let earth = ModelEntity(named: "Earth")
async let moon = ModelEntity(named: "Moon")
if let earth = try? await earth, let moon = try? await moon {
content.add(earth)
content.add(moon)
moon.position = [0.5, 0, 0]
}
}
}
}
キーポイント:
async letで複数モデルを並行読み込みcontent.add()で Entity をシーンに追加positionは[x, y, z]配列でメートル単位の位置を設定- RealityKit 座標: Y 上、Z はユーザー方向、X は右
ドラッグ操作
(18:31)
import SwiftUI
import RealityKit
struct DraggableModel: View {
var earth: Entity
var body: some View {
RealityView { content in
content.add(earth)
}
.gesture(DragGesture()
.targetedToEntity(earth)
.onChanged { value in
earth.position = value.convert(value.location3D,
from: .local, to: earth.parent!)
})
}
}
キーポイント:
- Entity は
InputTargetComponentとCollisionComponentの両方が必要 .targetedToEntityで特定 Entity にのみジェスチャを適用value.location3Dはジェスチャ位置。SwiftUI 座標から Entity の親座標へ変換- CollisionComponent は Reality Composer Pro で簡単に追加できる
アニメーション再生
(14:56)
import SwiftUI
import RealityKit
struct AnimatedModel: View {
@State var subscription: EventSubscription?
var body: some View {
RealityView { content in
if let moon = try? await Entity(named: "Moon"),
let animation = moon.availableAnimations.first {
moon.playAnimation(animation)
content.add(moon)
}
subscription = content.subscribe(to: AnimationEvents.PlaybackCompleted.self) {
// アニメーションかんりょうごのしょり
}
}
}
}
キーポイント:
availableAnimationsでモデル内の組み込みアニメーションを取得playAnimation()で再生content.subscribe()でイベント購読。ここでは再生完了@Stateで購読を保持し、早期解放を防ぐ
軌道アニメーション
(20:20)
let orbit = OrbitAnimation(name: "Orbit",
duration: 30,
axis: [0, 1, 0],
startTransform: moon.transform,
bindTarget: .transform,
repeatMode: .repeat)
if let animation = try? AnimationResource.generate(with: orbit) {
moon.playAnimation(animation)
}
キーポイント:
OrbitAnimationで Entity を親の周りに回転duration: 30は 30 秒で 1 周axis: [0, 1, 0]は Y 軸回りstartTransformは現在位置から開始しジャンプを防ぐrepeatMode: .repeatでループ再生
空間オーディオ
(22:12)
// オーディオソースとしてからの Entity をつくる
let audioSource = Entity()
// オーディオをしこうせいエミッターにせっていし、フォーカスどを 0.75 にする
audioSource.spatialAudio = SpatialAudioComponent(directivity: .beam(focus: 0.75))
// オーディオソースをかいてんし、おとをとくていほうこうへだす
audioSource.orientation = .init(angle: .pi, axis: [0, 1, 0])
// ループオーディオをよみこみ、さいせい
if let audio = try? await AudioFileResource(named: "SatelliteLoop",
configuration: .init(shouldLoop: true)) {
satellite.addChild(audioSource)
audioSource.playAudio(audio)
}
キーポイント:
- オーディオはデフォルトで空間化され、空間に存在するように聞こえる
directivity: .beam(focus: 0.75)で指向性ビームを作成orientationで放出方向を制御shouldLoop: trueでループ再生- 3 種類: Spatial(指向)、Ambient(環境)、Channel(スピーカー直送)
カスタム Component と System
(23:47)
import RealityKit
// Component は Entity にふよされるデータ
struct TraceComponent: Component {
var mesh = TraceMesh()
}
// Entity から Component をよみとり、またはかきこむ
func updateTrace(for entity: Entity) {
var component = entity.components[TraceComponent.self] ?? TraceComponent()
component.update()
entity.components[TraceComponent.self] = component
}
// Codable Component は Reality Composer Pro でつかえる
struct PointOfInterestComponent: Component, Codable {
var name = ""
}
キーポイント:
- Component は
Componentプロトコルに準拠 entity.components[ComponentType.self]で読み書きCodable準拠で Reality Composer Pro で編集可能
(24:51)
import SwiftUI
import RealityKit
// System はロジックコードをふくむ
struct TraceSystem: System {
static let query = EntityQuery(where: .has(TraceComponent.self))
init(scene: Scene) {
// しょきか
}
func update(context: SceneUpdateContext) {
for entity in context.entities(Self.query, updatingSystemWhen: .rendering) {
addCurrentPositionToTrace(entity)
}
}
}
// System をとうろく
struct MyApp: App {
init() {
TraceSystem.registerSystem()
}
}
キーポイント:
- System は
Systemプロトコルに準拠 EntityQueryで System が対象とする Entity を定義updatingSystemWhen: .renderingはレンダーフレームレートで更新- App の
init()でregisterSystem()を呼び登録 - System は App 内のすべての RealityKit コンテンツに自動適用
重要ポイント
-
やること: 学生が惑星をドラッグし軌道運動を観察できる 3D 太陽系教育 App を作る。
-
価値: RealityKit の
OrbitAnimationとドラッグジェスチャで抽象的天文概念を触れられる体験にできる。 -
始め方:
RealityViewで惑星モデルを読み込み、DragGestureでドラッグ、OrbitAnimationで公転アニメーションを実装。 -
やること: 顧客が商品 3D モデルをあらゆる角度から見られる製品展示 App を開発する。
-
価値: Volumetric window は実寸を保ち、顧客が商品の周りを歩いて細部を確認できる。
-
始め方:
WindowGroup+.windowStyle(.volumetric)で体積ウィンドウを作り、内部でModel3Dに USDZ を読み込む。 -
やること: 音符が空間に浮かび空間オーディオを放つ没入型音楽ビジュアライゼーション App を作る。
-
価値:
SpatialAudioComponentで音に方向感を与え、パーティクルと組み合わせて没入感を演出できる。 -
始め方:
ImmersiveSpaceを作り、Entity+SpatialAudioComponentで音源を配置し、カスタム System でパーティクル位置を更新。 -
やること: ユーザーが実際の部屋に仮想家具を置けるインテリアプレビューツールを開発する。
-
価値: RealityKit のアンカーで 3D モデルを壁や床に固定でき、空間オーディオで部屋の音響を模擬できる。
-
始め方:
AnchorEntityで家具を平面にアンカーし、CollisionComponentでドラッグ位置調整を実装。
関連セッション
- Enhance your spatial computing app with RealityKit — RealityKit 応用: portal、パーティクル、アンカー
- Meet Reality Composer Pro — Reality Composer Pro 入門
- Explore materials in Reality Composer Pro — Reality Composer Pro のマテリアル編集
- Work with Reality Composer Pro content in Xcode — Xcode で Reality Composer Pro コンテンツを使う
コメント
GitHub Issues · utterances