using UnityEngine; using UnityEngine.EventSystems; public class Joystick : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler, IBeginDragHandler, IEndDragHandler, IPointerClickHandler { public float horizontal { get { return (_snapX) ? SnapFloat(_input.x, AxisOptions.Horizontal) : _input.x; } } public float vertical { get { return (_snapY) ? SnapFloat(_input.y, AxisOptions.Vertical) : _input.y; } } public Vector2 direction { get { return new Vector2(horizontal, vertical); } } public float handleRange { get { return _handleRange; } set { _handleRange = Mathf.Abs(value); } } public float invalidRange { get { return _invalidRange; } set { _invalidRange = Mathf.Abs(value); } } public AxisOptions axisOptions { get { return _axisOptions; } set { _axisOptions = value; } } public bool snapX { get { return _snapX; } set { _snapX = value; } } public bool snapY { get { return _snapY; } set { _snapY = value; } } [SerializeField] private float _handleRange = 1; [SerializeField] private float _invalidRange = 0; [SerializeField] private AxisOptions _axisOptions = AxisOptions.Both; [SerializeField] private bool _snapX = false; [SerializeField] private bool _snapY = false; [SerializeField] protected RectTransform background = null; [SerializeField] protected RectTransform handle = null; [SerializeField] protected RectTransform indicator = null; private RectTransform _baseRect = null; private Canvas _canvas; private Camera _camera; private Vector2 _input = Vector2.zero; private float _pointTime; protected int _dragPointerId = int.MaxValue; private float _dragPointTime; private Vector2[] _dragPosition = new Vector2[2]; private bool _dragInvalid; private void OnEnable() { #if UNITY_WEBGL G.KPlatform.ApplicationFocusEvent += OnApplicationFocusEvent; #endif } private void OnDisable() { #if UNITY_WEBGL G.KPlatform.ApplicationFocusEvent -= OnApplicationFocusEvent; #endif } void OnApplicationFocusEvent(bool focus) { if (focus) _dragPointerId = int.MaxValue; } protected virtual void Start() { handleRange = _handleRange; invalidRange = _invalidRange; _baseRect = GetComponent(); _canvas = GetComponentInParent(); if (_canvas == null) Debug.LogError("The Joystick is not placed inside a canvas"); //cam = null; if (_canvas.renderMode == RenderMode.ScreenSpaceCamera) _camera = _canvas.worldCamera; Vector2 center = new Vector2(0.5f, 0.5f); background.pivot = center; handle.anchorMin = center; handle.anchorMax = center; handle.pivot = center; handle.anchoredPosition = Vector2.zero; } public virtual void OnPointerDown(PointerEventData eventData) { _pointTime = Time.unscaledTime; //OnDrag(eventData); } public virtual void OnPointerUp(PointerEventData eventData) { var magn = _input.magnitude; _input = Vector2.zero; handle.anchoredPosition = Vector2.zero; indicator.gameObject.SetActive(false); } public void OnPointerClick(PointerEventData eventData) { if (_dragPointerId != eventData.pointerId) { if (Time.unscaledTime - _pointTime < 0.36f) { OnShortTap(); } } //OnShortTap(eventData.position); } public virtual void OnBeginDrag(PointerEventData eventData) { if (_dragPointerId == int.MaxValue) { _dragPointerId = eventData.pointerId; _dragPointTime = Time.unscaledTime; _dragInvalid = true; } } public virtual void OnEndDrag(PointerEventData eventData) { if (_dragPointerId == eventData.pointerId) { _dragPointerId = int.MaxValue; if (_dragInvalid) { OnShortTap(); } else { var iMagn = (eventData.position - eventData.pressPosition).sqrMagnitude; var wMagn = (eventData.position - _dragPosition[0]).sqrMagnitude; if ((iMagn > 900f && Time.unscaledTime - _dragPointTime < 0.3f) || wMagn > 450f) { OnArrowAction(); } } } } public void OnDrag(PointerEventData eventData) { if (_dragPointerId == eventData.pointerId) { _dragPosition[0] = _dragPosition[1]; _dragPosition[1] = eventData.position; //Vector2 position = RectTransformUtility.WorldToScreenPoint(_camera, background.position); Vector2 radius = background.sizeDelta / 2f; _input = (eventData.position - eventData.pressPosition) / (radius * _canvas.scaleFactor); FormatInput(); HandleInput(_input.magnitude, _input.normalized, radius, _camera); handle.anchoredPosition = _input * radius * _handleRange; } } protected virtual void HandleInput(float magnitude, Vector2 normalised, Vector2 radius, Camera cam) { if (magnitude > _invalidRange) { _dragInvalid = false; if (magnitude > 1) _input = normalised; if (indicator) { indicator.gameObject.SetActive(true); indicator.right = _input; } } else { _input = Vector2.zero; if (indicator) { indicator.gameObject.SetActive(false); } } } private void FormatInput() { if (_axisOptions == AxisOptions.Horizontal) _input = new Vector2(_input.x, 0f); else if (_axisOptions == AxisOptions.Vertical) _input = new Vector2(0f, _input.y); } private float SnapFloat(float value, AxisOptions snapAxis) { if (value == 0) return value; if (_axisOptions == AxisOptions.Both) { float angle = Vector2.Angle(_input, Vector2.up); if (snapAxis == AxisOptions.Horizontal) { if (angle < 22.5f || angle > 157.5f) return 0; else return (value > 0) ? 1 : -1; } else if (snapAxis == AxisOptions.Vertical) { if (angle > 67.5f && angle < 112.5f) return 0; else return (value > 0) ? 1 : -1; } return value; } else { if (value > 0) return 1; if (value < 0) return -1; } return 0; } protected Vector2 ScreenPointToAnchoredPosition(Vector2 screenPosition) { Vector2 localPoint = Vector2.zero; if (RectTransformUtility.ScreenPointToLocalPointInRectangle(_baseRect, screenPosition, _camera, out localPoint)) { Vector2 pivotOffset = _baseRect.pivot * _baseRect.sizeDelta; return localPoint - (background.anchorMax * _baseRect.sizeDelta) + pivotOffset; } return Vector2.zero; } protected virtual void OnArrowAction() { } protected virtual void OnShortTap() { } protected virtual void OnLongTap() { } } public enum AxisOptions { Both, Horizontal, Vertical }