Coordinate Utilities

Luna UI provides utilities for coordinate conversion between screen space and world space UI through UITKCoordinateUtility and UITKWorldSpace.

UITKCoordinateUtility

A static utility class for coordinate conversions in UI Toolkit, supporting both ScreenSpace and WorldSpace UI panels.

IsWorldSpace

Determines whether a VisualElement or IPanel belongs to a world-space panel.

csharp
public static bool IsWorldSpace(VisualElement visualElement) public static bool IsWorldSpace(IPanel panel)

Example:

csharp
bool isWorldSpace = UITKCoordinateUtility.IsWorldSpace(myElement); if (isWorldSpace) { // Handle world-space specific logic }

WorldBoundToLocalBound

Converts an element's worldBound to panel-local coordinates.

csharp
public static Rect WorldBoundToLocalBound( VisualElement element, VisualElement panelRoot, bool worldSpace )
ParameterDescription
elementThe element to get bounds for
panelRootThe root element of the panel
worldSpaceWhether the panel is world-space

Returns: Rect in panel-local pixel coordinates.

  • For WorldSpace UI: Converts from world units (meters) to panel-local pixels
  • For ScreenSpace UI: Returns worldBound as-is (already correct)

Example:

csharp
var root = _uiDocument.rootVisualElement; var button = root.Q<Button>("MyButton"); bool isWorldSpace = UITKCoordinateUtility.IsWorldSpace(button); Rect localBounds = UITKCoordinateUtility.WorldBoundToLocalBound( button, root, isWorldSpace );

PointerToLocalPosition

Converts a pointer/mouse position to panel-local pixel coordinates.

csharp
public static Vector2 PointerToLocalPosition( Vector2 pointerPosition, IPanel panel, VisualElement referenceElement, bool invertY, bool worldSpace )
ParameterDescription
pointerPositionScreen position of the pointer
panelThe UI panel
referenceElementReference element for coordinate mapping
invertYWhether to invert Y-axis (usually true for WorldSpace)
worldSpaceWhether the panel is world-space

Example:

csharp
void OnPointerMove(PointerMoveEvent evt) { Vector2 localPos = UITKCoordinateUtility.PointerToLocalPosition( evt.position, _element.panel, _panelRoot, invertY: true, worldSpace: _isWorldSpace ); // Position element at pointer _tooltip.style.left = localPos.x; _tooltip.style.top = localPos.y; }

WorldToLocalPosition

Converts a world-space position to panel-local pixel coordinates when you already have the reference bounds.

csharp
public static Vector2 WorldToLocalPosition( Vector2 worldPosition, IPanel panel, Rect referenceWorldBound, Rect referenceLocalBound, bool worldSpace )

GetPanelBounds

Gets the appropriate bounds for an element based on panel context.

csharp
public static Rect GetPanelBounds(VisualElement element, bool worldSpace)
  • For WorldSpace UI: Returns localBound
  • For ScreenSpace UI: Returns worldBound

UITKWorldSpace

A MonoBehaviour component that sets up a UI Toolkit panel for world-space rendering on a 3D object.

How It Works

  1. Creates a unique PanelSettings instance for the UIDocument
  2. Creates a RenderTexture for the UI
  3. Assigns the RenderTexture to both the PanelSettings and a child Renderer's material

Setup

  1. Create a 3D object (e.g., Quad) with a Renderer
  2. Add a child GameObject with UIDocument
  3. Add UITKWorldSpace to the parent object

Hierarchy:

WorldSpaceUI (UITKWorldSpace) ├── UICanvas (UIDocument) └── Quad (MeshRenderer)

Usage

csharp
using UnityEngine; using UnityEngine.UIElements; using CupkekGames.Luna; public class WorldSpaceUIExample : MonoBehaviour { // UITKWorldSpace automatically sets up the panel // Just add your UIDocument as a child private UIDocument _uiDocument; void Start() { _uiDocument = GetComponentInChildren<UIDocument>(); var root = _uiDocument.rootVisualElement; // UI works normally - it's just rendered to a texture var button = root.Q<Button>("MyButton"); button.clicked += OnButtonClicked; } void OnButtonClicked() { Debug.Log("World space button clicked!"); } }

Configuration

The default RenderTexture is 256x256 pixels. Modify the component to change resolution:

csharp
// In UITKWorldSpace.cs, you can change: _renderTexture = new RenderTexture(512, 512, 16, RenderTextureFormat.ARGB32);

Example: Drag and Drop with World Space Support

The DragAndDropManipulator uses these utilities internally:

csharp
public class DragAndDropManipulator : Manipulator { private bool _worldSpace; private VisualElement _dragArea; protected void PointerMoveHandler(PointerMoveEvent evt) { // Convert pointer position to local coordinates Vector2 localPosition = UITKCoordinateUtility.PointerToLocalPosition( evt.position, target.panel, _dragArea, invertY: true, worldSpace: _worldSpace ); // Position the drag element _dragElement.style.left = localPosition.x; _dragElement.style.top = localPosition.y; } protected bool OverlapsTarget(VisualElement target) { // Get bounds in the correct coordinate space Rect dragBounds = UITKCoordinateUtility.WorldBoundToLocalBound( _dragElement, _dragArea, _worldSpace); Rect targetBounds = UITKCoordinateUtility.WorldBoundToLocalBound( target, _dragArea, _worldSpace); return dragBounds.Overlaps(targetBounds); } }

Example: World Space Tooltip

csharp
using UnityEngine; using UnityEngine.UIElements; using CupkekGames.Luna; public class WorldSpaceTooltip : MonoBehaviour { [SerializeField] private UIDocument _uiDocument; private VisualElement _tooltip; private bool _isWorldSpace; void Start() { var root = _uiDocument.rootVisualElement; _tooltip = root.Q<VisualElement>("Tooltip"); _isWorldSpace = UITKCoordinateUtility.IsWorldSpace(root); var target = root.Q<Button>("HoverTarget"); target.RegisterCallback<PointerMoveEvent>(OnPointerMove); } void OnPointerMove(PointerMoveEvent evt) { Vector2 localPos = UITKCoordinateUtility.PointerToLocalPosition( evt.position, _tooltip.panel, _uiDocument.rootVisualElement, invertY: true, worldSpace: _isWorldSpace ); // Offset tooltip from cursor _tooltip.style.left = localPos.x + 10; _tooltip.style.top = localPos.y + 10; } }

Understanding Coordinate Spaces

Screen Space UI

  • worldBound is in screen pixels
  • No coordinate conversion needed
  • Y increases downward from top-left

World Space UI

  • worldBound is in world units (meters)
  • Must convert to panel-local pixels
  • Y-axis may need inversion (world Y increases up, UI Y increases down)
PropertyScreenSpaceWorldSpace
worldBoundScreen pixelsWorld units
localBoundPanel pixelsPanel pixels
Y-axisDown positiveUp positive

Tips

  • Always use IsWorldSpace() to detect the panel type before conversions
  • Use invertY: true for most WorldSpace pointer conversions
  • The reference element should be the panel root for accurate mapping
  • WorldSpace UI requires a Renderer with appropriate material
  • Consider resolution when creating world space UI (higher res = more GPU cost)

Settings

Theme

Light

Contrast

Material

Dark

Dim

Material Dark

System

Sidebar(Light & Contrast only)

Light
Dark

Font Family

DM Sans

Wix

Inclusive Sans

AR One Sans

Direction

LTR
RTL