VirtualJoystick
Coding Style wiki
VirtualJoystick is the UGUI virtual joystick component (MonoBehaviour). It implements the UGUI event interfaces (IPointerDownHandler, IPointerUpHandler, IDragHandler), converts touch / mouse dragging into a Vector2 stick vector, and outputs it through the onStickInput callback; supports Fixed / Floating stick types, Normalized / Delta vector modes, axis constraints, and dead-zone filtering (adjusted from annulusgames - EnhancedOnScreenStick).
| Namespace | OxGKit.VirtualJoystick |
| Type | public class VirtualJoystick : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler |
| Source | VirtualJoystick.cs |
using OxGKit.VirtualJoystick;
Attention The component is decorated with [AddComponentMenu("OxGKit/VirtualJoystick/VirtualJoystick")] and [RequireComponent(typeof(RectTransform), typeof(Image))] — the host object itself is the touch area (the image can be made transparent to act as the touch surface).
Quick Start
using OxGKit.VirtualJoystick;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField]
private VirtualJoystick _joystick;
private Vector2 _move;
private void Awake()
{
// Configure the joystick (can also be set directly in the Inspector)
this._joystick.stickType = StickType.Floating;
this._joystick.stickVectorMode = StickVectorMode.Normalized;
this._joystick.axisConstraint = AxisConstraint.Both;
this._joystick.deadZone = 0.1f;
// Subscribe to the stick output callback (returns Vector2.zero on release)
this._joystick.onStickInput += this._OnStickInput;
}
private void _OnStickInput(Vector2 v2)
{
this._move = v2;
}
private void OnDestroy()
{
// Unsubscribe when the object is destroyed
this._joystick.onStickInput -= this._OnStickInput;
}
}
General Rules
Requirements
- The component must live under a Canvas:
Awakefetches the parent Canvas (used for scale-factor and position conversion) — if none is found, it logs an error and disables itself (enabled = false). - The scene needs an EventSystem, and the component's
Imagemust be raycastable (not blocked byCanvasGroup.blocksRaycasts = false, etc.), otherwise no pointer events are received. - The component is purely UGUI-event driven — it does not depend on Unity New InputSystem's OnScreen controls and works with both Old and New Input backends.
Output Flow
- Press (OnPointerDown): For non-
Fixedtypes, the joystick background is repositioned to the press point; the press start position is recorded andOnDragruns once immediately. - Drag (OnDrag): The output vector is computed from "current position - press start position" (divided by
handleMovementRange * Canvas.scaleFactor), then the axis constraint, dead-zone filtering, and vector mode are applied in order; finally the vector is emitted throughonStickInputand the handle position is updated. - Release (OnPointerUp): The handle snaps back to center and
onStickInputreturnsVector2.zero.
Enums
StickVectorMode
public enum StickVectorMode
{
Normalized,
Delta
}
The stick vector output mode:
| Value | Description |
|---|---|
Normalized | Output is limited to -1.00 ~ 1.00 (vectors longer than 1 are normalized); suitable for movement control. |
Delta | Mimics Mouse Delta; output can exceed 1, suitable for camera-look control (scale it by your sensitivity). |
StickType
public enum StickType
{
Fixed = 0,
Floating = 1
}
The stick display type:
| Value | Description |
|---|---|
Fixed | The joystick stays at its original position. |
Floating | On every press, the joystick background is repositioned to the press point. |
AxisConstraint
public enum AxisConstraint
{
Both = 0,
Horizontal = 1,
Vertical = 2
}
Constrains the stick output direction:
| Value | Description |
|---|---|
Both | Outputs both axes. |
Horizontal | Outputs the horizontal axis only. |
Vertical | Outputs the vertical axis only. |
Members
Member Overview
| Member | Description |
|---|---|
| onStickInput | Callback invoked when the stick output vector changes (returns Vector2.zero on release). |
| stickVectorMode | The stick vector output mode (StickVectorMode). |
| stickType | The stick display type (StickType). |
| axisConstraint | Constrains the stick output direction (AxisConstraint). |
| handleMovementRange | The handle movement range (pixel radius, default 100). |
| deadZone | Dead-zone range (0 ~ 1); stick inputs below this value are treated as invalid. |
onStickInput
public Action<Vector2> onStickInput
Callback invoked when the stick output vector changes: it fires on every input change while dragging, and returns Vector2.zero on release.
this._joystick.onStickInput += (v2) => this._move = v2;
Important onStickInput is a plain Action<Vector2> delegate field — assigning with = replaces all existing subscriptions; prefer += / -= for subscribing and unsubscribing (and unsubscribe when the object is destroyed).
stickVectorMode
public StickVectorMode stickVectorMode { get; set; }
The stick vector output mode, default StickVectorMode.Normalized (see StickVectorMode).
stickType
public StickType stickType { get; set; }
The stick display type, default StickType.Fixed (see StickType).
axisConstraint
public AxisConstraint axisConstraint { get; set; }
Constrains the stick output direction, default AxisConstraint.Both (see AxisConstraint).
handleMovementRange
public float handleMovementRange { get; set; }
The handle movement range (pixel radius, default 100); the output vector is computed as "drag delta / (handleMovementRange * Canvas.scaleFactor)".
Attention The movement range is measured in pixels and affected by the Canvas scaleFactor — test at your target resolutions / aspect ratios.
deadZone
public float deadZone { get; set; }
Dead-zone range (0 ~ 1, default 0); output vectors with a magnitude below this value are treated as invalid and emit Vector2.zero (prevents accidental touches).
Reminder The dead zone is a fraction of the movement range — values around 0.05 ~ 0.15 filter accidental touches without hurting responsiveness.
Inspector Fields
The following serialized fields can only be configured in the Inspector (no public properties); you can import the VirtualJoystickUI Prefab through Package Manager -> Samples (pre-wired, see the Module Intro):
| Field (Inspector) | Description |
|---|---|
Background (_background) | The joystick background UI (RectTransform, stick base image) — must be assigned. |
Handle (_handle) | The joystick handle UI (RectTransform, the draggable knob) — must be assigned. |
Show Only When Pressed (_showOnlyWhenPressed) | Whether to show the joystick background only while pressed (hidden at Awake, shown on press, hidden on release); pairs well with the Floating type. |
UGUI Event Callbacks
Method Overview
| Method | Description |
|---|---|
| OnPointerDown | Invoked on press (repositioning and initial output). |
| OnPointerUp | Invoked on release (handle snaps back and output resets to zero). |
| OnDrag | Invoked while dragging (computes and emits the stick vector). |
Attention These methods are the UGUI event interface implementations (IPointerDownHandler, IPointerUpHandler, IDragHandler) and are driven automatically by the EventSystem — you normally do not call them manually.
OnPointerDown
public void OnPointerDown(PointerEventData eventData)
Invoked on press: shows the joystick background (if hidden); for non-Fixed types the background is repositioned to the press point; records the press start position and runs OnDrag once immediately.
OnPointerUp
public void OnPointerUp(PointerEventData eventData)
Invoked on release: the handle snaps back to center; if Show Only When Pressed is enabled, the joystick background is hidden; and Vector2.zero is returned through onStickInput.
OnDrag
public void OnDrag(PointerEventData eventData)
Invoked while dragging: computes the output vector from the press start position, applies the axis constraint, dead-zone filtering, and vector mode in order, emits it through onStickInput, and updates the handle position (visually clamped to handleMovementRange).