chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
152
Assets/Scripts/Input/MobileOnScreenStick.cs
Normal file
152
Assets/Scripts/Input/MobileOnScreenStick.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user