๐Ÿง  Quest Development Fundamentals

๐ŸŽฏ Building for Meta Quest

Meta Quest (formerly Oculus Quest) is the most popular standalone VR/MR platform, with 20+ million units sold. It runs Android-based Horizon OS with OpenXR support. Development targets: Unity, Unreal, and native OpenXR. The platform supports VR, AR (passthrough), hand tracking, eye tracking (Quest Pro), and mixed reality.

Why Develop for Quest?

Largest user base: 20M+ headsets sold. No PC required: Standalone, all-in-one. Affordable: $249-$649. Feature-rich: Hand tracking, passthrough, MR, eye tracking. Store access: Reach millions via Meta Horizon Store. Cross-platform: OpenXR ensures portability. Growing market: VR/MR is booming.

๐Ÿ“Œ Standalone๐Ÿ“Œ OpenXR๐Ÿ“Œ Hand Tracking๐Ÿ“Œ Passthrough
// Quest development pipeline (conceptual) class QuestDevelopment: def __init__(self): self.target_device = "Quest 3" # or Quest 2, Quest Pro self.engine = "Unity" # or Unreal, Native self.api = "OpenXR" def setup_project(self): # 1. Install Unity/Unreal with Android support # 2. Install Meta XR SDK or OpenXR plugin # 3. Configure build settings for Android # 3. Enable XR Plugin Management # 4. Set up XR Origin / Camera Rig pass def build_and_deploy(self): # 1. Connect Quest via USB # 2. Enable Developer Mode # 3. Build APK # 4. Install via ADB # 5. Run on device pass def iterate(self): # 1. Test on device # 2. Profile performance # 3. Optimize # 4. Repeat pass # Quest specs (Quest 3) quest_3_specs = { 'chipset': 'Snapdragon XR2 Gen 2', 'ram': '8 GB', 'storage': '128 GB / 512 GB', 'display': '2064ร—2208 per eye (LCD)', 'refresh_rate': '90 Hz / 120 Hz', 'fov': '110ยฐ horizontal', 'tracking': '6-DOF inside-out', 'controllers': 'Touch Plus', 'hand_tracking': 'Yes', 'passthrough': 'Color (2 RGB cameras)', 'depth_sensor': 'Yes (Quest 3)', 'weight': '515 g' }

๐Ÿ•ถ๏ธ Quest Hardware & Specs

๐ŸŽฏ Know your target

Quest devices: Quest 2 (2020), Quest Pro (2022), Quest 3 (2023), Quest 3S (2024). Each has different specs โ€” optimize for the lowest common denominator or target specific devices.

Hardware Comparison

Quest 2: XR2 Gen 1, 6GB RAM, LCD, 1832ร—1920/eye. Quest Pro: XR2+ Gen 1, 12GB RAM, pancake lenses, eye/face tracking. Quest 3: XR2 Gen 2, 8GB RAM, pancake lenses, 2064ร—2208/eye, depth sensor. Quest 3S: XR2 Gen 2, 8GB RAM, Fresnel lenses (Quest 2 style), $299.

๐Ÿฅฝ Quest 3๐Ÿฅฝ Quest 3S๐Ÿฅฝ Quest 2๐Ÿฅฝ Quest Pro
// Query device capabilities at runtime (Unity) using UnityEngine; using UnityEngine.XR; using UnityEngine.XR.OpenXR.Features.MetaQuestSupport; public class QuestDeviceInfo : MonoBehaviour { void Start() { // Device name string deviceName = XRSettings.loadedDeviceName; Debug.Log($"Device: {deviceName}"); // Display info Debug.Log($"Resolution: {XRSettings.eyeTextureWidth}x{XRSettings.eyeTextureHeight}"); Debug.Log($"Refresh Rate: {XRSettings.refreshRate}"); // Device model var device = InputDevices.GetDeviceAtXRNode(XRNode.Head); if (device.TryGetFeatureValue(CommonUsages.deviceModel, out string model)) { Debug.Log($"Model: {model}"); } // Check for specific features bool hasHandTracking = CheckFeature("hand-tracking"); bool hasPassthrough = CheckFeature("passthrough"); bool hasEyeTracking = CheckFeature("eye-tracking"); bool hasFaceTracking = CheckFeature("face-tracking"); // Log device tier DeviceTier tier = GetDeviceTier(); Debug.Log($"Device Tier: {tier}"); } DeviceTier GetDeviceTier() { // Quest 3 / 3S: Tier 3 // Quest Pro: Tier 2 // Quest 2: Tier 1 string model = SystemInfo.deviceModel; if (model.Contains("Quest 3")) return DeviceTier.High; if (model.Contains("Quest Pro")) return DeviceTier.Medium; if (model.Contains("Quest 2")) return DeviceTier.Low; return DeviceTier.Unknown; } enum DeviceTier { Low, // Quest 2 Medium, // Quest Pro High, // Quest 3 / 3S Unknown } } // Device-specific settings [System.Serializable] public class QuestQualitySettings { public DeviceTier tier; public int renderScale; public int textureQuality; public int antiAliasing; public bool enableFoveation; public bool enableSpaceWarp; public int targetFrameRate; public static QuestQualitySettings GetForTier(DeviceTier tier) { switch (tier) { case DeviceTier.Low: return new QuestQualitySettings { tier = DeviceTier.Low, renderScale = 100, textureQuality = 1, antiAliasing = 2, enableFoveation = true, enableSpaceWarp = true, targetFrameRate = 72 }; case DeviceTier.Medium: return new QuestQualitySettings { tier = DeviceTier.Medium, renderScale = 110, textureQuality = 2, antiAliasing = 4, enableFoveation = true, enableSpaceWarp = true, targetFrameRate = 90 }; case DeviceTier.High: return new QuestQualitySettings { tier = DeviceTier.High, renderScale = 120, textureQuality = 3, antiAliasing = 4, enableFoveation = true, enableSpaceWarp = true, targetFrameRate = 120 }; default: return GetForTier(DeviceTier.Low); } } }

โš™๏ธ Development Setup

๐ŸŽฏ Getting started

Quest development requires: Unity/Unreal, Android build support, Meta XR SDK or OpenXR plugin, Developer Mode on the headset, and ADB for deployment.

Setup Steps

1. Install Unity: 2021.3 LTS or newer with Android Build Support. 2. Install Meta XR SDK: Via Package Manager or Asset Store. 3. Configure Player Settings: Android, IL2CPP, ARM64, OpenGL ES 3.0. 4. Enable XR Plugin: OpenXR or Oculus. 5. Set up XR Origin. 6. Enable Developer Mode on Quest. 7. Deploy via ADB.

๐Ÿ”ง Unity๐Ÿ“ฆ Meta XR SDK๐Ÿ”Œ OpenXR๐Ÿ“ฑ ADB
// Unity project setup for Quest (C# editor script) #if UNITY_EDITOR using UnityEditor; using UnityEditor.XR.Management; using UnityEngine.XR.Management; using UnityEngine.XR.OpenXR; public class QuestProjectSetup : EditorWindow { [MenuItem("Quest/Setup Project")] static void SetupProject() { // 1. Set build target to Android EditorUserBuildSettings.SwitchActiveBuildTarget( BuildTargetGroup.Android, BuildTarget.Android ); // 2. Configure Player Settings PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel29; PlayerSettings.Android.targetSdkVersion = AndroidSdkVersions.AndroidApiLevel32; PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP); PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64; PlayerSettings.SetGraphicsAPIs(BuildTarget.Android, new[] { UnityEngine.Rendering.GraphicsDeviceType.OpenGLES3 }); // 3. Enable XR Plug-in Management XRGeneralSettingsPerBuildTarget buildTargetSettings; EditorBuildSettings.TryGetConfigObject( XRGeneralSettings.k_SettingsKey, out buildTargetSettings ); if (buildTargetSettings == null) { buildTargetSettings = ScriptableObject.CreateInstance(); EditorBuildSettings.AddConfigObject( XRGeneralSettings.k_SettingsKey, buildTargetSettings, true ); } var settings = buildTargetSettings.SettingsForBuildTarget(BuildTargetGroup.Android); if (settings == null) { settings = ScriptableObject.CreateInstance(); buildTargetSettings.SetSettingsForBuildTarget(BuildTargetGroup.Android, settings); } settings.InitManagerOnStart = true; // 4. Enable OpenXR (or Oculus) var openXRSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android); if (openXRSettings != null) { // Enable Meta Quest feature var questFeature = openXRSettings.GetFeature(); if (questFeature != null) { questFeature.enabled = true; } } // 5. Add XR Origin to scene AddXROriginToScene(); Debug.Log("Quest project setup complete!"); } static void AddXROriginToScene() { // Create XR Origin GameObject var xrOrigin = new GameObject("XR Origin"); var originComponent = xrOrigin.AddComponent(); // Add camera var cameraGO = new GameObject("Main Camera"); cameraGO.transform.SetParent(xrOrigin.transform); var camera = cameraGO.AddComponent(); camera.tag = "MainCamera"; camera.nearClipPlane = 0.01f; // Add camera offset var offsetGO = new GameObject("Camera Offset"); offsetGO.transform.SetParent(xrOrigin.transform); cameraGO.transform.SetParent(offsetGO.transform); // Configure XR Origin originComponent.Camera = camera; originComponent.CameraFloorOffsetObject = offsetGO; } } #endif // Android manifest requirements (Assets/Plugins/Android/AndroidManifest.xml) /* */ // Deploy via ADB /* # Build APK in Unity # Then install: adb install -r path/to/build.apk # Launch: adb shell am start -n com.company.app/com.unity3d.player.UnityPlayerActivity # View logs: adb logcat -s Unity # Wireless ADB (Quest 3): adb tcpip 5555 adb connect :5555 */

๐ŸŽฎ Unity for Quest

๐ŸŽฏ The most popular engine for Quest

Unity is the dominant engine for Quest development. With Meta XR SDK or OpenXR, you get hand tracking, passthrough, spatial anchors, and more. Unity's XR Interaction Toolkit simplifies input and interaction.

Unity Quest Stack

XR Plugin Management: Enable OpenXR or Oculus. XR Interaction Toolkit: Grab, ray, UI interaction. Meta XR SDK: Oculus-specific features (passthrough, anchors). XR Hands: Hand tracking. AR Foundation: For MR features. Universal Render Pipeline: Optimized for mobile XR.

๐ŸŽฎ XR Toolkit๐Ÿ“ฆ Meta SDKโœ‹ XR Hands๐ŸŽจ URP
// Unity Quest controller setup using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; public class QuestControllerSetup : MonoBehaviour { public XRBaseController leftController; public XRBaseController rightController; public XRRayInteractor leftRay; public XRRayInteractor rightRay; public XRDirectInteractor leftDirect; public XRDirectInteractor rightDirect; void Start() { // Configure controllers for Quest ConfigureController(leftController, XRNode.LeftHand); ConfigureController(rightController, XRNode.RightHand); // Add haptic feedback leftController.SendHapticImpulse(0.1f, 0.05f); } void ConfigureController(XRBaseController controller, XRNode node) { // Controller follows tracked device var poseDriver = controller.gameObject.AddComponent(); // Set up model var model = controller.GetComponent(); if (model != null) { model.modelPrefab = Resources.Load($"Models/{node}Controller"); } } // Button mapping for Quest Touch Plus void Update() { // Trigger if (rightController.TryGetFeatureValue(CommonUsages.trigger, out float trigger)) { if (trigger > 0.5f) { // Trigger pressed } } // Grip if (rightController.TryGetFeatureValue(CommonUsages.grip, out float grip)) { if (grip > 0.5f) { // Grip pressed } } // Thumbstick if (rightController.TryGetFeatureValue(CommonUsages.primary2DAxis, out Vector2 thumbstick)) { // Thumbstick input } // A/B buttons (right controller) if (rightController.TryGetFeatureValue(CommonUsages.primaryButton, out bool aButton)) { // A button } if (rightController.TryGetFeatureValue(CommonUsages.secondaryButton, out bool bButton)) { // B button } // X/Y buttons (left controller) if (leftController.TryGetFeatureValue(CommonUsages.primaryButton, out bool xButton)) { // X button } if (leftController.TryGetFeatureValue(CommonUsages.secondaryButton, out bool yButton)) { // Y button } // Menu button if (leftController.TryGetFeatureValue(CommonUsages.menuButton, out bool menu)) { // Menu button } } } // Meta XR SDK: Passthrough setup using Oculus.Interaction; using Meta.XR; public class QuestPassthrough : MonoBehaviour { public OVRPassthroughLayer passthroughLayer; public Camera xrCamera; void Start() { // Enable passthrough passthroughLayer.enabled = true; passthroughLayer.hidden = false; // Set style passthroughLayer.SetStyle(PassthroughStyle.Color); // Set opacity passthroughLayer.SetAlpha(0.8f); // Enable camera OVRManager.instance.enableMixedReality = true; } public void SetPassthroughStyle(PassthroughStyle style) { passthroughLayer.SetStyle(style); } public void TogglePassthrough() { passthroughLayer.hidden = !passthroughLayer.hidden; } } // Scene understanding (Quest 3) using Meta.XR; public class QuestSceneUnderstanding : MonoBehaviour { public OVRSceneManager sceneManager; public OVRSceneAnchor[] anchors; void Start() { sceneManager.SceneModelLoadedSuccessfully += OnSceneLoaded; sceneManager.LoadSceneModel(); } void OnSceneLoaded() { anchors = FindObjectsOfType(); foreach (var anchor in anchors) { if (anchor.Classification == OVRSceneAnchor.OVRSceneAnchorClassification.Floor) { // Floor detected var volume = anchor.GetComponent(); // Use floor for placement } else if (anchor.Classification == OVRSceneAnchor.OVRSceneAnchorClassification.Wall) { // Wall detected } } } }

๐ŸŽฌ Unreal for Quest

๐ŸŽฏ High-fidelity visuals on Quest

Unreal Engine offers stunning visuals for Quest, with built-in OpenXR support and Meta XR plugin. Best for high-fidelity VR experiences, though more challenging to optimize than Unity.

Unreal Quest Stack

OpenXR Plugin: Standard XR support. Meta XR Plugin: Quest-specific features. Forward Renderer: Essential for mobile XR. Mobile HDR: Enable for better lighting. Vulkan: Better performance than OpenGL. Instanced Stereo: Required for performance.

๐ŸŽฌ OpenXR๐Ÿ“ฆ Meta XRโšก Forward Renderer๐Ÿ”ฅ Vulkan
// Unreal Quest setup (C++) // In DefaultEngine.ini: /* [/Script/AndroidRuntimeSettings.AndroidRuntimeSettings] +PackageName=com.yourcompany.yourapp +MinSDKVersion=29 +TargetSDKVersion=32 +bBuildForArm64=true +bBuildForArmv7=false +bBuildForX86_64=false [/Script/Engine.RendererSettings] r.DefaultFeature.AutoExposure=False r.MobileHDR=True r.Mobile.AntiAliasing=2 # MSAA r.Mobile.ShadingPath=0 # Forward shading r.SupportSkyAtmosphere=True r.SupportStationarySkylight=True [/Script/OpenXRPlugin.OpenXRPlugin] +EnabledExtensions=XR_EXT_hand_tracking +EnabledExtensions=XR_FB_passthrough +EnabledExtensions=XR_FB_scene +EnabledExtensions=XR_FB_spatial_anchor */ // Unreal Quest controller (Blueprints or C++) #include "HeadMountedDisplayFunctionLibrary.h" #include "IXRTrackingSystem.h" #include "MotionControllerComponent.h" // C++ setup AMyQuestPawn::AMyQuestPawn() { // Create VR root VROrigin = CreateDefaultSubobject(TEXT("VROrigin")); RootComponent = VROrigin; // Create camera Camera = CreateDefaultSubobject(TEXT("Camera")); Camera->SetupAttachment(VROrigin); // Create motion controllers LeftController = CreateDefaultSubobject(TEXT("LeftController")); LeftController->SetupAttachment(VROrigin); LeftController->MotionSource = FXRMotionControllerBase::LeftHandSourceId; RightController = CreateDefaultSubobject(TEXT("RightController")); RightController->SetupAttachment(VROrigin); RightController->MotionSource = FXRMotionControllerBase::RightHandSourceId; } // Hand tracking in Unreal (OpenXR) void AMyQuestPawn::EnableHandTracking() { // Enable hand tracking extension UHeadMountedDisplayFunctionLibrary::EnableHandTracking(); // Get hand tracking data FXRHandTrackingState HandState; if (UHeadMountedDisplayFunctionLibrary::GetHandTrackingState(HandState)) { // Process hand joints for (int i = 0; i < HandState.NumJoints; i++) { FVector JointPosition = HandState.JointPositions[i]; FQuat JointRotation = HandState.JointRotations[i]; // Use for hand mesh } } } // Passthrough in Unreal (Meta XR) void AMyQuestPawn::EnablePassthrough() { // Enable passthrough layer UMetaXRFunctionLibrary::EnablePassthrough(true); UMetaXRFunctionLibrary::SetPassthroughStyle(EPassthroughStyle::Color); UMetaXRFunctionLibrary::SetPassthroughOpacity(0.8f); }

๐Ÿ“ฑ Native Development (OpenXR)

๐ŸŽฏ Maximum performance and control

Native Quest development uses OpenXR with the Android NDK and C/C++. This gives maximum performance and access to all features, but requires more work. Best for high-performance engines and custom renderers.

Native Quest Stack

OpenXR: Standard XR API. Android NDK: C/C++ development. Vulkan: High-performance graphics. Oboe: Low-latency audio. Meta XR SDK: Native Quest features. CMake: Build system.

๐Ÿ”ง OpenXR๐Ÿ“ฑ NDK๐ŸŽจ Vulkan๐Ÿ”Š Oboe
// Native Quest app (C++ with OpenXR) #include #include #include #include #define LOG_TAG "QuestNative" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) class QuestNativeApp { private: XrInstance instance; XrSystemId systemId; XrSession session; XrSpace stageSpace; XrSwapchain swapchain; VkDevice device; VkQueue queue; public: bool Initialize(ANativeActivity* activity) { // 1. Create OpenXR instance XrInstanceCreateInfo instanceInfo{XR_TYPE_INSTANCE_CREATE_INFO}; strcpy(instanceInfo.applicationInfo.applicationName, "QuestNativeApp"); instanceInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION; // Enable extensions const char* extensions[] = { XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME, XR_EXT_HAND_TRACKING_EXTENSION_NAME, XR_FB_PASSTHROUGH_EXTENSION_NAME }; instanceInfo.enabledExtensionCount = 3; instanceInfo.enabledExtensionNames = extensions; XrResult result = xrCreateInstance(&instanceInfo, &instance); if (XR_FAILED(result)) { LOGI("Failed to create OpenXR instance: %d", result); return false; } // 2. Get system XrSystemGetInfo systemInfo{XR_TYPE_SYSTEM_GET_INFO}; systemInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; xrGetSystem(instance, &systemInfo, &systemId); // 3. Create Vulkan device if (!CreateVulkanDevice()) return false; // 4. Create session XrGraphicsBindingVulkan2KHR graphicsBinding{XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR}; graphicsBinding.instance = vkInstance; graphicsBinding.physicalDevice = vkPhysicalDevice; graphicsBinding.device = device; graphicsBinding.queueFamilyIndex = queueFamilyIndex; graphicsBinding.queueIndex = 0; XrSessionCreateInfo sessionInfo{XR_TYPE_SESSION_CREATE_INFO}; sessionInfo.next = &graphicsBinding; sessionInfo.systemId = systemId; xrCreateSession(instance, &sessionInfo, &session); // 5. Create reference space XrReferenceSpaceCreateInfo spaceInfo{XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; spaceInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; spaceInfo.poseInReferenceSpace = identityPose; xrCreateReferenceSpace(session, &spaceInfo, &stageSpace); // 6. Create swapchains CreateSwapchains(); LOGI("Quest native app initialized"); return true; } void RenderFrame() { // 1. Poll events PollEvents(); // 2. Wait for frame XrFrameWaitInfo waitInfo{XR_TYPE_FRAME_WAIT_INFO}; XrFrameState frameState{XR_TYPE_FRAME_STATE}; xrWaitFrame(session, &waitInfo, &frameState); // 3. Begin frame XrFrameBeginInfo beginInfo{XR_TYPE_FRAME_BEGIN_INFO}; xrBeginFrame(session, &beginInfo); // 4. Locate views XrViewLocateInfo viewLocateInfo{XR_TYPE_VIEW_LOCATE_INFO}; viewLocateInfo.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; viewLocateInfo.displayTime = frameState.predictedDisplayTime; viewLocateInfo.space = stageSpace; XrViewState viewState{XR_TYPE_VIEW_STATE}; uint32_t viewCount = 2; XrView views[2] = {{XR_TYPE_VIEW}, {XR_TYPE_VIEW}}; xrLocateViews(session, &viewLocateInfo, &viewState, viewCount, &viewCount, views); // 5. Render each eye for (uint32_t i = 0; i < viewCount; i++) { RenderEye(views[i], i); } // 6. End frame XrCompositionLayerProjectionView projectionViews[2] = {}; for (uint32_t i = 0; i < viewCount; i++) { projectionViews[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; projectionViews[i].pose = views[i].pose; projectionViews[i].fov = views[i].fov; projectionViews[i].subImage.swapchain = swapchains[i]; projectionViews[i].subImage.imageRect = {{0, 0}, {width, height}}; } XrCompositionLayerProjection projectionLayer{XR_TYPE_COMPOSITION_LAYER_PROJECTION}; projectionLayer.space = stageSpace; projectionLayer.viewCount = viewCount; projectionLayer.views = projectionViews; const XrCompositionLayerBaseHeader* layers[] = { (XrCompositionLayerBaseHeader*)&projectionLayer }; XrFrameEndInfo endInfo{XR_TYPE_FRAME_END_INFO}; endInfo.displayTime = frameState.predictedDisplayTime; endInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; endInfo.layerCount = 1; endInfo.layers = layers; xrEndFrame(session, &endInfo); } void Shutdown() { xrDestroySpace(stageSpace); xrDestroySession(session); xrDestroyInstance(instance); } };

๐ŸŽฎ Quest Input & Controllers

๐ŸŽฏ Touch Plus controllers

Quest 3 uses Touch Plus controllers with improved tracking (no rings). They have: trigger, grip, thumbstick, A/B/X/Y buttons, menu button, and haptic feedback. Quest also supports hand tracking and voice commands.

Input Methods

Touch Plus: 6-DOF tracking, haptics, buttons. Hand tracking: 26 joints per hand, pinch gestures. Voice: "Hey Meta" commands. Eye tracking: Quest Pro only. Body tracking: Estimated from head/hands. Keyboard/mouse: For productivity.

๐ŸŽฎ Touch Plusโœ‹ Hands๐Ÿ—ฃ๏ธ Voice๐Ÿ‘๏ธ Eyes
// Quest Touch Plus input (Unity) using UnityEngine; using UnityEngine.XR; using UnityEngine.XR.Interaction.Toolkit; public class QuestTouchPlusInput : MonoBehaviour { public XRBaseController leftController; public XRBaseController rightController; public ActionBasedController leftActionController; public ActionBasedController rightActionController; void Start() { // Set up action-based input SetupActions(); } void SetupActions() { // Trigger -> Select // Grip -> Grab // Thumbstick -> Move/Turn // A/B/X/Y -> Actions // Menu -> Pause // Haptic feedback leftActionController.hapticImpulseDuration = 0.1f; leftActionController.hapticImpulseAmplitude = 0.5f; } // Direct input reading void Update() { InputDevice leftDevice = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand); InputDevice rightDevice = InputDevices.GetDeviceAtXRNode(XRNode.RightHand); // Trigger (analog) if (rightDevice.TryGetFeatureValue(CommonUsages.trigger, out float rightTrigger)) { // 0.0 to 1.0 } // Grip (analog) if (rightDevice.TryGetFeatureValue(CommonUsages.grip, out float rightGrip)) { // 0.0 to 1.0 } // Thumbstick (2D) if (rightDevice.TryGetFeatureValue(CommonUsages.primary2DAxis, out Vector2 rightThumbstick)) { // Move: thumbstick.y // Turn: thumbstick.x } // Thumbstick click if (rightDevice.TryGetFeatureValue(CommonUsages.primary2DAxisClick, out bool rightThumbClick)) { // Thumbstick pressed } // Buttons if (rightDevice.TryGetFeatureValue(CommonUsages.primaryButton, out bool aButton)) { // A button (right) } if (rightDevice.TryGetFeatureValue(CommonUsages.secondaryButton, out bool bButton)) { // B button (right) } if (leftDevice.TryGetFeatureValue(CommonUsages.primaryButton, out bool xButton)) { // X button (left) } if (leftDevice.TryGetFeatureValue(CommonUsages.secondaryButton, out bool yButton)) { // Y button (left) } // Menu button if (leftDevice.TryGetFeatureValue(CommonUsages.menuButton, out bool menuButton)) { // Menu } // Haptic feedback if (Input.GetKeyDown(KeyCode.Space)) { SendHaptics(rightController, 0.5f, 0.1f); } } void SendHaptics(XRBaseController controller, float amplitude, float duration) { controller.SendHapticImpulse(amplitude, duration); } } // Meta XR SDK: Advanced input using Oculus.Interaction; using Oculus.Interaction.Input; public class MetaXRInput : MonoBehaviour { public ControllerRef leftControllerRef; public ControllerRef rightControllerRef; public HandRef leftHandRef; public HandRef rightHandRef; void Start() { // Check if controllers are connected bool hasControllers = leftControllerRef.Controller != null; bool hasHands = leftHandRef.Hand != null; // Switch between controllers and hands if (hasControllers) { EnableControllers(); } else if (hasHands) { EnableHandTracking(); } } void EnableControllers() { leftControllerRef.gameObject.SetActive(true); rightControllerRef.gameObject.SetActive(true); leftHandRef.gameObject.SetActive(false); rightHandRef.gameObject.SetActive(false); } void EnableHandTracking() { leftControllerRef.gameObject.SetActive(false); rightControllerRef.gameObject.SetActive(false); leftHandRef.gameObject.SetActive(true); rightHandRef.gameObject.SetActive(true); } }

โœ‹ Hand Tracking on Quest

๐ŸŽฏ Controller-free interaction

Quest supports hand tracking with 26 joints per hand. It enables natural interaction: pinch, grab, point, and gestures. Works alongside controllers (switches automatically when you put them down).

Hand Tracking Features

26 joints: Wrist, palm, and 5 joints per finger. Pinch: Thumb + index finger. Grab: Fist gesture. Point: Index finger extended. Gestures: Thumbs up, peace, etc. Poke: Touch UI with fingertip. Auto-switch: Controllers โ†” hands.

๐Ÿ–๏ธ 26 Joints๐Ÿค PinchโœŠ Grab๐Ÿ‘‰ Poke
// Quest hand tracking (Unity XR Hands) using UnityEngine; using UnityEngine.XR.Hands; using UnityEngine.XR.Management; public class QuestHandTracking : MonoBehaviour { private XRHandSubsystem handSubsystem; void Start() { // Get hand subsystem var subsystems = new List(); SubsystemManager.GetSubsystems(subsystems); if (subsystems.Count > 0) { handSubsystem = subsystems[0]; handSubsystem.updatedHands += OnHandsUpdated; } } void OnHandsUpdated(XRHandSubsystem subsystem, XRHandSubsystem.UpdateSuccessFlags updateSuccessFlags, XRHandSubsystem.UpdateType updateType) { // Left hand XRHand leftHand = subsystem.leftHand; if (leftHand.isTracked) { ProcessHand(leftHand, "left"); } // Right hand XRHand rightHand = subsystem.rightHand; if (rightHand.isTracked) { ProcessHand(rightHand, "right"); } } void ProcessHand(XRHand hand, string side) { // Get wrist pose XRHandJoint wrist = hand.GetJoint(XRHandJointID.Wrist); if (wrist.TryGetPose(out Pose wristPose)) { // Use wrist position } // Get index fingertip XRHandJoint indexTip = hand.GetJoint(XRHandJointID.IndexTip); if (indexTip.TryGetPose(out Pose indexPose)) { // Use index tip for pointing } // Get thumb tip XRHandJoint thumbTip = hand.GetJoint(XRHandJointID.ThumbTip); if (thumbTip.TryGetPose(out Pose thumbPose)) { // Compute pinch float pinchDistance = Vector3.Distance(indexPose.position, thumbPose.position); bool isPinching = pinchDistance < 0.02f; // 2cm if (isPinching) { OnPinch(side, indexPose.position); } } // Check for grab (all fingers curled) bool isGrabbing = IsGrabbing(hand); if (isGrabbing) { OnGrab(side); } } bool IsGrabbing(XRHand hand) { // Check if middle, ring, pinky are curled float middleCurl = GetFingerCurl(hand, XRHandFingerID.Middle); float ringCurl = GetFingerCurl(hand, XRHandFingerID.Ring); float pinkyCurl = GetFingerCurl(hand, XRHandFingerID.Little); return middleCurl > 0.7f && ringCurl > 0.7f && pinkyCurl > 0.7f; } float GetFingerCurl(XRHand hand, XRHandFingerID fingerId) { // Get fingertip and base XRHandJoint tip = hand.GetJoint(GetTipJoint(fingerId)); XRHandJoint base = hand.GetJoint(GetBaseJoint(fingerId)); if (tip.TryGetPose(out Pose tipPose) && base.TryGetPose(out Pose basePose)) { // Compute curl angle Vector3 direction = tipPose.position - basePose.position; return Vector3.Angle(basePose.up, direction) / 180f; } return 0f; } void OnPinch(string side, Vector3 position) { // Handle pinch Debug.Log($"Pinch with {side} hand at {position}"); } void OnGrab(string side) { // Handle grab Debug.Log($"Grab with {side} hand"); } } // Meta XR SDK: Hand tracking with interaction using Oculus.Interaction; using Oculus.Interaction.HandGrab; public class MetaHandInteraction : MonoBehaviour { public HandGrabInteractor leftGrabInteractor; public HandGrabInteractor rightGrabInteractor; public PoseUseInteractor leftPoseInteractor; public PoseUseInteractor rightPoseInteractor; void Start() { // Enable hand grab leftGrabInteractor.enabled = true; rightGrabInteractor.enabled = true; // Enable pose use (buttons) leftPoseInteractor.enabled = true; rightPoseInteractor.enabled = true; } }

๐Ÿ“ท Passthrough & Mixed Reality

๐ŸŽฏ Blending real and virtual

Quest 3 and Quest Pro support color passthrough, enabling mixed reality. You can see the real world through the headset cameras and overlay virtual content. Combined with scene understanding, this enables powerful MR experiences.

Passthrough Features

Color passthrough: Full-color view of reality. Layers: Passthrough as a layer behind VR content. Style: Color, grayscale, edge. Opacity: Blend between VR and reality. Scene understanding: Detect walls, floor, furniture. Spatial anchors: Persistent content in real world. Occlusion: Virtual objects behind real ones.

๐ŸŽฅ Color Passthrough๐Ÿงฑ Scene Meshโš“ Anchors๐Ÿšช Room Setup
// Quest passthrough setup (Meta XR SDK) using UnityEngine; using Meta.XR; using Oculus.Interaction; public class QuestMixedReality : MonoBehaviour { public OVRPassthroughLayer passthroughLayer; public OVRSceneManager sceneManager; public Camera xrCamera; void Start() { // 1. Enable passthrough passthroughLayer.enabled = true; passthroughLayer.hidden = false; passthroughLayer.SetStyle(PassthroughStyle.Color); passthroughLayer.SetAlpha(1.0f); // 2. Enable scene understanding sceneManager.SceneModelLoadedSuccessfully += OnSceneLoaded; sceneManager.LoadSceneModel(); // 3. Enable spatial anchors EnableSpatialAnchors(); } void OnSceneLoaded() { // Scene model loaded โ€” place content on real surfaces var anchors = FindObjectsOfType(); foreach (var anchor in anchors) { switch (anchor.Classification) { case OVRSceneAnchor.OVRSceneAnchorClassification.Floor: CreateFloorMarker(anchor); break; case OVRSceneAnchor.OVRSceneAnchorClassification.Wall: CreateWallMarker(anchor); break; case OVRSceneAnchor.OVRSceneAnchorClassification.Ceiling: // Ceiling detected break; case OVRSceneAnchor.OVRSceneAnchorClassification.Table: CreateTableContent(anchor); break; case OVRSceneAnchor.OVRSceneAnchorClassification.Couch: CreateCouchContent(anchor); break; } } } void EnableSpatialAnchors() { // Enable spatial anchor storage OVRAnchor.EnableSpatialAnchorStorage(); } public void CreateSpatialAnchor(Vector3 position, Quaternion rotation) { // Create anchor at position var anchor = OVRAnchor.CreateSpatialAnchorAsync( new Pose(position, rotation) ); // Save anchor for persistence OVRAnchor.SaveAnchorAsync(anchor); } // Passthrough styles public void SetPassthroughStyle(PassthroughStyle style) { passthroughLayer.SetStyle(style); } public void SetPassthroughOpacity(float opacity) { passthroughLayer.SetAlpha(opacity); } } // Scene understanding (Quest 3) using Meta.XR; using UnityEngine; public class QuestSceneUnderstanding : MonoBehaviour { public OVRSceneManager sceneManager; public GameObject floorPrefab; public GameObject wallPrefab; public GameObject tablePrefab; void Start() { // Request scene permission sceneManager.RequestSceneCapture(); // Load scene model sceneManager.SceneModelLoadedSuccessfully += OnSceneLoaded; sceneManager.LoadSceneModel(); } void OnSceneLoaded() { Debug.Log("Scene model loaded!"); // Get all scene anchors var anchors = FindObjectsOfType(); foreach (var anchor in anchors) { // Get volume (bounding box) var volume = anchor.GetComponent(); var plane = anchor.GetComponent(); var classification = anchor.Classification; switch (classification) { case OVRSceneAnchor.OVRSceneAnchorClassification.Floor: Instantiate(floorPrefab, anchor.transform.position, anchor.transform.rotation); break; case OVRSceneAnchor.OVRSceneAnchorClassification.Wall: var wall = Instantiate(wallPrefab, anchor.transform.position, anchor.transform.rotation); if (plane != null) { wall.transform.localScale = new Vector3(plane.Width, plane.Height, 0.01f); } break; case OVRSceneAnchor.OVRSceneAnchorClassification.Table: var table = Instantiate(tablePrefab, anchor.transform.position, anchor.transform.rotation); if (volume != null) { table.transform.localScale = volume.Size; } break; } } } } // Spatial anchors for persistent MR using Meta.XR; using UnityEngine; public class SpatialAnchorManager : MonoBehaviour { private OVRAnchor anchor; public async void CreateAnchor(Vector3 position, Quaternion rotation) { // Create anchor anchor = await OVRAnchor.CreateSpatialAnchorAsync(new Pose(position, rotation)); if (anchor.IsValid) { // Save anchor await OVRAnchor.SaveAnchorAsync(anchor); Debug.Log($"Anchor saved: {anchor.Uuid}"); } } public async void LoadAnchor(string uuid) { // Load anchor by UUID var anchors = await OVRAnchor.FetchAnchorsAsync( new List(), new OVRAnchor.FetchOptions { Uuids = new[] { Guid.Parse(uuid) } } ); if (anchors.Count > 0) { anchor = anchors[0]; // Place content at anchor transform.position = anchor.Pose.position; transform.rotation = anchor.Pose.rotation; } } }

โšก Performance Optimization

๐ŸŽฏ Hitting 72/90/120 fps

Quest is a mobile device โ€” performance is critical. You must maintain the target frame rate (72/90/120 Hz) to avoid judder and motion sickness. Optimization is mandatory.

Optimization Techniques

Fixed Foveated Rendering: Lower resolution at edges. Application SpaceWarp: Synthesize frames. Single Pass Instanced: Render both eyes in one pass. LOD: Level of detail. Occlusion culling: Don't render hidden objects. GPU instancing: Batch draw calls. Texture compression: ASTC. Shaders: Mobile-optimized.

โšก 72/90/120 fps๐ŸŽฏ Foveated๐Ÿ”„ SpaceWarp๐Ÿ“ฆ Batching
// Quest performance optimization (Unity) using UnityEngine; using UnityEngine.XR; using Unity.XR.Oculus; public class QuestPerformance : MonoBehaviour { void Start() { // 1. Set target frame rate OVRManager.display.displayFrequency = 90.0f; // or 72, 120 // 2. Enable Fixed Foveated Rendering OVRManager.foveatedRenderingLevel = OVRManager.FoveatedRenderingLevel.High; OVRManager.useDynamicFoveatedRendering = true; // Quest Pro/3 // 3. Enable Application SpaceWarp OVRManager.enableSpaceWarp = true; // 4. Enable single-pass instanced rendering // (Set in Player Settings > XR Plugin Management) // 5. Set CPU/GPU performance levels OVRManager.cpuLevel = 4; // 0-4 (higher = more performance) OVRManager.gpuLevel = 4; // 6. Enable phase sync OVRPlugin.SetPhaseSync(true); // 7. Set eye texture resolution XRSettings.eyeTextureResolutionScale = 1.0f; // 1.0 = native // 8. Configure quality settings QualitySettings.vSyncCount = 0; // XR handles VSync Application.targetFrameRate = 90; } void Update() { // Monitor performance float fps = 1.0f / Time.unscaledDeltaTime; if (fps < 85) { // Below 90fps // Reduce quality ReduceQuality(); } } void ReduceQuality() { // 1. Lower foveated rendering level (more aggressive) OVRManager.foveatedRenderingLevel = OVRManager.FoveatedRenderingLevel.HighTop; // 2. Lower eye texture resolution XRSettings.eyeTextureResolutionScale = 0.9f; // 3. Reduce LOD bias QualitySettings.lodBias = 0.7f; // 4. Disable expensive effects // ... } } // LOD setup for Quest public class QuestLOD : MonoBehaviour { void Start() { var lodGroup = GetComponent(); // Configure LOD for Quest (lower distances) var lods = lodGroup.GetLODs(); // LOD 0: 0-5m (full detail) // LOD 1: 5-15m (medium) // LOD 2: 15-30m (low) // Culled: 30m+ for (int i = 0; i < lods.Length; i++) { lods[i].screenRelativeTransitionHeight = 0.5f - i * 0.15f; } lodGroup.SetLODs(lods); } } // Occlusion culling setup public class QuestOcclusionCulling : MonoBehaviour { void Start() { // Bake occlusion data (Editor) // StaticOcclusionCulling.Compute(); // Enable occlusion culling at runtime Camera.main.useOcclusionCulling = true; } } // Texture compression (ASTC) /* In Unity: 1. Select texture 2. Set Platform: Android 3. Set Format: ASTC 6x6 (or 8x8 for UI) 4. Set Compression: Normal Quality */ // Mobile shader optimization Shader "Quest/MobileOptimized" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_instancing #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; UNITY_VERTEX_OUTPUT_STEREO }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; v2f vert (appdata v) { v2f o; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_OUTPUT(v2f, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o, o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv) * _Color; UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDPROC } } } // Profiling with OVR Metrics Tool /* OVR Metrics Tool (installed on Quest): - CPU/GPU utilization - Frame rate - Frames dropped - Temperature - Power consumption - Foveation level Access via: adb shell am start -n com.oculus.ovrmetricstool/.MainActivity */

๐Ÿช Store Submission & Distribution

๐ŸŽฏ Getting your app to users

Distribute your Quest app via the Meta Horizon Store (formerly Oculus Store) or App Lab (early access). Requirements: passing technical review, content guidelines, and performance targets.

Distribution Options

Horizon Store: Full store, curated, discovery. App Lab: Early access, less curation. SideQuest: Sideloading, open. Direct APK: Enterprise, testing. Requirements: 72fps min, comfort, no crashes. Review: Technical + content review.

๐Ÿช Horizon Store๐Ÿงช App Lab๐Ÿ“ฆ SideQuestโœ… Review
// Quest store submission checklist public class QuestStoreSubmission { // 1. Technical requirements bool passesPerformance() { // Must maintain 72fps (Quest 2) / 90fps (Quest 3) // No dropped frames > 1% return true; } bool passesComfort() { // No forced camera movement // Comfort options available // No motion sickness triggers return true; } bool passesStability() { // No crashes in 30min session // No memory leaks // Handles pause/resume return true; } // 2. Content requirements bool passesContentGuidelines() { // No offensive content // Age-appropriate // Proper warnings return true; } // 3. Metadata void prepareStoreListing() { // App name // Description // Screenshots (16:9, 2560x1440) // Trailer video // Icon (1024x1024) // Category // Age rating // Privacy policy // Support contact } // 4. Build requirements void prepareBuild() { // Signed APK // 64-bit ARM // Target SDK 32+ // Min SDK 29+ // IL2CPP // OpenGL ES 3.0 or Vulkan } } // App Lab submission /* 1. Create Meta Developer account 2. Create app in Developer Dashboard 3. Upload APK 4. Fill out metadata 5. Submit for review 6. Once approved, set release channel */ // Horizon Store submission /* 1. Complete App Lab first 2. Meet quality bar 3. Apply for Horizon Store 4. Pass technical review 5. Pass content review 6. Launch! */ // Sideloading with SideQuest /* 1. Enable Developer Mode on Quest 2. Connect via USB 3. Install SideQuest on PC 4. Drag APK to SideQuest 5. Launch from Unknown Sources */ // Enterprise distribution /* 1. Meta Quest for Business 2. Device Management 3. Private channel distribution 4. MDM integration */ // Version management [System.Serializable] public class AppVersion { public int major; public int minor; public int patch; public string buildNumber; public override string ToString() { return $"{major}.{minor}.{patch} ({buildNumber})"; } } // Analytics for Quest using Unity.Services.Analytics; public class QuestAnalytics : MonoBehaviour { void Start() { // Initialize Unity Analytics AnalyticsService.Instance.StartDataCollection(); // Log session start AnalyticsService.Instance.CustomData("session_start", new Dictionary { { "device", SystemInfo.deviceModel }, { "version", Application.version } }); } public void LogEvent(string eventName, Dictionary parameters) { AnalyticsService.Instance.CustomData(eventName, parameters); } }

๐Ÿงช Testing & Debugging

๐ŸŽฏ Finding and fixing issues

Quest testing requires on-device testing (emulators are limited). Tools: ADB logcat, OVR Metrics Tool, RenderDoc, Unity Profiler, and Meta Quest Developer Hub.

Testing Tools

ADB logcat: View logs from device. OVR Metrics: Performance overlay on device. RenderDoc: GPU debugging. Unity Profiler: CPU/GPU profiling. MQDH: Meta Quest Developer Hub. Remote testing: Test without deploying.

๐Ÿ“ ADB๐Ÿ“Š OVR Metrics๐Ÿ” RenderDoc๐Ÿ“ˆ Profiler
# ADB commands for Quest testing # View logs adb logcat -s Unity:V # Filter for errors adb logcat *:E # Clear log adb logcat -c # Take screenshot adb exec-out screencap -p > screenshot.png # Record video adb shell screenrecord /sdcard/video.mp4 adb pull /sdcard/video.mp4 # Install APK adb install -r build.apk # Uninstall adb uninstall com.company.app # Launch app adb shell am start -n com.company.app/com.unity3d.player.UnityPlayerActivity # Force stop adb shell am force-stop com.company.app # Get device info adb shell getprop ro.product.model # Battery level adb shell dumpsys battery | grep level # Temperature adb shell dumpsys battery | grep temperature # Performance stats adb shell dumpsys gfxinfo com.company.app # Memory info adb shell dumpsys meminfo com.company.app // Unity Profiler for Quest using Unity.Profiling; public class QuestProfiler : MonoBehaviour { ProfilerRecorder cpuRecorder; ProfilerRecorder gpuRecorder; ProfilerRecorder memoryRecorder; void OnEnable() { cpuRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Internal, "CPU Main Thread Frame Time"); gpuRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, "GPU Frame Time"); memoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Total Used Memory"); } void Update() { if (cpuRecorder.Valid) { float cpuTime = cpuRecorder.LastValue / 1000000f; // ms if (cpuTime > 11.1f) { // 90fps = 11.1ms Debug.LogWarning($"CPU time high: {cpuTime:F2}ms"); } } if (gpuRecorder.Valid) { float gpuTime = gpuRecorder.LastValue / 1000000f; if (gpuTime > 11.1f) { Debug.LogWarning($"GPU time high: {gpuTime:F2}ms"); } } if (memoryRecorder.Valid) { long memory = memoryRecorder.LastValue; if (memory > 3_000_000_000) { // 3GB Debug.LogWarning($"Memory high: {memory / 1_000_000}MB"); } } } void OnDisable() { cpuRecorder.Dispose(); gpuRecorder.Dispose(); memoryRecorder.Dispose(); } } // Remote testing with Quest Developer Hub /* 1. Install Meta Quest Developer Hub (MQDH) 2. Connect Quest via USB 3. Enable Developer Mode 4. Use MQDH for: - App installation - Log viewing - Performance monitoring - Screenshots/video - Device management */ // Automated testing public class QuestAutomatedTest : MonoBehaviour { void Start() { StartCoroutine(RunTests()); } IEnumerator RunTests() { // Test 1: Frame rate yield return TestFrameRate(); // Test 2: Memory yield return TestMemory(); // Test 3: Input yield return TestInput(); // Test 4: Tracking yield return TestTracking(); Debug.Log("All tests complete"); } IEnumerator TestFrameRate() { float duration = 10f; float elapsed = 0; int frames = 0; while (elapsed < duration) { elapsed += Time.deltaTime; frames++; yield return null; } float avgFps = frames / duration; Debug.Log($"Average FPS: {avgFps:F1}"); if (avgFps < 85) { Debug.LogError("Frame rate below target!"); } } }

โš ๏ธ Challenges & Best Practices

๐ŸŽฏ Avoiding common pitfalls

Quest development has unique challenges: limited performance, motion sickness, controller/hand switching, and store requirements. Following best practices ensures a smooth launch.

Common Pitfalls

Performance: Not optimizing for mobile GPU. Comfort: Forced camera movement, no comfort options. Input: Ignoring hand tracking, poor controller mapping. Battery: High power consumption. Heat: Thermal throttling. Store: Missing requirements. Testing: Not testing on actual device.

โšก Performance๐Ÿ›‹๏ธ Comfort๐ŸŽฎ Input๐Ÿ”‹ Battery
// Best practices for Quest development // 1. Performance public class PerformanceBestPractices { void Start() { // Use URP or lightweight render pipeline // Enable Fixed Foveated Rendering // Use Single Pass Instanced // Enable Application SpaceWarp // Use LOD groups // Bake lighting // Use occlusion culling // Compress textures (ASTC) // Use mobile shaders // Batch draw calls // Limit real-time lights // Use GPU instancing // Profile regularly } } // 2. Comfort public class ComfortBestPractices { void Start() { // Never move camera without user input // Provide comfort options: // - Teleport vs smooth locomotion // - Snap turn vs smooth turn // - Vignette during movement // - Seated mode // Maintain stable frame rate // Keep horizon level // Avoid acceleration/deceleration // Provide rest reminders } } // 3. Input public class InputBestPractices { void Start() { // Support both controllers and hands // Auto-switch when controllers put down // Provide clear affordances // Use haptic feedback // Map buttons consistently // Support both left and right handed // Provide input remapping } } // 4. Battery & Heat public class BatteryBestPractices { void Start() { // Lower CPU/GPU levels when possible // Reduce display brightness // Pause rendering when not visible // Use 72Hz mode for less demanding apps // Avoid continuous high GPU usage // Monitor temperature // Implement thermal throttling handling } void OnApplicationPause(bool pause) { if (pause) { // Reduce rendering when paused Application.targetFrameRate = 30; } else { Application.targetFrameRate = 90; } } } // 5. Store requirements public class StoreBestPractices { void Start() { // Maintain 72fps minimum // No crashes // Handle pause/resume // Support recentering // Provide comfort options // Include age rating // Privacy policy // Support contact // Regular updates } } // 6. Testing public class TestingBestPractices { void Start() { // Test on actual Quest device // Test all supported devices // Test for 30+ minutes // Test with different users // Monitor performance // Test edge cases // Test pause/resume // Test controller disconnect // Test hand tracking } } // 7. UX public class UXBestPractices { void Start() { // Large, legible text // High contrast UI // Comfortable interaction distances // Clear feedback for all actions // Minimize text input // Use spatial audio // Provide onboarding // Support accessibility } }

๐Ÿš€ Future: Quest 4 & Beyond

๐ŸŽฏ What's next for Quest

The future of Quest: higher resolution, better passthrough, eye tracking, face tracking, brain-computer interfaces, and AI integration. Meta is investing billions in XR.

Future Quest Features

Quest 4: Higher res, better optics, more power. Neural interfaces: EMG wristbands for input. Photorealistic avatars: Codec avatars. AI assistant: Context-aware help. Cloud rendering: Offload to cloud. Neural rendering: NeRF/Gaussian Splatting. Smart glasses: Ray-Ban Meta evolution.

๐Ÿฅฝ Quest 4๐Ÿง  Neural Input๐Ÿค– AIโ˜๏ธ Cloud
// Future Quest development (conceptual) public class FutureQuestDevelopment { // 1. Neural input (EMG wristband) void ProcessNeuralInput() { // Read muscle signals from wristband float[] emgSignals = NeuralInput.GetEMGSignals(); // Classify gestures Gesture gesture = NeuralClassifier.Classify(emgSignals); // Map to actions switch (gesture) { case Gesture.Pinch: OnPinch(); break; case Gesture.Point: OnPoint(); break; case Gesture.Swipe: OnSwipe(); break; } } // 2. Photorealistic avatars void RenderCodecAvatar() { // Load avatar model CodecAvatar avatar = CodecAvatar.Load("user_avatar"); // Drive with face/eye tracking avatar.SetFacePose(FaceTracking.GetPose()); avatar.SetEyeGaze(EyeTracking.GetGaze()); // Render avatar.Render(); } // 3. AI assistant void AIAssistant() { // Context-aware help string context = GetUserContext(); string query = GetUserQuery(); string response = LLM.Generate(query, context); // Display in AR ShowInAR(response); } // 4. Cloud rendering void CloudRendering() { // Send camera pose to cloud Pose pose = GetCameraPose(); CloudRenderer.SendPose(pose); // Receive rendered frame Texture2D frame = CloudRenderer.ReceiveFrame(); // Display DisplayFrame(frame); } // 5. Neural rendering (Gaussian Splatting) void NeuralRendering() { // Load Gaussian Splatting scene GaussianSplatting scene = GaussianSplatting.Load("scene.splat"); // Render from user's viewpoint RenderTexture output = scene.Render(GetCameraPose()); // Display Display(output); } } // Ray-Ban Meta smart glasses development public class RayBanMetaDevelopment { void Start() { // Camera access // Audio (open-ear speakers) // Touch controls // Voice commands // AI assistant (Meta AI) // Live streaming // Photo/video capture } } // Meta Horizon OS public class HorizonOS { void Start() { // Multi-app support // Spatial anchors // Scene understanding // Passthrough // Hand tracking // Eye tracking (future) // Face tracking (future) } } // WebXR on Quest public class WebXRQuest { void Start() { // Browser-based VR/AR // No app installation // Cross-platform // Three.js, Babylon.js, A-Frame // WebXR Hand Input // WebXR Passthrough } }