A responsive, event-driven virtual joystick component for the 'Stride Game Engine', designed for mobile projects that require smooth, intuitive directional input. This implementation uses two companion libraries for performance and ergonomics:
- Sharp — general-purpose utilities
- Sharp.Collections — lightweight, allocation-friendly collections
The joystick integrates seamlessly with Stride’s UI system and provides both absolute and relative directional input, angle tracking, and radius information.
-
'Absolute & Relative Input' Provides normalized input vectors in both world-aligned and object-relative modes.
-
'Angle & Radius Output' Emits angles in radians and degrees, plus a normalized radius (0–1).
-
'Event-Driven Architecture' Subscribe to:
- StartedDragging
- StoppedDragging
- AbsoluteInputChanged
- RelativeInputChanged
- RadiusChanged
- AbsoluteAngleChanged
- RelativeAngleChanged
-
'Resolution-Independent UI' Automatically scales and repositions UI elements when the window size changes.
-
'High-Performance Internals' Uses:
- CommunityToolkit.HighPerformance for Span-based iteration
- Sharp for functional helpers (Value, Reference, etc.)
- Sharp.Collections for custom event handler lists
-
'Drop-in Stride UI Integration' Works with Stride’s UIComponent, Canvas, and ImageElement.
Install or include the following libraries in your Stride project:
- 'Sharp'
- 'Sharp.Collections'
You can add them manually (NuGet packages are not available yet).
Place the following files anywhere inside your Stride game project:
- VirtualJoystick.cs
- IVirtualJoystick.cs
- Angle.cs
- UIElementExtensions.cs
Your UI layout must contain the following elements:
'Surface' — root Canvas
├── 'Zone' — the interactive area (Canvas)
├──── 'Threshold' — outer ring image (ContentDecorator)
└────── 'Thumbstick' — inner stick image
These elements must be named exactly:
Zone
Threshold
Thumbstick
The script automatically locates them using FindVisualChildOfType.
Attach the VirtualJoystick script to the same entity that contains your UIComponent.
public override void Start()
{
var joystick = Entity.Get<IVirtualJoystick>();
joystick.AbsoluteInputChanged += OnAbsoluteInput;
joystick.RelativeInputChanged += OnRelativeInput;
joystick.StartedDragging += pos => { /``` ... ```/ };
joystick.StoppedDragging += pos => { /``` ... ```/ };
}private void OnRelativeInput(Vector2 input)
{
var direction = new Vector3(input.X, 0, input.Y);
CharacterComponent.Move(direction);
}Normalized vector based on thumbstick position: (-1..1, -1..1)
Same as above, but rotated by the yaw of 'RelativeObject'.
Distance from center (0–1).
- AbsoluteAngleInRadians
- AbsoluteAngleInDegrees
- RelativeAngleInRadians
- RelativeAngleInDegrees
float angle = joystick.AbsoluteAngleInDegrees;
float radius = joystick.Radius;
Vector2 input = joystick.AbsoluteInput;- References — lightweight event listener lists
- Reference — optional single delegate
- Value — optional value wrapper
- Span-based iteration over Stride pointer events
- DangerousGetReferenceAt for zero-allocation access
- Absolute positioning via SetCanvasAbsolutePosition
- Custom resolution scaling logic
The joystick:
- Tracks previous and current resolution
- Scales UI elements proportionally
- Recomputes absolute positions on window resize
- Maintains consistent feel across devices
UI Entity
├── UIComponent (with Page containing Surface/Zone/Threshold/Thumbstick)
└── VirtualJoystick (script)
Remember to enter correct page design resolution in VirtualJoystick parameters to keep correct scaling.
The VirtualJoystick provides a direction based on the orientation of your chosen relative object, allowing your e.g. character to move exactly where the VirtualJoystick points to within that object’s local space.



