AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Hz Unity Meta Quest Ui

skill-meta-quest-agentic-tools-hz-unity-meta-quest-ui · by meta-quest

Configures Unity UI for Meta Quest and Horizon OS VR development — world-space canvases, TextMesh Pro setup, comfortable sizing, viewing distances, and interaction readiness.

No reviews yet
0 installs
48 views
0.0% view→install

Install

$ agentstack add skill-meta-quest-agentic-tools-hz-unity-meta-quest-ui

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-meta-quest-agentic-tools-hz-unity-meta-quest-ui)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Hz Unity Meta Quest Ui? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Meta Quest VR UI Setup

When to use this skill

Use this skill automatically when:

  • Setting up a Canvas for VR
  • Creating UI text with TextMesh Pro in a VR project
  • Adding buttons, sliders, or other interactive UI in VR
  • User reports pink/magenta text, unclickable buttons, or UI sizing issues in VR
  • Configuring VR interaction (ray or poke) on a Canvas

Prerequisite: TMP Essential Resources

Before creating ANY VR UI, verify TMP resources are imported. Use Unity_RunCommand:

using UnityEngine;
using UnityEditor;
using System.IO;

internal class CommandScript : IRunCommand
{
    public void Execute(ExecutionResult result)
    {
        string fontPath = "Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF.asset";
        var font = AssetDatabase.LoadAssetAtPath(fontPath);
        if (font != null)
            result.Log("TMP Essential Resources: IMPORTED. Default font present.");
        else
            result.LogError("TMP Essential Resources: NOT IMPORTED. Use tmp-resources skill first.");
    }
}

If not imported, use the tmp-resources skill before proceeding.

Step 1: Create World Space Canvas

Use Unity_RunCommand to create and configure the canvas:

using UnityEngine;
using UnityEditor;
using UnityEngine.UI;

internal class CommandScript : IRunCommand
{
    public void Execute(ExecutionResult result)
    {
        // Adapt the name to match your canvas (e.g., "MainMenu", "SettingsUI")
        var go = new GameObject("MenuUI");
        var canvas = go.AddComponent();
        canvas.renderMode = RenderMode.WorldSpace;

        go.AddComponent();

        // Remove CanvasScaler — not appropriate for VR
        var scaler = go.GetComponent();
        if (scaler != null)
            Object.DestroyImmediate(scaler);

        var rt = go.GetComponent();
        rt.localScale = new Vector3(0.001f, 0.001f, 0.001f);
        rt.sizeDelta = new Vector2(1920f, 1080f);
        rt.position = new Vector3(0f, 1.5f, 2f);

        result.RegisterObjectCreation(go);
        result.Log("Created VR Canvas '{0}'. Scale: {1}, Size: {2}, Position: {3}",
            go.name, rt.localScale, rt.sizeDelta, rt.position);
    }
}

Canvas rules

  • Render Mode: Always World Space. Screen Space modes break stereo rendering.
  • Scale: 0.001 on all axes (1 unit in canvas = 1mm in world).
  • CanvasScaler: Remove it. Physical size is controlled by world scale, not screen adaptation.
  • Distance: Place 1.5-3m from user. Never closer than 0.5m. Max 5m for readable text.
  • Physical size formula: Canvas sizeDelta * scale = meters. Example: 1920 * 0.001 = 1.92m wide.

Step 2: Create child UI elements

All child elements (panels, buttons, text) must follow these rules:

  • localScale: Always [1, 1, 1]. Never scale children to compensate for canvas scale.
  • localPosition.z: Always 0. Children must sit on the canvas plane.
  • Size control: Use RectTransform.sizeDelta and anchors, never scale.
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using TMPro;

internal class CommandScript : IRunCommand
{
    public void Execute(ExecutionResult result)
    {
        // Replace "MenuUI" with the actual canvas name used in Step 1
        var canvas = GameObject.Find("MenuUI");
        if (canvas == null) { result.LogError("Canvas 'MenuUI' not found."); return; }

        // Panel
        var panel = new GameObject("ButtonPanel");
        panel.transform.SetParent(canvas.transform, false);
        var panelRT = panel.AddComponent();
        panelRT.localScale = Vector3.one;
        panelRT.sizeDelta = new Vector2(800f, 600f);
        var panelImg = panel.AddComponent();
        panelImg.color = new Color(0.1f, 0.1f, 0.1f, 0.95f);
        panelImg.raycastTarget = false;

        var layout = panel.AddComponent();
        layout.spacing = 50f;
        layout.padding = new RectOffset(80, 80, 100, 100);
        layout.childAlignment = TextAnchor.MiddleCenter;

        // Button
        var btnGO = new GameObject("StartButton");
        btnGO.transform.SetParent(panel.transform, false);
        var btnRT = btnGO.AddComponent();
        btnRT.localScale = Vector3.one;
        btnRT.sizeDelta = new Vector2(400f, 120f);
        var btnImg = btnGO.AddComponent();
        btnImg.color = new Color(0.2f, 0.6f, 1f, 1f);
        btnGO.AddComponent();

        // Button text
        var textGO = new GameObject("Text");
        textGO.transform.SetParent(btnGO.transform, false);
        var textRT = textGO.AddComponent();
        textRT.localScale = Vector3.one;
        textRT.anchorMin = Vector2.zero;
        textRT.anchorMax = Vector2.one;
        textRT.sizeDelta = Vector2.zero;
        var tmp = textGO.AddComponent();
        tmp.text = "Start";
        tmp.fontSize = 48f;
        tmp.color = new Color(0.95f, 0.95f, 0.95f, 1f);
        tmp.alignment = TextAlignmentOptions.Center;
        tmp.raycastTarget = false;

        result.RegisterObjectCreation(panel);
        result.Log("Created panel with button. Panel scale: {0}, Button scale: {1}",
            panelRT.localScale, btnRT.localScale);
    }
}

Step 3: Validate created UI

After creating UI, verify critical properties with Unity_RunCommand:

using UnityEngine;
using UnityEditor;

internal class CommandScript : IRunCommand
{
    public void Execute(ExecutionResult result)
    {
        // Replace "MenuUI" with the actual canvas name
        var canvas = GameObject.Find("MenuUI");
        if (canvas == null) { result.LogError("Canvas not found."); return; }

        var crt = canvas.GetComponent();
        var c = canvas.GetComponent();

        bool pass = true;

        if (c.renderMode != RenderMode.WorldSpace)
        { result.LogError("Canvas renderMode is not World Space."); pass = false; }

        if (Mathf.Abs(crt.localScale.x - 0.001f) > 0.0001f)
        { result.LogError("Canvas scale is {0}, expected 0.001.", crt.localScale); pass = false; }

        // Check all children
        foreach (Transform child in canvas.GetComponentsInChildren(true))
        {
            if (child == canvas.transform) continue;
            var rt = child.GetComponent();
            if (rt == null) continue;

            if (rt.localScale != Vector3.one)
            {
                result.LogError("Child '{0}' has localScale {1}, expected (1,1,1).", child.name, rt.localScale);
                rt.localScale = Vector3.one;
                pass = false;
            }
            if (Mathf.Abs(rt.localPosition.z) > 0.01f)
            {
                result.LogError("Child '{0}' has localPosition.z={1}, expected 0.", child.name, rt.localPosition.z);
                rt.localPosition = new Vector3(rt.localPosition.x, rt.localPosition.y, 0f);
                pass = false;
            }
        }

        if (pass)
            result.Log("Validation PASSED. All properties correct.");
        else
            result.LogWarning("Validation found issues. Auto-fixed child scale/position where possible.");
    }
}

Common issues this catches:

  • Canvas scale stuck at 1,1,1 instead of 0.001
  • Child elements auto-scaled to compensate for parent (e.g., 1000x)
  • Child elements offset on Z axis (e.g., localPosition.z = -2500)

Step 4: Add VR interaction (interactive UI only)

Only add interaction when your Canvas has interactive elements (buttons, sliders, dropdowns, toggles, input fields). Skip this for display-only UI (HUD, score, labels).

Decision tree

Does your Canvas have buttons, dropdowns, sliders, toggles, or input fields?
  YES -> Add VR interaction. Without it, interactive elements won't work in VR.
  NO  -> Skip. Display-only UI doesn't need interaction overhead.

Ray interaction (default choice)

Use for most VR UI — lets users point at UI with controller or hand rays:

Call the Meta Unity extension MCP tool meta_add_canvas_interaction_ray with the canvas name:

meta_add_canvas_interaction_ray  with  NameOrID: ""

This adds: RayInteractable, PointableCanvas, ISDK_RayCanvasInteraction child, and a scene-level Pointable Canvas Module.

When to use: menus, settings panels, any UI beyond arm's reach.

Poke interaction (close-range only)

Use for physical touch-style interaction within arm's reach ("


This adds: `PokeInteractable`, `PointableCanvas`, close-range collision detection.

**When to use**: control panels on surfaces, virtual keyboards, diegetic UI on props.

### Both (advanced)

Call ray first, then poke, for hybrid UIs where users can point OR touch.

### Verify interaction was added

After calling the Meta Unity extension MCP tool, verify with `Unity_RunCommand`:

```csharp
using UnityEngine;
using UnityEditor;

internal class CommandScript : IRunCommand
{
    public void Execute(ExecutionResult result)
    {
        // Replace "MenuUI" with the actual canvas name
        var canvas = GameObject.Find("MenuUI");
        if (canvas == null) { result.LogError("Canvas not found."); return; }

        var components = canvas.GetComponents();
        bool hasPointable = false;
        foreach (var c in components)
        {
            if (c != null && c.GetType().Name.Contains("PointableCanvas"))
                hasPointable = true;
        }

        if (hasPointable)
            result.Log("VR interaction verified: PointableCanvas present on '{0}'.", canvas.name);
        else
            result.LogError("VR interaction MISSING on '{0}'. Call meta_add_canvas_interaction_ray.", canvas.name);

        // Check scene-level Pointable Canvas Module
        var module = GameObject.Find("Pointable Canvas Module");
        if (module != null)
            result.Log("Pointable Canvas Module found in scene.");
        else
            result.LogWarning("Pointable Canvas Module not found. It should be auto-created by the Meta Unity extension MCP tool.");
    }
}

If buttons work in Editor but not on Quest device

Ensure the interaction rig exists in the scene:

meta_add_interactionrig  (no parameters)

This adds OVRInteractionComprehensive as a child of the Camera Rig, providing all hand/controller interactors. Requires OVRCameraRig to be in the scene first.

VR UI sizing reference

All sizes assume canvas scale of 0.001 (1 unit = 1mm).

Element sizes (minimum)

| Element | sizeDelta (units) | Physical size | |---|---|---| | Button (small) | 250 x 100 | 25cm x 10cm | | Button (standard) | 300 x 120 | 30cm x 12cm | | Button (large) | 400 x 150 | 40cm x 15cm | | Dropdown | 500 x 140 | 50cm x 14cm | | Slider | 600 x 80 | 60cm x 8cm | | Toggle | 120 x 120 | 12cm x 12cm | | Input Field | 800 x 140 | 80cm x 14cm | | Panel/Menu | 1200-2000 x 800-1400 | 1.2-2m x 0.8-1.4m |

Button spacing

Minimum 50 units (5cm) between interactive elements to prevent mis-clicks.

Font sizes (at 0.001 canvas scale)

| Distance | Minimum | Comfortable | Large/Title | |---|---|---|---| | 1.5m | 32pt | 40-48pt | 60-72pt | | 2.0m | 36pt | 48-56pt | 72-84pt | | 2.5m | 40pt | 52-64pt | 84-96pt | | 3.0m | 48pt | 64-72pt | 96-120pt |

Never use auto-sizing in VR. Never use legacy Text components — always TextMeshPro.

Colors

  • Text: off-white (0.9, 0.9, 0.9) or dark gray (0.1, 0.1, 0.1). Avoid pure white/black.
  • Backgrounds: dark (0.1, 0.1, 0.1, 0.95) or light (0.9, 0.9, 0.9, 0.95).
  • Contrast ratio: minimum 4.5:1, prefer 7:1+.
  • Prefer opaque backgrounds over transparent (cheaper to render).

Performance tips

  • Disable raycastTarget on non-interactive elements (labels, backgrounds, decorative images).
  • Split static and dynamic content onto separate canvases to minimize rebuilds.
  • Canvas rebuild cost should be = 250x100 units

[ ] Button spacing >= 50 units [ ] Text >= 48pt, raycastTarget = false on non-interactive text [ ] Validation script run and passed


### Display-only UI (HUD, score, labels)

[ ] TMP Essential Resources imported [ ] Canvas: renderMode = World Space [ ] Canvas: localScale = [0.001, 0.001, 0.001] [ ] All children: localScale = [1, 1, 1] [ ] All children: localPosition.z = 0 [ ] Text >= 48pt [ ] raycastTarget = false on all elements [ ] NO interaction components needed


## Integration with other skills

- **tmp-resources**: Use first to import TMP Essential Resources before creating any UI.
- **unity-placement**: Use for positioning canvases relative to other scene objects.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [meta-quest](https://github.com/meta-quest)
- **Source:** [meta-quest/agentic-tools](https://github.com/meta-quest/agentic-tools)
- **License:** Apache-2.0
- **Homepage:** https://developers.meta.com/horizon

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.