using UnityEngine; using UnityEngine.EventSystems; #if UNITY_EDITOR using UnityEditor; #endif namespace MoreMountains.Tools { /// /// Add this component to a UI rectangle and it'll act as a detection zone for a joystick. /// Note that this component extends the MMTouchJoystick class so you don't need to add another joystick to it. It's both the detection zone and the stick itself. /// [AddComponentMenu("More Mountains/Tools/Controls/MMTouchRepositionableJoystick")] public class MMTouchRepositionableJoystick : MMTouchJoystick, IPointerDownHandler { [MMInspectorGroup("Repositionable Joystick", true, 22)] /// the canvas group to use as the joystick's knob [Tooltip("the canvas group to use as the joystick's knob")] public CanvasGroup KnobCanvasGroup; /// the canvas group to use as the joystick's background [Tooltip("the canvas group to use as the joystick's background")] public CanvasGroup BackgroundCanvasGroup; /// if this is true, the joystick won't be able to travel beyond the bounds of the top level canvas [Tooltip("if this is true, the joystick won't be able to travel beyond the bounds of the top level canvas")] public bool ConstrainToInitialRectangle = true; /// if this is true, the joystick will return back to its initial position when released [Tooltip("if this is true, the joystick will return back to its initial position when released")] public bool ResetPositionToInitialOnRelease = false; protected Vector3 _initialPosition; protected Vector3 _newPosition; protected CanvasGroup _knobCanvasGroup; protected RectTransform _rectTransform; /// /// On Start, we instantiate our joystick's image if there's one /// protected override void Start() { base.Start(); // we store the detection zone's initial position _rectTransform = GetComponent(); _initialPosition = _rectTransform.position; } /// /// On init we set our knob transform /// public override void Initialize() { base.Initialize(); SetKnobTransform(KnobCanvasGroup.transform); _canvasGroup = KnobCanvasGroup; _initialOpacity = _canvasGroup.alpha; } /// /// When the zone is pressed, we move our joystick accordingly /// /// Data. public override void OnPointerDown(PointerEventData data) { base.OnPointerDown(data); // if we're in "screen space - camera" render mode if (ParentCanvasRenderMode == RenderMode.ScreenSpaceCamera) { _newPosition = TargetCamera.ScreenToWorldPoint(data.position); } // otherwise else { _newPosition = data.position; } _newPosition.z = this.transform.position.z; if (!WithinBounds()) { return; } // we define a new neutral position BackgroundCanvasGroup.transform.position = _newPosition; SetNeutralPosition(_newPosition); _knobTransform.position = _newPosition; } /// /// Returns true if the joystick's new position is within the bounds of the top level canvas /// /// protected virtual bool WithinBounds() { if (!ConstrainToInitialRectangle) { return true; } return RectTransformUtility.RectangleContainsScreenPoint(_rectTransform, _newPosition); } /// /// When the player lets go of the stick, we restore our stick's position if needed /// /// Event data. public override void OnPointerUp(PointerEventData eventData) { base.OnPointerUp(eventData); if (ResetPositionToInitialOnRelease) { BackgroundCanvasGroup.transform.position = _initialPosition; _knobTransform.position = _initialPosition; } } #if UNITY_EDITOR /// /// Draws gizmos if needed /// protected override void OnDrawGizmos() { if (!DrawGizmos) { return; } Handles.color = MMColors.Orange; if (!Application.isPlaying) { if (KnobCanvasGroup != null) { Handles.DrawWireDisc(KnobCanvasGroup.transform.position, Vector3.forward, ComputedMaxRange); } else { Handles.DrawWireDisc(this.transform.position, Vector3.forward, ComputedMaxRange); } } else { Handles.DrawWireDisc(_neutralPosition, Vector3.forward, ComputedMaxRange); } } #endif } }