Luna UI provides utilities for coordinate conversion between screen space and world space UI through UITKCoordinateUtility and UITKWorldSpace.
A static utility class for coordinate conversions in UI Toolkit, supporting both ScreenSpace and WorldSpace UI panels.
Determines whether a VisualElement or IPanel belongs to a world-space panel.
public static bool IsWorldSpace(VisualElement visualElement)
public static bool IsWorldSpace(IPanel panel)Example:
bool isWorldSpace = UITKCoordinateUtility.IsWorldSpace(myElement);
if (isWorldSpace)
{
// Handle world-space specific logic
}Converts an element's worldBound to panel-local coordinates.
public static Rect WorldBoundToLocalBound(
VisualElement element,
VisualElement panelRoot,
bool worldSpace
)| Parameter | Description |
|---|---|
| element | The element to get bounds for |
| panelRoot | The root element of the panel |
| worldSpace | Whether the panel is world-space |
Returns: Rect in panel-local pixel coordinates.
Example:
var root = _uiDocument.rootVisualElement;
var button = root.Q<Button>("MyButton");
bool isWorldSpace = UITKCoordinateUtility.IsWorldSpace(button);
Rect localBounds = UITKCoordinateUtility.WorldBoundToLocalBound(
button,
root,
isWorldSpace
);Converts a pointer/mouse position to panel-local pixel coordinates.
public static Vector2 PointerToLocalPosition(
Vector2 pointerPosition,
IPanel panel,
VisualElement referenceElement,
bool invertY,
bool worldSpace
)| Parameter | Description |
|---|---|
| pointerPosition | Screen position of the pointer |
| panel | The UI panel |
| referenceElement | Reference element for coordinate mapping |
| invertY | Whether to invert Y-axis (usually true for WorldSpace) |
| worldSpace | Whether the panel is world-space |
Example:
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;
}Converts a world-space position to panel-local pixel coordinates when you already have the reference bounds.
public static Vector2 WorldToLocalPosition(
Vector2 worldPosition,
IPanel panel,
Rect referenceWorldBound,
Rect referenceLocalBound,
bool worldSpace
)Gets the appropriate bounds for an element based on panel context.
public static Rect GetPanelBounds(VisualElement element, bool worldSpace)localBoundworldBoundA MonoBehaviour component that sets up a UI Toolkit panel for world-space rendering on a 3D object.
PanelSettings instance for the UIDocumentRenderTexture for the UIUITKWorldSpace to the parent objectHierarchy:
WorldSpaceUI (UITKWorldSpace)
├── UICanvas (UIDocument)
└── Quad (MeshRenderer)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!");
}
}The default RenderTexture is 256x256 pixels. Modify the component to change resolution:
// In UITKWorldSpace.cs, you can change:
_renderTexture = new RenderTexture(512, 512, 16, RenderTextureFormat.ARGB32);The DragAndDropManipulator uses these utilities internally:
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);
}
}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;
}
}worldBound is in screen pixelsworldBound is in world units (meters)| Property | ScreenSpace | WorldSpace |
|---|---|---|
| worldBound | Screen pixels | World units |
| localBound | Panel pixels | Panel pixels |
| Y-axis | Down positive | Up positive |
IsWorldSpace() to detect the panel type before conversionsinvertY: true for most WorldSpace pointer conversionsSettings
Theme
Light
Contrast
Material
Dark
Dim
Material Dark
System
Sidebar(Light & Contrast only)
Font Family
DM Sans
Wix
Inclusive Sans
AR One Sans
Direction