# Tank Commander VR

**[🎮 Get it on Meta Horizon App Lab](https://www.meta.com/s/53lmhEPTk)** — one-tap
install on Quest 2/3/Pro, no adb required (alpha channel, auto-approve invite link).
*New app as of v0.7.0 — the original alpha (v0.6.29) is frozen and can't be updated; the new build uses package `com.agilelens.tankcommandervr`, so uninstall the old one first. Details in [RELEASE_NOTES.md](RELEASE_NOTES.md).*
The v0.6.30 source/APK changes below passed XRSim checks. Its alpha upload is
pending recovery of the original signing key; see [the audit](docs/audit-2026-10-11.md).
*Made for Ani* 🧡
A VR tank game for Meta Quest 3, built with [Godot 4](https://godotengine.org/)
(Mobile renderer + OpenXR). You sit inside a one-man turret modeled on real
armored-vehicle crew stations and physically operate everything: flip the
battery master, hold the starter until the engine catches, grab the twin
tillers to drive the tracks, work the turret joystick, cycle the breech lever
to reload the cannon, and arm the rocket console behind its red safety cover.
Every texture and sound is procedurally generated — no external assets.
## Trailer
[](https://github.com/ibrews/TankCommanderVR/releases/download/v0.6.28/tank_commander_vr_trailer.mp4)
Click the image to download/play the trailer (~95MB mp4, hosted as a
[release asset](../../releases/tag/v0.6.28)).
## Screenshots
| | |
|---|---|
|  |  |
| The cardboard gymnasium | Balloon mode |
|  |  |
| The cockpit (battery → fuel → starter → gear) | Hurricane debris |
|  |  |
| Ridge-bridges over live lava (v0.6.0) | Beach assault |
More in [docs/EVOLUTION.md](docs/EVOLUTION.md).
## Modes
**Solo** waves on 10+ battlefields (outdoor, city, town, mudpit, castle,
gymnasium, beach, island, volcano — flowing lava, lethal eruptions, killable
baby-room boss) plus a **DEBUG: KITCHEN SINK** level with one of every enemy
type at close range for fast smoke-testing, with easy/medium/hard, seven
vehicles (tank, jeep, boat, plane, biplane, helicopter, runner), fog/rain/
storm weather, day / golden hour / night-ops stealth, and silly mutators
(low-g, underwater, balloon, paintball). **ENDLESS TOUR** hops to a random
new battlefield every three cleared waves and keeps your score rolling.
**On-foot mode**: dismount your vehicle and walk, sprint (arm-swing or a
stick-sprint option), grapple-swing, and climb using pickable items found in
the world (grapple hook, climbing gloves, energy drink, coffee), then climb
back into the seat you left — climbing works on terrain, buildings, rocks,
trees, and castle walls. Pick up one of the newer weapons (a burst SMG, a
mini-howitzer, a close-range spread gun) alongside the pistol and cabbage
grenade. **Co-op** over LAN or an online relay fallback: one headset drives
+ machine-guns, the other runs the turret (seat-swap hotkey to trade), with
a shared round clock and score — both players render as full Rec-Room-style
procedural avatars (hip/head/hand IK, no imported skeletal assets) to each
other, on-foot or seated, with player names and team colors. **Versus**:
tank, jeep, boat, or plane duel, first to five, with a live round timer/
scoreboard and host god-mode (change map/mode/difficulty, spawn bots on the
fly). No LAN host nearby? The game falls back to an online relay room
automatically. New players get voice coaching and cockpit hints — veterans
can switch **HELP: OFF** in the menu and the tank computer stops repeating
itself.
## Play
**[Get it on App Lab](https://www.meta.com/s/53lmhEPTk)** (recommended) — open that
link on your headset, accept the invite, and install like any other Quest app; you'll
also get "Update" notifications for future builds through your normal App Lab library.
Or sideload the APK from [Releases](../../releases) onto a Quest 2/3/Pro:
```
adb install -r -g TankCommanderVR.apk
```
Or build it yourself: Godot 4.7 + the
[godot_openxr_vendors](https://github.com/GodotVR/godot_openxr_vendors) addon
(included), matching Android build template, Android SDK, and JDK 17 or newer.
The "Meta Quest" preset targets SDK 34 and ARM64. For an isolated export with
data-volume caches, run `python tools/build_quest.py --help` from a named worktree.
## Controls
**Thumbsticks:** drive, aim, fire, reload, rockets, restart, and MG are mapped
to thumbsticks/buttons. Physical cockpit controls provide an alternate way to play.
| Input | Action |
|---|---|
| X | Auto start ritual (battery + engine) |
| Left stick | Drive (tracks mix automatically) |
| Right stick | Turret traverse / gun elevation |
| Right trigger | Forward throttle when the hand is free and away from cockpit controls; trigger on the held turret stick fires |
| Right grip (empty hand) | Fire cannon (auto-reloads) — or trigger while gripping the turret stick |
| A (hold) | Coax machine gun · quick tap cycles the radio station |
| B (tap) | Fire rocket salvo · hold ~1s toggles the HUD |
| Y (tap, left hand) | Recalibrate seat height · hold ~1s respawns/resets the run |
| R-stick click | Cycle camera: 1st / 3rd / far 3rd person (seated only) |
| L-stick click | Restart level (solo) / leave to hangar (multiplayer) |
| Hold LEFT trigger ~1s | Exit the current vehicle (walk up + grip to re-enter) |
**Physical grab/poke:** the cockpit is built entirely from real `VRControl`
levers/switches/knobs/buttons meant to be grabbed and poked by hand —
flip the battery switch, pull the starter, work the tillers, cycle the
breech lever, grab the jeep's steering wheel. Follow the yellow hints and blue
next-control highlight. Nearby controls glow amber. Hold grip to grab; release
to let go. Touch switches once, withdraw, then touch again to change them.
Radio knobs support wrist twisting. The red reload lever stays with the seat
through turret rotation, and the driving tillers, horn, and IR switch are closer.
**Hand tracking:** put the controllers down — pinch = trigger (fire), whole-
hand squeeze = grab. Bare hands have no analog thumbsticks; driving and aiming
use the physical controls. Controller interaction passed XRSim/OpenXR acceptance;
optical hand tracking and wearer comfort still need headset validation.
(Runner/on-foot mode: pump your arms to sprint, or switch to stick-sprint
in the menu — either way it works hands-free.)
**Playtest tuning:** every gameplay number lives in `tuning.cfg`
(auto-created in the app's files dir; on Quest:
`/sdcard/Android/data/com.agilelens.tankcommandervr/files/tuning.cfg`).
Edit, restart, report back.
## Performance (measured on Quest 3S)
The v0.6.30 source caps the enabled thermal sight at 30Hz and skips hidden diagnostic text.
It preserves Mobile/OpenXR, XR VRS, and dynamic foveation. See the
[October audit](docs/audit-2026-10-11.md) for reach measurements, 25 XRSim checks,
regressions, and the remaining graphics/performance priorities. Current standalone
Quest frame times still need a fresh device capture.
Golden-hour beach, full combat demo, glow + fill-light on: **locked 72/72 fps,
App GPU 9.2 ms avg of the 13.8 ms budget** (VrApi logcat, v0.6.0). Glow costs
<1 ms on the Adreno 740 — it stays on.
*Needs re-measuring:* the number above predates v0.6.29, which found that
foveated rendering had likely never actually been engaging — the setting was
present but the Vulkan Mobile renderer's required `Viewport.vrs_mode` flag
wasn't, so the 9.2ms figure (and the "foveation level 2 vs 3 is free" A/B)
may both have been measured with foveation effectively off the whole time.
v0.6.29 also turns on real-time shadows, which cost GPU foveation is meant to
help pay for. Both need a fresh on-device pass.
Foveation, glow, and 40+ gameplay dials are
runtime-tunable via `tuning.cfg` (see above); `autostart.cfg` in the same dir
boots the game into a self-playing demo scene for hands-off profiling:
```
[auto]
level="beach"
time=1 ; 0 day, 1 golden hour, 2 night
demo=true
delay=6.0
```
## Development
Written overnight by [Claude Code](https://claude.com/claude-code) on the
Agile Lens fleet — desktop-verified via a self-playing screenshot loop, then
exported straight to Quest. Design notes and the full build story live in the
Agile Lens knowledge base.
**Using this as a sample project?** The [wiki](../../wiki) covers both
how to play and how the code is put together — procedural mesh generation
(`MeshKit`), the zero-imported-assets/vertex-color rendering convention, the
`VRControl` grab/poke system, `XRRig`/`DesktopRig` structure, the on-foot
locomotion + procedural avatar systems (built on
[godot-xr-tools](https://github.com/GodotVR/godot-xr-tools)), and the
headless QA/smoke-test harnesses under `tools/` and `scripts/*_qa.gd` that
let this project be verified without a headset attached. Everything is
built at runtime in pure GDScript — nothing is instantiated from a `.tscn`
scene file — which is unusual but deliberate; see the wiki's Architecture
page for why and what it costs.
### Claude Code and Codex skills
Five repository-local skills cover GDScript, shaders, Quest performance,
multiplayer, and verification. Both agents use the same maintained instructions,
with optional MIT-licensed community references. See
[the skill guide](docs/agent-skills.md) for discovery, example prompts, and sources.
Check the pack with `python tools/agent_skills.py --check`; after changing skill
metadata, run `python tools/agent_skills.py --sync` to refresh the Claude entries.
## Things to Try
1. Install the game using the App Lab link above and start a solo beach session.
2. Open this checkout in Godot 4.7 and run the main scene to explore the desktop fallback.
3. Open the checkout in Claude Code or Codex and ask it to use `tcvr-testing`
to select a check for a cockpit-control change.
4. Run `python tools/agent_skills.py --check` to verify the shared skill pack.
5. Start the tank using its battery, fuel cover/switch, and starter; grip the
tillers to drive, fire using the held turret stick, then pull the red reload lever.
## Store assets
`docs/store-art/` holds App Lab / marketing assets composed from the game's
own real screenshots (this project has zero external art assets, so store
art is built the same way as everything else — from what the engine
actually renders, not stock imagery): `icon_512.png` (square store icon),
`hero_1920x1080.png` (store cover image), `banner_1280x400.png` (this
README's header), and `screenshot_0[1-5]_*.png` (gallery). Regenerate with
ImageMagick if screenshots change — see git history for the exact commands.
## License
MIT — see [LICENSE](LICENSE). The bundled `godot_openxr_vendors` addon keeps
its own MIT/Apache licenses (see `addons/godotopenxrvendors/`).
## Support
If you like seeing this kind of thing get built and shared, [donations are always welcome](https://www.alexcoulombepresents.com/support) — they buy hardware, render time, and the freedom to keep giving most of this away.