using UnityEngine; using System.Collections; using UnityEngine.EventSystems; namespace CarbonInput { /// /// Touch control simulating a thumbstick. /// public class TouchStick : BaseTouchInput, IPointerDownHandler, IPointerUpHandler, IDragHandler { private const float NearZero = 0.0001f; /// /// Horizontal axis of this control. /// [Tooltip("Horizontal axis")] public CAxis X = CAxis.LX; /// /// Vertical axis of this control. /// [Tooltip("Vertical axis")] public CAxis Y = CAxis.LY; /// /// Touches inside this area will be handled by the stick. /// [Tooltip("Touches inside this area will be handled by the stick.")] public RectTransform TouchArea; /// /// Base of the joystick. /// [Tooltip("Base of the joystick.")] public RectTransform Base; /// /// Knob of the joystick. /// [Tooltip("Knob of the joystick.")] public RectTransform Stick; /// /// Maximum distance between center of base and center of stick. /// [Tooltip("Maximum distance between center of base and center of stick.")] [Range(20, 120)] public float Range = 60; /// /// Should the joystick disappear on release? /// [Tooltip("Should the joystick disappear on release?")] public bool HideOnRelease; /// /// If HideOnRelease is set to true, this value will determine after which time the joystick will start to fade out. /// [Tooltip("If HideOnRelease is set to true, this value will determine after which time the joystick will start to fade out.")] public float FadeoutDelay; /// /// If HideOnRelease is set to true, this value will determine how long the fadeout will last. /// [Tooltip("If HideOnRelease is set to true, this value will determine how long the fadeout will last.")] public float FadeoutTime = 1f; /// /// If the user moves to far away from the stick, should the stick follow? /// [Tooltip("If the user moves to far away from the stick, should the stick follow?")] public bool Movable; private CanvasRenderer[] childRenderer; void Start() { InitMapping(); childRenderer = GetComponentsInChildren(); if(HideOnRelease) Hide(false); } /// /// Shows this control. /// public void Show() { StopAllCoroutines(); SetOpacity(1f); } /// /// Hides this control. /// /// If true, the control will slowly fade out. public void Hide(bool fadeout) { StopAllCoroutines(); if(fadeout) StartCoroutine(FadeSequence()); else SetOpacity(0f); } /// /// Sets the opacity of this control and all children. /// /// private void SetOpacity(float opacity) { foreach(CanvasRenderer renderer in childRenderer) renderer.SetAlpha(opacity); } /// /// Coroutine used to slowly fadeout. /// /// private IEnumerator FadeSequence() { if(FadeoutDelay > 0) yield return new WaitForSeconds(FadeoutDelay); float opacity = 1f; float speed = 1f / FadeoutTime; while(opacity >= 0.0f) { opacity -= Time.deltaTime * speed; if(opacity < 0) opacity = 0; SetOpacity(opacity); yield return null; } } /// /// Sets the value of this stick in the and also sets the knob position. /// If is true, it will also follow the user. /// /// Touch position in world space private void UpdateStick(Vector2 pos) { // get direction in local space Vector2 direction = (pos - (Vector2)Base.position); direction.x /= Base.lossyScale.x; direction.y /= Base.lossyScale.y; float length = direction.magnitude; if(length < NearZero) { UpdateAxis(Vector2.zero); return; } if(length > Range) { if(Movable) { Vector2 delta = direction.normalized * (length - Range); Vector2 newPos = (Vector2)Base.localPosition + delta; newPos.x = Mathf.Clamp(newPos.x, TouchArea.rect.xMin, TouchArea.rect.xMax); newPos.y = Mathf.Clamp(newPos.y, TouchArea.rect.yMin, TouchArea.rect.yMax); Base.localPosition = newPos; } length = Range; } UpdateAxis(direction.normalized * (length / Range)); } /// /// Updates the . /// /// private void UpdateAxis(Vector2 axis) { if(Mapping == null) return; Stick.localPosition = axis * Range; Mapping[X] = axis.x; Mapping[Y] = -axis.y; // invert to match "normal" controller axis } public void OnPointerDown(PointerEventData data) { Show(); if(RectTransformUtility.RectangleContainsScreenPoint(Stick, data.position) || RectTransformUtility.RectangleContainsScreenPoint(Base, data.position)) { UpdateStick(data.position); } else if(Movable) { Base.position = data.position; UpdateAxis(Vector2.zero); } } public void OnPointerUp(PointerEventData data) { UpdateAxis(Vector2.zero); if(HideOnRelease) Hide(true); } public void OnDrag(PointerEventData data) { UpdateStick(data.position); } } }