ハイライト
RealityKit は visionOS 向けに Attachments、VideoPlayerComponent、Portals、Particle Emitters、Anchors の 5 大機能を追加し、SwiftUI ビューを 3D シーンに埋め込み、空間で動画を再生し、異世界へのポータルを作り、パーティクル効果を加え、実世界の壁や床にコンテンツを固定できます。
主要内容
Attachments: SwiftUI ビューを 3D シーンへ
以前は 3D モデル横にテキストラベルを付けるにはテクスチャマップや複雑な 3D テキストが必要でした。Attachments なら SwiftUI を書くように簡単です(02:08)。
RealityView の attachments view builder で SwiftUI ビューを定義し、make クロージャで entity(for:) により対応エンティティを取得して、通常のエンティティと同様にシーンへ追加します。テキストラベル、説明カード、操作ボタンなどが可能です。
VideoPlayerComponent: 3D 空間での動画再生
VideoPlayerComponent は 3D シーンに動画を埋め込む新しい RealityKit コンポーネントです(06:27)。
動画のアスペクト比から矩形メッシュを自動生成し、2D 動画や 3D MV-HEVC を含む AVFoundation 形式をすべてサポートします。デフォルト高さは 1 メートルで、エンティティのスケールで調整します。パススルー着色(passthrough tinting)で環境光を動画のトーンに合わせられます。
Portals: 別世界への窓
Portal は空間に「窓」を開き、そこから別の独立した世界が見えます(11:15)。
その世界は独自の照明、コンテンツ、空間関係を持ちます。Portal は PortalMaterial を表面マテリアルに、PortalComponent で対象世界へリンクします。魔法の窓、門廊、展示空間などに適しています。
Particle Emitters: パーティクル効果
ParticleEmitterComponent は火花、雪、衝撃波などの視覚効果をシーンに作れます(15:09)。
Reality Composer Pro で設計し、実行時に RealityKit で属性を動的調整できます。カスタム System と組み合わせればシーン変化に応答できます。
Anchors: 実世界への固定
AnchorEntity は 3D コンテンツを壁、床、頭部/手部相対位置に固定できます(17:14)。
追跡モードは 2 種: .continuous はアンカー移動に継続追従、.once は配置後は動かない。AnchoredStateChanged イベントで状態変化を監視できます。
詳細
Attachments コード例
(02:30)
import SwiftUI
import RealityKit
struct MoonOrbit: View {
var body: some View {
RealityView { content, attachments in
guard let earth = try? await Entity(named: "Earth") else {
return
}
content.add(earth)
if let earthAttachment = attachments.entity(for: "earth_label") {
earthAttachment.position = [0, -0.15, 0]
earth.addChild(earthAttachment)
}
} attachments: {
Attachment(id: "earth_label") {
Text("Earth")
}
}
}
}
キーポイント:
- RealityView の make クロージャに
attachmentsパラメータが追加 attachmentsview builder で埋め込む SwiftUI ビューを定義- 各 Attachment は一意の
id(任意の Hashable)が必要 attachments.entity(for:)で id からエンティティを取得- 取得したエンティティは位置設定し、他エンティティの子として追加可能
glassBackgroundEffect()で Attachment にすりガラス背景を追加できる
VideoPlayerComponent コード例
(08:03)
public func makeVideoEntity() -> Entity {
let entity = Entity()
let asset = AVURLAsset(url: Bundle.main.url(forResource: "tides_video",
withExtension: "mp4")!)
let playerItem = AVPlayerItem(asset: asset)
let player = AVPlayer()
entity.components[VideoPlayerComponent.self] = .init(avPlayer: player)
entity.scale *= 0.4
player.replaceCurrentItem(with: playerItem)
player.play()
return entity
}
キーポイント:
VideoPlayerComponentはAVPlayerインスタンスが必要- コンポーネントが動画アスペクト比から矩形メッシュを自動生成
- デフォルト高さ 1 メートル。ここでは 0.4 メートル(40 cm)にスケール
- MV-HEVC 3D 動画を含む AVFoundation 形式をサポート
- AVPlayer の字幕を自動表示
パススルー着色
(10:05)
var videoPlayerComponent = VideoPlayerComponent(avPlayer: player)
videoPlayerComponent.isPassthroughTintingEnabled = true
entity.components[VideoPlayerComponent.self] = videoPlayerComponent
キーポイント:
isPassthroughTintingEnabledでパススルーを動画トーンに合わせる- TV App で映画を見るときの環境光効果に似る
- 仮想と実環境の調和で没入感を高める
Portal コード例
(13:12)
struct PortalView : View {
var body: some View {
RealityView { content in
let world = makeWorld()
let portal = makePortal(world: world)
content.add(world)
content.add(portal)
}
}
}
public func makeWorld() -> Entity {
let world = Entity()
world.components[WorldComponent.self] = .init()
let environment = try! EnvironmentResource.load(named: "SolarSystem")
world.components[ImageBasedLightComponent.self] = .init(source: .single(environment),
intensityExponent: 6)
world.components[ImageBasedLightReceiverComponent.self] = .init(imageBasedLight: world)
let earth = try! Entity.load(named: "Earth")
let moon = try! Entity.load(named: "Moon")
let sky = try! Entity.load(named: "OuterSpace")
world.addChild(earth)
world.addChild(moon)
world.addChild(sky)
return world
}
public func makePortal(world: Entity) -> Entity {
let portal = Entity()
portal.components[ModelComponent.self] = .init(
mesh: .generatePlane(width: 1, height: 1, cornerRadius: 0.5),
materials: [PortalMaterial()]
)
portal.components[PortalComponent.self] = .init(target: world)
return portal
}
キーポイント:
WorldComponentはエンティティツリーを別世界に属するとマーク- 世界内コンテンツは Portal 表面を通してのみ見える
ImageBasedLightComponentで画像ベース照明を設定PortalMaterial()でメッシュを Portal 表面にするPortalComponent(target: world)で Portal を対象世界へリンク- world と Portal の両方を RealityView の content に追加する
パーティクル効果 System
(15:50)
public class ParticleTransitionSystem: System {
private static let query = EntityQuery(where: .has(ParticleEmitterComponent.self))
public func update(context: SceneUpdateContext) {
let entities = context.scene.performQuery(Self.query)
for entity in entities {
updateParticles(entity: entity)
}
}
}
public func updateParticles(entity: Entity) {
guard var particle = entity.components[ParticleEmitterComponent.self] else {
return
}
let scale = max(entity.scale(relativeTo: nil).x, 0.3)
let vortexStrength: Float = 2.0
let lifeSpan: Float = 1.0
particle.mainEmitter.vortexStrength = scale * vortexStrength
particle.mainEmitter.lifeSpan = Double(scale * lifeSpan)
entity.components[ParticleEmitterComponent.self] = particle
}
キーポイント:
ParticleEmitterComponentを持つエンティティをすべてクエリ- エンティティスケールに応じてパーティクル属性を動的調整
vortexStrengthは回転強度、lifeSpanは寿命- 変更後は Component をエンティティに再代入する必要がある
- パーティクルリソースは Reality Composer Pro で設計可能
壁へのアンカー
(18:19)
import SwiftUI
import RealityKit
struct PortalApp: App {
@State private var immersionStyle: ImmersionStyle = .mixed
var body: some SwiftUI.Scene {
ImmersiveSpace {
RealityView { content in
let anchor = AnchorEntity(.plane(.vertical, classification: .wall,
minimumBounds: [1, 1]))
content.add(anchor)
anchor.addChild(makePortal())
}
}
.immersionStyle(selection: $immersionStyle, in: .mixed)
}
}
キーポイント:
- アンカーは
ImmersiveSpace内で使用する .plane(.vertical, classification: .wall)で垂直壁を探索minimumBounds: [1, 1]は壁が少なくとも 1×1 メートル必要- 一致するアンカーが見つかると RealityKit が壁に自動付着
.mixedimmersion style はパススルーを保ちつつ仮想を表示- アンカー変換は App から見えず、プライバシーを保護
重要ポイント
-
やること: 壁の Portal から世界各地の風景が見える魔法の窓 App を作る。
-
価値: Portal で実際の部屋から任意の世界への窓を開き、空間オーディオで臨場感を出せる。
-
始め方:
WorldComponentで世界コンテンツを定義し、AnchorEntity(.plane(.vertical))で Portal を壁に固定。 -
やること: 3D モデル横に Attachment で知識ラベルを表示する教育 App を開発する。
-
価値: Attachments で SwiftUI と 3D をシームレスに結合し、説明文がモデルと一緒に動く。
-
始め方: RealityView の
attachmentsで Text を定義し、entity(for:)で取得してモデルの子に追加。 -
やること: 空間で 3D 映画を再生する没入型シネマ App を作る。
-
価値: VideoPlayerComponent は MV-HEVC 3D をサポートし、passthrough tinting で環境光を映画トーンに合わせられる。
-
始め方:
VideoPlayerComponent(avPlayer: player)で動画エンティティを作り、isPassthroughTintingEnabled = trueを設定。 -
やること: 部屋にパーティクル(雪、花火)を置ける祝祭デコレーション App を開発する。
-
価値: ParticleEmitterComponent とアンカーで効果を実空間位置に固定できる。
-
始め方: Reality Composer Pro でパーティクルを設計し、
AnchorEntityで天井や壁に固定、System で属性を動的調整。
関連セッション
- Build spatial experiences with RealityKit — RealityKit 基礎: Entity、Component、System、RealityView
- Meet Reality Composer Pro — Reality Composer Pro 入門
- Explore materials in Reality Composer Pro — Reality Composer Pro のマテリアル編集
- Go beyond the window with SwiftUI — SwiftUI の没入空間とシーンタイプ
コメント
GitHub Issues · utterances