EnvironmentRaycastManager functionality and a step-by-step placement workflow.EnvironmentRaycastManager component (raycasts, box placement, overlap checks).HitPointOutsideOfCameraFrustum or NoHit.Raycast: Shoots a virtual ray into the live depth buffer and reports the first intersection point. You’ll use this for pinpointing where on a surface to anchor your content.PlaceBox: Aligns an axis-aligned box (e.g., the bounds of a UI panel or furniture) with a detected flat surface, then checks whether that box would fit without colliding with real-world geometry.CheckBox: Performs an overlap test for a proposed bounding box, letting you quickly reject placements that would intersect walls, tables, or other obstacles.IsSupported: Returns true only on devices and runtimes where environment raycasting is available, so you can safely disable or fallback on unsupported platforms.// Cast a single ray against live depth data
bool Raycast(Ray ray, out EnvironmentRaycastHit hitInfo, float maxDistance = 100f);
// Try to place an axis-aligned box on a flat surface
bool PlaceBox(Ray ray, Vector3 boxSize, Vector3 upwards, out EnvironmentRaycastHit hitInfo);
// Check if an axis-aligned box overlaps the environment
bool CheckBox(Vector3 center, Vector3 halfExtents, Quaternion orientation);
// True if environment raycasts are supported on this device
public static bool IsSupported { get; }


using Meta.XR;
using UnityEngine;
public class InstantPlacementController : MonoBehaviour
{
public Transform rightControllerAnchor;
public GameObject prefabToPlace;
public EnvironmentRaycastManager raycastManager;
}
private void Update()
{
if (OVRInput.GetDown(OVRInput.RawButton.RIndexTrigger))
{
var ray = new Ray(
rightControllerAnchor.position,
rightControllerAnchor.forward
);
TryPlace(ray);
}
}
private void TryPlace(Ray ray)
{
if (raycastManager.Raycast(ray, out var hit))
{
var objectToPlace = Instantiate(prefabToPlace);
objectToPlace.transform.SetPositionAndRotation(
hit.point,
Quaternion.LookRotation(hit.normal, Vector3.up)
);
// If no MRUK component is present in the scene, we add an OVRSpatialAnchor component
// to the instantiated prefab to anchor it in the physical space and prevent drift.
if (MRUK.Instance?.IsWorldLockActive != true)
{
objectToPlace.AddComponent<OVRSpatialAnchor>();
}
}
}
☕ Full Instant Content Placement Script
using Meta.XR.MRUtilityKit;
using UnityEngine;
public class InstantPlacementController : MonoBehaviour
{
public Transform rightControllerAnchor;
public GameObject prefabToPlace;
public EnvironmentRaycastManager raycastManager;
private void Update()
{
if (OVRInput.GetDown(OVRInput.RawButton.RIndexTrigger))
{
var ray = new Ray(
rightControllerAnchor.position,
rightControllerAnchor.forward
);
TryPlace(ray);
}
}
private void TryPlace(Ray ray)
{
if (raycastManager.Raycast(ray, out var hit))
{
var objectToPlace = Instantiate(prefabToPlace);
objectToPlace.transform.SetPositionAndRotation(
hit.point,
Quaternion.LookRotation(hit.normal, Vector3.up)
);
if (MRUK.Instance?.IsWorldLockActive != true)
{
objectToPlace.AddComponent<OVRSpatialAnchor>();
}
}
}
}

PlaceBox to stick the panel. If no vertical surface is hit, CheckBox ensures the panel won’t intersect real-world geometry.
PlaceBox to align and fit a box (e.g. a panel, furniture bounds) onto a flat surface, and CheckBox to preflight collision checks without instantiating anything.// Cast a ray from your controller
var ray = new Ray(controller.position, controller.forward);
// Define the panel’s local size (width, height, depth)
Vector3 panelSize = new Vector3(0.5f, 0.1f, 0.3f);
// 'up' keeps the panel upright after placement
Vector3 up = Vector3.up;
if (_raycastManager.PlaceBox(ray, panelSize, up, out var hitInfo))
{
// Instantiate and orient the panel at the hit point
var panel = Instantiate(panelPrefab);
panel.transform.SetPositionAndRotation(
hitInfo.point,
Quaternion.LookRotation(hitInfo.normal, up)
);
}
else
{
Debug.Log("No suitable flat area found for panel.");
}
// Desired spawn position and cube half‑extents
Vector3 spawnPos = hitInfo.point;
Vector3 halfExtents = Vector3.one * 0.5f;
// Keep cube axis‑aligned
Quaternion orientation = Quaternion.identity;
if (_raycastManager.CheckBox(spawnPos, halfExtents, orientation))
{
Debug.Log("Cube overlaps environment—choose a different spot.");
}
else
{
Instantiate(cubePrefab, spawnPos, orientation);
}