-
Notifications
You must be signed in to change notification settings - Fork 0
Build and Deploy
This is the loop you'll run dozens of times: change code → see it on the headset. Once the engine is built, a full round-trip is about 3–4 minutes.
One-time setup (building the engine, ~30–90 min) is covered in the repo README → Building the engine. This page is the repeating loop afterward.
The companion godot-visionos-simulator-kit
(extracted from this project — it pairs great with Cascade Countdown) ships a build.sh switcher
that runs the whole loop with one command and flips between the Simulator and a real device
for you (they differ in the xcodebuild -destination, code-signing, and install tool — exactly the
tedium it hides). It also bundles the simulator input + hand-tracking tooling:
./build.sh sim # build + run in the visionOS Simulator (recommended for prototyping)
./build.sh device # build signed + install on a paired Apple Vision Pro
./build.sh export # just re-export the .pckProject-specific values (device/sim UDID, signing team, bundle id, paths) live in the CONFIG
block at the top of build.sh — override via env var or a sibling build.config — so the
script is reusable across Godot visionOS projects. The four steps below are what it does under
the hood (and what to run by hand when you want to understand or tweak a step).
Iterate in the visionOS Simulator (./build.sh sim); reach for a device only when you need
hand tracking, final perf, or TestFlight. The Simulator shows the real spatial render —
unlike running the project in desktop Godot, which renders broken XR visuals (audio only) and
misleads. GDScript/.pck changes need no engine rebuild — just re-export + rebuild the app
for the sim. The differences the switcher handles for you:
sim |
device |
|
|---|---|---|
xcodebuild -destination
|
platform=visionOS Simulator,id=… |
platform=visionOS,id=… |
| Code signing | CODE_SIGNING_ALLOWED=NO |
CODE_SIGN_IDENTITY + DEVELOPMENT_TEAM
|
| Install / launch |
simctl install + simctl launch
|
devicectl device install + tap by hand |
| Engine slice (auto-picked from the xcframework) |
xros-arm64-simulator (Clancey, renders) |
xros-arm64 (your hand-tracking lib) |
| Hand input | none in-sim (a round auto-starts so audio is audible) | real ARKit hands |
Gotcha: an immersive sim app suspends when backgrounded — keep the Simulator window frontmost or the scene (and its audio) pauses.
1. edit GDScript 2. export .pck 3. xcodebuild 4. install + tap
(main_v2.gd) ──► "pack the game" ──► "wrap in an app" ──► "put on headset"
Change test-project/main_v2.gd (or a sandbox/ actor, or project.godot).
This asks the Godot editor to pack your project into one file the app will run.
~/godot-visionos-pilot/Godot.app/Contents/MacOS/Godot --headless \
--path /Users/you/godot-visionos-pilot/test-project \
--export-pack "visionOS" \
/Users/you/godot-visionos-pilot/out/xcode-visionos/GodotVisionPilot.pck| Piece | ELI5 |
|---|---|
--headless |
run the editor with no window — just do the job and quit |
--path …/test-project |
which project to export |
--export-pack "visionOS" |
use the export preset named "visionOS" |
| the last path | where to write the .pck
|
🔥 The #1 footgun here: the output path must be absolute. A relative path resolves from the project directory, not your shell — so the new
.pcklands somewhere you're not looking, Xcode rebuilds the old one, and you swear your change "did nothing." Always pass a full/Users/...path.
This wraps the .pck + libgodot.a into a signed Vision Pro app.
xcodebuild \
-project out/xcode-visionos/GodotVisionPilot.xcodeproj \
-scheme GodotVisionPilot \
-configuration Debug \
-destination "platform=visionOS,id=<YOUR_DEVICE_UDID>" \
CODE_SIGN_IDENTITY="Apple Development" \
DEVELOPMENT_TEAM="<YOUR_TEAM_ID>" \
buildReplace <YOUR_DEVICE_UDID> (find it in Xcode → Window → Devices and Simulators) and
<YOUR_TEAM_ID> (your Apple Developer team). Signing is required — Vision Pro won't run
an unsigned app.
xcrun devicectl device install app \
--device <YOUR_DEVICE_UDID> \
$(ls -d ~/Library/Developer/Xcode/DerivedData/GodotVisionPilot-*/Build/Products/Debug-xros/GodotVisionPilot.app)Then put on the headset and tap the app icon. You can't remote-launch an immersive app (see Gotcha #6) — so the loop always ends with a human tap.
The app writes a diagnostic frame count you can pull back off the device:
xcrun devicectl device copy from \
--device <YOUR_DEVICE_UDID> \
--source "Documents/xr_diag.txt" \
--destination /tmp/xr_diag.txt \
--domain-type appDataContainer \
--domain-identifier com.agilelens.godotvisionpilotThe per-5-second frame delta should be exactly 450 — that's 90 FPS × 5 s, locked. Anything less means a frame-rate wall was hit; back off whatever you changed last.
| Symptom | Likely cause | Do this |
|---|---|---|
| App runs old code | relative .pck path (Step 2) |
use an absolute output path, re-export |
devicectl install → "unable to locate device" |
the AVP sleeps when you take it off | put it on, retry 2–3× |
| App opens to passthrough only, no scene | script failed to load, or editor/lib version mismatch | run headless first to catch script errors; see Gotcha #5 |
| Renders nothing at 90 FPS | an XR-setup gotcha | run the pre-flight checklist |
To record or screenshot the headset's view at full quality, attach the Developer Strap and use Xcode → Window → Devices and Simulators → (select AVP) → Take Screenshot / Record Screen. (The hero clip and stills in this wiki were captured that way.) Without the strap, AirPlay-mirror to a Mac and record in QuickTime — but you'll get one eye at ~30 fps.
Back to Getting Started · or browse Architecture, Hand Tracking, Procedural Audio.
Built by Alex Coulombe (@ibrews) · Source · Independent project — not affiliated with or endorsed by the Godot Foundation; "Godot" is a trademark of the Godot Foundation.
Developer Guide (ELI5)
- 1 · Getting Started
- 2 · How the Game Is Built
- 3 · Hand Tracking
- 4 · Procedural Audio
- 5 · visionOS Gotchas
- 6 · Build & Deploy
Links