using System; using UnityEngine; [Serializable] public class Joystick : MonoBehaviour { [NonSerialized] private static Joystick[] joysticks; [NonSerialized] private static bool enumeratedJoysticks; [NonSerialized] private static float tapTimeDelta = 0.3f; public bool touchPad; public Rect touchZone; public Vector2 deadZone = Vector2.zero; public bool normalize; public Vector2 position; public int tapCount; private int lastFingerId = -1; private float tapTimeWindow; private Vector2 fingerDownPos; private float fingerDownTime; private float firstDeltaTime = 0.5f; private Rect defaultRect; private Boundary guiBoundary = new Boundary(); private Vector2 guiTouchOffset; private Vector2 guiCenter; void Start() { defaultRect = new Rect( Screen.width * 0.1f, Screen.height * 0.1f, 128, 128 ); if (touchPad) { touchZone = defaultRect; return; } guiTouchOffset.x = defaultRect.width * 0.5f; guiTouchOffset.y = defaultRect.height * 0.5f; guiCenter.x = defaultRect.x + guiTouchOffset.x; guiCenter.y = defaultRect.y + guiTouchOffset.y; guiBoundary.min.x = defaultRect.x - guiTouchOffset.x; guiBoundary.max.x = defaultRect.x + guiTouchOffset.x; guiBoundary.min.y = defaultRect.y - guiTouchOffset.y; guiBoundary.max.y = defaultRect.y + guiTouchOffset.y; } public void Disable() { gameObject.SetActive(false); enumeratedJoysticks = false; } public void ResetJoystick() { lastFingerId = -1; position = Vector2.zero; defaultRect.x = guiCenter.x - guiTouchOffset.x; defaultRect.y = guiCenter.y - guiTouchOffset.y; } public bool IsFingerDown() { return lastFingerId != -1; } public void LatchedFinger(int fingerId) { if (lastFingerId == fingerId) { ResetJoystick(); } } void Update() { if (!enumeratedJoysticks) { joysticks = FindObjectsOfType(); enumeratedJoysticks = true; } int count = Input.touchCount; if (tapTimeWindow > 0) { tapTimeWindow -= Time.deltaTime; } else { tapCount = 0; } if (count == 0) { ResetJoystick(); } else { for (int i = 0; i < count; i++) { Touch touch = Input.GetTouch(i); Vector2 guiTouchPos = touch.position - guiTouchOffset; bool shouldLatchFinger = false; if (touchPad) { if (touchZone.Contains(touch.position)) { shouldLatchFinger = true; } } else { Rect touchRect = new Rect( guiCenter.x - guiTouchOffset.x, guiCenter.y - guiTouchOffset.y, guiTouchOffset.x * 2, guiTouchOffset.y * 2 ); if (touchRect.Contains(touch.position)) { shouldLatchFinger = true; } } if ( shouldLatchFinger && (lastFingerId == -1 || lastFingerId != touch.fingerId) ) { if (touchPad) { fingerDownPos = touch.position; fingerDownTime = Time.time; } lastFingerId = touch.fingerId; if (tapTimeWindow > 0) { tapCount++; } else { tapCount = 1; tapTimeWindow = tapTimeDelta; } foreach (Joystick j in joysticks) { if (j != this) { j.LatchedFinger(touch.fingerId); } } } if (lastFingerId == touch.fingerId) { if (touch.tapCount > tapCount) { tapCount = touch.tapCount; } if (touchPad) { position.x = Mathf.Clamp( (touch.position.x - fingerDownPos.x) / (touchZone.width / 2), -1, 1 ); position.y = Mathf.Clamp( (touch.position.y - fingerDownPos.y) / (touchZone.height / 2), -1, 1 ); } else { defaultRect.x = Mathf.Clamp( guiTouchPos.x, guiBoundary.min.x, guiBoundary.max.x ); defaultRect.y = Mathf.Clamp( guiTouchPos.y, guiBoundary.min.y, guiBoundary.max.y ); } if ( touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled ) { ResetJoystick(); } } } } if (!touchPad) { position.x = (defaultRect.x + guiTouchOffset.x - guiCenter.x) / guiTouchOffset.x; position.y = (defaultRect.y + guiTouchOffset.y - guiCenter.y) / guiTouchOffset.y; } float absoluteX = Mathf.Abs(position.x); float absoluteY = Mathf.Abs(position.y); if (absoluteX < deadZone.x) { position.x = 0; } else if (normalize) { position.x = Mathf.Sign(position.x) * (absoluteX - deadZone.x) / (1 - deadZone.x); } if (absoluteY < deadZone.y) { position.y = 0; } else if (normalize) { position.y = Mathf.Sign(position.y) * (absoluteY - deadZone.y) / (1 - deadZone.y); } } }