Files
DeviantMobile-Rohan/Assets/Scripts/Input/MobileOnScreenStick.cs

153 lines
5.4 KiB
C#

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.EnhancedTouch;
using UnityEngine.InputSystem.OnScreen;
using UnityEngine.InputSystem.Layouts;
using EnhancedTouchSupport = UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport;
namespace DeviantMobile.Input
{
// Drag-based on-screen stick that writes a Vector2 to the Input System.
// Bind your Move action to the same control path (e.g. <Gamepad>/leftStick).
[AddComponentMenu("Input/Mobile On-Screen Stick")]
public class MobileOnScreenStick : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler
{
[Header("Platform / Visibility")]
[Tooltip("If true, on-screen stick will be visible when running in the Editor (useful for testing). If false, controls are hidden in editor/desktop builds and only shown on mobile platforms.")]
[SerializeField] private bool allowShowInEditor = false;
// Helper to determine whether on-screen controls should be active for the current platform.
private bool ShouldShowOnScreenControls()
{
if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
return true;
if (Application.isEditor && allowShowInEditor)
return true;
return false;
}
[Header("Control Path")]
[Tooltip("Input System control path to write the Vector2 to. Commonly '<Gamepad>/leftStick'.")]
[SerializeField, InputControl(layout = "Vector2")] private string m_ControlPath = "<Gamepad>/leftStick";
protected override string controlPathInternal
{
get => m_ControlPath;
set => m_ControlPath = value;
}
[Header("UI References")]
[SerializeField] private RectTransform background; // Joystick background area
[SerializeField] private RectTransform handle; // Joystick handle/knob
[Header("Behavior")]
[Tooltip("Max radius in pixels the handle can travel from center.")]
[SerializeField] private float maxRadius = 100f;
[Tooltip("Deadzone as a fraction of radius (0..1).")]
[Range(0f, 0.5f)] [SerializeField] private float deadZone = 0.1f;
[Tooltip("Return handle to center on pointer up.")]
[SerializeField] private bool snapBackOnRelease = true;
private bool _pressed;
private Vector2 _centerLocal;
void Awake()
{
if (background == null)
background = GetComponent<RectTransform>();
if (handle == null && background != null && background.childCount > 0)
handle = background.GetChild(0) as RectTransform;
// Disable the UI for non-mobile platforms by default.
gameObject.SetActive(ShouldShowOnScreenControls());
}
void OnEnable()
{
// Ensure the Enhanced Touch pipeline is active so pointer/touch events map correctly to OnScreenControl.
EnhancedTouchSupport.Enable();
}
void OnDisable()
{
EnhancedTouchSupport.Disable();
}
public void OnPointerDown(PointerEventData eventData)
{
_pressed = true;
CacheCenter();
OnDrag(eventData); // update immediately
}
public void OnDrag(PointerEventData eventData)
{
if (background == null)
return;
RectTransformUtility.ScreenPointToLocalPointInRectangle(background, eventData.position, eventData.pressEventCamera, out var localPoint);
var delta = localPoint - _centerLocal;
// Clamp to radius
if (delta.magnitude > maxRadius)
delta = delta.normalized * maxRadius;
// Normalize to [-1,1]
var norm = (maxRadius > 0f) ? (delta / maxRadius) : Vector2.zero;
// Apply deadzone
var mag = norm.magnitude;
if (mag < deadZone)
norm = Vector2.zero;
else if (mag > 1f)
norm = norm.normalized;
// Move handle visually
if (handle != null)
handle.anchoredPosition = delta;
// Send to Input System
SendValueToControl(norm);
}
public void OnPointerUp(PointerEventData eventData)
{
_pressed = false;
if (snapBackOnRelease)
{
ResetHandle();
}
SendValueToControl(Vector2.zero);
}
private void CacheCenter()
{
if (background != null)
{
// Center is the pivot point local position (0 if centered pivot). Use rect center for safety.
_centerLocal = background.rect.center;
}
else
{
_centerLocal = Vector2.zero;
}
}
private void ResetHandle()
{
if (handle != null)
handle.anchoredPosition = Vector2.zero;
}
#if UNITY_EDITOR
void OnValidate()
{
if (background == null)
background = GetComponent<RectTransform>();
if (handle == null && background != null && background.childCount > 0)
handle = background.GetChild(0) as RectTransform;
}
#endif
}
}