Skip to main content

VirtualJoystick

Important Attention Reminder

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).

NamespaceOxGKit.VirtualJoystick
Typepublic class VirtualJoystick : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler
SourceVirtualJoystick.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​

  1. The component must live under a Canvas: Awake fetches the parent Canvas (used for scale-factor and position conversion) — if none is found, it logs an error and disables itself (enabled = false).
  2. The scene needs an EventSystem, and the component's Image must be raycastable (not blocked by CanvasGroup.blocksRaycasts = false, etc.), otherwise no pointer events are received.
  3. 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​

  1. Press (OnPointerDown): For non-Fixed types, the joystick background is repositioned to the press point; the press start position is recorded and OnDrag runs once immediately.
  2. 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 through onStickInput and the handle position is updated.
  3. Release (OnPointerUp): The handle snaps back to center and onStickInput returns Vector2.zero.

Enums​

StickVectorMode​

public enum StickVectorMode
{
Normalized,
Delta
}

The stick vector output mode:

ValueDescription
NormalizedOutput is limited to -1.00 ~ 1.00 (vectors longer than 1 are normalized); suitable for movement control.
DeltaMimics 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:

ValueDescription
FixedThe joystick stays at its original position.
FloatingOn 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:

ValueDescription
BothOutputs both axes.
HorizontalOutputs the horizontal axis only.
VerticalOutputs the vertical axis only.

Members​

Member Overview​

MemberDescription
onStickInputCallback invoked when the stick output vector changes (returns Vector2.zero on release).
stickVectorModeThe stick vector output mode (StickVectorMode).
stickTypeThe stick display type (StickType).
axisConstraintConstrains the stick output direction (AxisConstraint).
handleMovementRangeThe handle movement range (pixel radius, default 100).
deadZoneDead-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​

MethodDescription
OnPointerDownInvoked on press (repositioning and initial output).
OnPointerUpInvoked on release (handle snaps back and output resets to zero).
OnDragInvoked 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).