Using object capture assets in RealityKit
At a glance
| Item | Summary |
|---|---|
| Purpose | Create a chess game using RealityKit and assets created using Object Capture. |
| App architecture | ObjectCaptureSampleApp presents ContentView; the shared GameManager owns the chess scene and rules, while piece components and AnimationSystem drive Object Capture-derived entities and SwiftUI overlays forward user intent. |
| Main patterns | SwiftUI scene composition, UI–RealityKit bridge, Observable state owner, Entity-component-system, Session owner boundary, Game manager plus ECS |
| Project style | Code-rich sample with 24 scanned Metal, Swift file(s) and 2693 implementation line(s). |
Project structure
Source bundle/
└── CaptureChess/
├── GameManager.swift # GameManager, State
├── AnimationSystem.swift # AnimationSystem
├── CaptureChessApp.swift # ObjectCaptureSampleApp
├── SplashScreenView.swift # SplashScreenView, RectangleGrid, RectangleRow
├── ContentView.swift # ContentView, OverlayView, ContentView_Previews
├── Components.swift # HasChessPiece, CheckerComponent, ChessPieceComponent
└── ChessViewport.swift # ChessViewport, GestureView
Structure observations
- The runtime boundary is WindowGroup + SwiftUI/UIKit host; the tree is pruned to lifecycle, state, framework, rendering, and ECS files.
- The source is Metal, Swift; role-named types and feature folders separate the major responsibilities.
Overall architecture
flowchart LR
ObjectCaptureSampleApp_1["ObjectCaptureSampleApp"]
WindowGroup___SwiftUI_UIKit_host_2["WindowGroup + SwiftUI/UIKit host"]
ContentView_3["ContentView"]
GameManager_4["GameManager"]
AnimationSystem_5["AnimationSystem"]
RealityKit_entity_graph_6["RealityKit entity graph"]
Object_Capture_assets___chess_ECS_7["Object Capture assets + chess ECS"]
ObjectCaptureSampleApp_1 --> WindowGroup___SwiftUI_UIKit_host_2
WindowGroup___SwiftUI_UIKit_host_2 --> ContentView_3
ContentView_3 --> GameManager_4
GameManager_4 --> AnimationSystem_5
AnimationSystem_5 --> RealityKit_entity_graph_6
RealityKit_entity_graph_6 --> Object_Capture_assets___chess_ECS_7
Reference code
CaptureChess/CaptureChessApp.swift:11 — executable or app-lifecycle anchor.
struct ObjectCaptureSampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}The diagram is a responsibility flow, not a claim that every adjacent node directly calls the next. It separates lifecycle, presentation, stable state, framework/session work, and the RealityKit entity graph.
Ownership and state
classDiagram
GameManager --> Player : turn
ContentView o-- GameManager : gameManager
GameManager --> State : state
GameManager *-- GameManager : shared
Ownership evidence
CaptureChess/GameManager.swift:17 — representative stored state or nearest verified lifecycle anchor.
@Published var turn: ChessGame.Player = .player1
@Published var selectedPiece: ChessPiece?
@Published var game: ChessGame = ChessGame()
@Published var okayToStart = false
@Published var loadProgress: Float = 0| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
GameManager |
Player as turn |
owns value state | The owning type coordinates writes |
ContentView |
GameManager as gameManager |
receives a shared or non-owning reference | The upstream owner controls lifetime; this scope may invoke the exposed mutable API |
GameManager |
State as state |
owns value state | The owning type coordinates writes |
GameManager |
GameManager as shared |
creates and retains | The owning type coordinates writes |
Ownership is intentionally narrow: environment, bindings, observed objects, weak links, and delegate callbacks do not prove lifetime ownership. Initialized stored services and wrapper-managed state do; entities added inside a RealityKit content closure belong to that entity graph.
Class and protocol design
| Type | Responsibility | Depends on or conforms to |
|---|---|---|
ObjectCaptureSampleApp (CaptureChess/CaptureChessApp.swift:11) |
Declares app lifecycle and top-level dependency lifetime. | App |
ContentView (CaptureChess/ContentView.swift:10) |
Presents UI and forwards gestures or lifecycle events. | View |
GameManager (CaptureChess/GameManager.swift:13) |
Coordinates long-lived framework or cross-view work. | ObservableObject |
AnimationSystem (CaptureChess/AnimationSystem.swift:10) |
Updates entities matching a RealityKit component query. | System |
HasChessPiece (CaptureChess/Components.swift:10) |
Declares a replaceable capability or collaboration contract. | Entity |
CheckerComponent (CaptureChess/Components.swift:23) |
Stores RealityKit entity data. | Component |
ChessPieceComponent (CaptureChess/Components.swift:30) |
Stores RealityKit entity data. | Component |
GestureView (CaptureChess/ChessViewport.swift:91) |
Presents UI and forwards gestures or lifecycle events. | UIView |
Verified protocol-oriented seams: ChessPiece → HasChessPiece. Framework conformances remain adapters, not proof of an app-wide protocol architecture.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
private let gameManager: GameManager (CaptureChess/ChessViewport.swift:17) |
private |
Use stays inside the declaration and same-file extensions permitted by Swift. | Inference: Hide lifecycle-sensitive state or an implementation step. |
fileprivate func prepareTexture(_ texture: inout MTLTexture?, format pi... (CaptureChess/ChessViewport+Bloom.swift:46) |
fileprivate |
Use is limited to declarations in this source file. | Inference: Share with same-file helpers without making the symbol module-wide. |
public func incrementPiecesLoaded() { (CaptureChess/GameManager.swift:27) |
public |
The symbol is available to importing modules, subject to its containing type’s visibility. | Inference: Expose an intentional package or cross-target surface. |
No reviewed Swift access example uses private(set), open; declarations without a modifier are internal.
Reference code
CaptureChess/ChessViewport.swift:17 — representative visibility boundary.
class ChessViewport: ARView {
// ...
private let gameManager: GameManager
// ...
}Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Application/command lifecycle | ObjectCaptureSampleApp |
The executable boundary determines app, controller, scene, or command lifetime. |
| Presentation and user intent | ContentView |
The view/controller forwards input and owns only presentation-local work. |
| Shared feature state and commands | GameManager |
A stable owner prevents view recomputation or callbacks from duplicating transitions. |
| Framework/session/render coordination | AnimationSystem |
The role-named collaborator isolates long-lived or per-frame work from presentation code. |
| Per-entity data and repeated behavior | CaptureChess/AnimationSystem.swift:10 |
Components carry data; the system queries and updates matching entities. |
| GPU and low-level resource work | CaptureChess/MetalLibLoader.swift:13 |
Buffer, texture, shader, or frame-resource mutation stays in a rendering/compute boundary. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| SwiftUI scene composition | CaptureChess/CaptureChessApp.swift:11 |
Keeps windows, volumes, and immersive presentation visible at the app boundary. |
| UI–RealityKit bridge | CaptureChess/ContentView.swift:10 |
Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle. |
| Observable state owner | CaptureChess/GameManager.swift:13 |
Keeps shared feature state in a stable owner rather than in transient view values. |
| Entity-component-system | CaptureChess/AnimationSystem.swift:10 |
Stores per-entity data in components and advances repeated behavior in registered systems. |
| Session owner boundary | CaptureChess/AnimationSystem.swift:10 |
Keeps authorization, session lifetime, and framework callbacks in a stable model, manager, or controller. |
| Game manager plus ECS | CaptureChess/GameManager.swift:12 |
Keeps chess rules and scene ownership in one manager while components and systems animate scanned piece assets. |
Naming conventions
- Role suffixes are evidence, not decoration: App:
ObjectCaptureSampleApp; Manager:GameManager; System:AnimationSystem; Component:CheckerComponent,ChessPieceComponent; View:ContentView,GestureView,OverlayView,SplashScreenView. - ECS names pair data and behavior: components
CheckerComponent,ChessPieceComponent; systemsAnimationSystem. - Protocols:
HasChessPiece. - Commands use verb-led methods:
incrementPiecesLoaded,update,handleTap,handleDoubleTap,handlePan,handleRotation,handlePinch,moveBoard. - Files and folders use primary-type or responsibility names such as
Views,Models,Rendering,Components,Systems,Packages, or feature names where those boundaries exist.
Architecture takeaways
- Treat
ObjectCaptureSampleAppas the owner of executable or scene lifecycle, not automatically as the owner of every entity created later. - Keep view/controller input near presentation, but move sessions, reconstruction, capture, rendering, shared game state, or documents into stable owners when their lifetime exceeds one callback or render pass.
- Use components for per-entity data and systems for repeated simulation instead of centralizing every update in a view model or controller.
Source map
| Source file | Relevant symbols |
|---|---|
CaptureChess/GameManager.swift:13 |
GameManager, State |
CaptureChess/AnimationSystem.swift:10 |
AnimationSystem |
CaptureChess/CaptureChessApp.swift:11 |
ObjectCaptureSampleApp |
CaptureChess/SplashScreenView.swift:10 |
SplashScreenView, RectangleGrid, RectangleRow, EmptyCircle, SplashScreenView_Preview |
CaptureChess/ContentView.swift:10 |
ContentView, OverlayView, ContentView_Previews |
CaptureChess/Components.swift:10 |
HasChessPiece, CheckerComponent, ChessPieceComponent |
CaptureChess/ChessViewport.swift:15 |
ChessViewport, GestureView |