Unity has been the default for standalone VR for years, largely because Unreal’s Android pipeline has a reputation for eating a day before you see anything in a headset. That reputation is earned and the problem is almost entirely setup, not development.
Once it builds, Unreal brings things Unity does not: Blueprints for rapid interaction logic, a renderer with better defaults, and Nanite and Lumen for PC VR. This gets you to a running build.
Quinn Kuslich’s setup walkthrough for Quest 3.
Step 1 — Start from the VR template, always
New Project → Games → Virtual Reality. Blueprint, not C++, unless you have a reason.
The template already contains the things you would otherwise spend a week on: a VR Pawn with head and hand tracking, teleport and snap-turn locomotion, grab interaction on physics objects, and a motion-controller setup with Enhanced Input mappings already wired. Start from an empty project and you will rebuild all of it worse.
Also set, in Project Settings:
- Engine → Rendering → Forward Shading: enabled (PC VR) or select Mobile rendering for standalone
- Instanced Stereo: enabled — renders both eyes in one pass. Roughly free performance; there is no reason not to.
- Default RHI: Vulkan for Android
Step 2 — The Android setup, which is the actual barrier
This is where people give up, so be systematic.
Install the Android SDK through Unreal’s own script, not Android Studio’s installer. Unreal ships SetupAndroid.bat (Windows) or SetupAndroid.sh (macOS/Linux) in Engine/Extras/Android/. Run that. It installs the exact SDK, NDK and JDK versions your engine version expects.
The single most common failure is having a newer Android Studio with a different NDK, which produces link errors that look like code problems and are not. Unreal is extremely specific about NDK version and the requirement changes between engine releases. Check Engine/Config/Android/ or the official page for your version — do not assume.
Then in Project Settings → Platforms → Android:
- Accept the SDK licence (there is a button; builds fail silently-ish without it)
- Minimum SDK Version: 32, Target SDK Version: 32 for current Quest headsets
- Package for Oculus Mobile devices: add your device
- Support arm64: on. Support armv7: off.
- Enable Vulkan: on. Enable OpenGL ES3.2: off, unless you need a fallback.
And install the Meta XR plugin from Fab, enable it, and restart. The template plus the plugin is the supported path.
Step 3 — Mobile rendering, and what you give up
For standalone, select the Mobile rendering path. It is a forward renderer with a hard feature budget, and knowing the limits up front saves you building something that cannot ship.
What you lose relative to desktop deferred:
- Lumen and Nanite — not available on mobile. Bake your lighting.
- Dynamic shadows are extremely expensive. One stationary directional light with a cascaded shadow map, or baked shadows only.
- Post-processing is mostly off the table. Bloom is affordable; ambient occlusion, motion blur and depth of field are not. Motion blur and DoF are also actively bad in VR regardless of cost, because the eye moves independently of the camera.
- Material instruction counts matter. Mobile has a much lower ceiling, and a complex master material will fail to compile rather than run slowly.
What you keep: forward shading, MSAA (which looks considerably better than temporal AA in a headset, and temporal AA in VR produces smearing), and instanced stereo.
Set r.MobileHDR to false unless you specifically need it. It is a significant saving.
Step 4 — Lighting, which is most of your performance
The single biggest decision in a standalone VR project.
Bake everything you can. Set lights to Static and build lighting. A fully baked scene on mobile hardware can look remarkable and costs nearly nothing per frame.
One stationary light at most if you need dynamic shadows on moving objects, and keep its shadow distance short.
Use a lightmass importance volume around the playable area so lightmap resolution goes where the player can get to, not into the sky.
Check lightmap resolution per object. The default is often far too high on large meshes and far too low on small detailed ones, and your build times and texture memory both come from here.
Step 5 — Build, deploy, iterate
File → Package Project → Android → Android (ASTC)
ASTC is the texture format for current mobile GPUs. Then install the APK with adb:
adb devices # confirm the headset is listed
adb install -r MyProject.apk
adb logcat -s UE4 UE5 # the logs you actually need
The headset must be in developer mode with USB debugging authorised — accept the prompt inside the headset, not on the desktop.
Faster iteration: use Launch On from the editor toolbar rather than full packaging. It cooks only what changed and pushes it, turning a ten-minute package into a one-minute deploy. For logic-only changes, VR Preview in the editor over Link or Air Link is faster still — but it uses the desktop renderer, so it tells you nothing about mobile performance.
Step 6 — Input through Enhanced Input
Unreal’s Enhanced Input system replaced the old action-mapping system, and the VR template already uses it.
The model is two layers: Input Actions (abstract — “Grab”, “Teleport”) and Input Mapping Contexts (which physical inputs trigger them, per situation). Add an action, add it to the mapping context, bind it in the Pawn’s Blueprint.
The reason this matters in VR specifically is contexts. Holding an object, pointing at a menu and free locomotion want different bindings for the same trigger, and swapping a mapping context is cleaner than a tree of branches checking state.
Step 7 — Hold the frame rate
The budget on standalone is 72 Hz minimum, two eyes, and dropping below it is immediately physical for the user rather than merely ugly.
In order of impact:
- Draw calls. The hard constraint on mobile. Merge static meshes, use instancing, and keep the count in the low hundreds.
- Overdraw from transparency. Translucent materials in VR are brutal because near-field transparency covers enormous numbers of pixels. Use masked instead of translucent wherever possible.
- Fixed Foveated Rendering. Enable it. It reduces peripheral resolution for a substantial saving at little perceptual cost — Unreal exposes it through the Meta XR plugin, and it is close to free performance.
stat unitandstat gpuin a packaged build on device. Profiling in the editor will mislead you about everything.- Occlusion. Mobile Unreal’s occlusion culling is limited; use cull distance volumes and design the level so the player cannot see the whole thing at once.
Where to go next
- PC VR, where the gloves come off. On a tethered headset you get the deferred renderer, Lumen and Nanite — a completely different visual ceiling and a completely different performance problem.
- Hand tracking via the Meta XR plugin, which exposes joints and pinch gestures without controllers.
- Passthrough and scene understanding for mixed reality, also through the Meta XR plugin — the room mesh and plane data that let content sit in the actual space.
- Blueprints for interaction, C++ for systems. The usual division, and the right one: iterate interaction logic visually, move anything that runs every frame on many actors into C++.