Week 5 Lab: Locomotion & Spatial UI

Virtual, Augmented and Spatial Computing

1 Lab Overview

Duration: 2 hours
Hardware: Quest 2, HTC Vive Pro
Software: Unity 2022 LTS, XR Interaction Toolkit 2.x
Deliverable: Lab journal entry + Unity scene (uploaded to VLE)


2 Learning Objectives

By the end of this lab you will be able to:

  • Implement teleportation locomotion in Unity
  • Implement a vignette comfort system for smooth locomotion
  • Build a basic wrist menu using world-space canvas
  • Compare diegetic and non-diegetic UI approaches

3 Part 1: Teleportation System (40 min)

3.1 Task 1.1 — Scene Setup

  1. Create a new Unity scene with a large flat plane (20m × 20m)
  2. Add several obstacles (cubes, ramps) to create an interesting space
  3. Add your XR Origin with action-based controllers

3.2 Task 1.2 — Teleportation Area

  1. Add a TeleportationArea component to the ground plane
  2. Add a TeleportationProvider to the XR Origin
  3. On the Right Hand controller’s XRRayInteractor:
    • Set Interaction Layer Mask to include Teleport layer
    • Enable Allow Anchor Control (lets user set facing direction)

3.3 Task 1.3 — Teleportation Anchor Points

  1. Create 5 TeleportationAnchor objects at specific points in the scene
  2. Set each anchor’s Teleport Trigger to OnSelectExited
  3. Add visual markers (cylinders) to show anchor positions

3.4 Task 1.4 — Test & Observe

Deploy to Quest 2. Test:

Test Observation
Teleport to area (free placement)
Teleport to anchor (fixed point)
Facing direction control
Teleport to invalid zone

Record: How does the fade transition affect disorientation?


4 Part 2: Smooth Locomotion with Comfort Features (30 min)

4.1 Task 2.1 — Add Smooth Locomotion

  1. Add ContinuousMoveProvider to the XR Origin
  2. Set Move Speed to 2 m/s
  3. Add ContinuousTurnProvider — set Turn Speed to 60°/s
  4. Replace with SnapTurnProvider — set Turn Amount to 45°

4.2 Task 2.2 — Vignette Effect

  1. Add a Post Processing Volume to the scene
  2. Create a script LocomotionVignette.cs:
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

public class LocomotionVignette : MonoBehaviour
{
    public ContinuousMoveProvider moveProvider;
    public float maxVignetteIntensity = 0.5f;

    private Material vignetteMaterial;

    void Update()
    {
        float speed = moveProvider.leftHandMoveAction.action.ReadValue<Vector2>().magnitude;
        // Apply vignette intensity proportional to speed
        // Implementation depends on your post-processing setup
        float intensity = Mathf.Lerp(0f, maxVignetteIntensity, speed);
        // Set vignette intensity here
    }
}
  1. Test with vignette on and off — note the difference in comfort

4.3 Task 2.3 — Comfort Comparison

Have a classmate test both locomotion modes. Record their comfort rating (1–5) after 2 minutes of each:

Mode Tester 1 Tester 2 Notes
Teleport
Smooth (no vignette)
Smooth (with vignette)
Smooth (snap turn)

5 Part 3: Wrist Menu (30 min)

5.1 Task 3.1 — Create the Menu Canvas

  1. Create a World Space Canvas (not Screen Space)
  2. Set canvas size to 0.2m × 0.15m
  3. Add 4 buttons: “Teleport Mode”, “Smooth Mode”, “Reset”, “Help”
  4. Set canvas Render Mode to World Space

5.2 Task 3.2 — Attach to Wrist

  1. In the XR Origin hierarchy, find the Left Hand controller
  2. Create an empty child object called WristMenuAnchor
  3. Position it on the inner wrist (approximately 0.05m from controller origin, rotated to face upward)
  4. Parent the canvas to WristMenuAnchor

5.3 Task 3.3 — Show/Hide Logic

Create WristMenuController.cs:

using UnityEngine;
using UnityEngine.XR;

public class WristMenuController : MonoBehaviour
{
    public GameObject menuCanvas;
    public float activationAngle = 60f;

    private InputDevice leftHand;

    void Start()
    {
        leftHand = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
        menuCanvas.SetActive(false);
    }

    void Update()
    {
        leftHand.TryGetFeatureValue(
            CommonUsages.deviceRotation, out Quaternion rotation
        );
        float wristAngle = rotation.eulerAngles.x;

        // Show menu when wrist is raised (palm up)
        bool shouldShow = wristAngle > activationAngle && wristAngle < 180f;
        menuCanvas.SetActive(shouldShow);
    }
}

5.4 Task 3.4 — Test the Wrist Menu

Deploy and test: - Does the menu appear reliably when wrist is raised? - Can you select buttons with the right hand ray? - Does it dismiss correctly?


6 Part 4: Diegetic vs Non-Diegetic UI Comparison (20 min)

6.1 Task 4.1 — Create a Score Display

Version A (Non-diegetic): Create a screen-space canvas with a score counter in the top-right corner.

Version B (Diegetic): Create a world-space canvas attached to a physical “scoreboard” object in the scene.

6.2 Task 4.2 — User Test

Have a classmate use both versions for 2 minutes each. Ask: 1. Which felt more natural? 2. Which was easier to read? 3. Which felt more immersive?


7 Submission

Upload to VLE by end of week:

    • Completed observation and comfort tables
    • Wrist menu screenshots
    • Diegetic vs non-diegetic user test results
    • Reflection: which UI approach would you use for your Assessment 2 prototype and why?

8 Troubleshooting

Problem Solution
Teleport ray not showing Check Interaction Layer Mask on both Ray Interactor and Teleportation Area
Wrist menu always visible Check rotation threshold values; print wristAngle to console
Canvas not visible in headset Ensure canvas is World Space, not Screen Space
Smooth locomotion too fast Reduce Move Speed in ContinuousMoveProvider