using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif #if MM_UI using UnityEngine.EventSystems; 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/MM Touch Repositionable Joystick")] 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(); _rectTransform = GetComponent(); _initialPosition = BackgroundCanvasGroup.GetComponent().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) { _targetOpacity = PressedOpacity; OnPointerDownEvent.Invoke(); _newPosition = ConvertToWorld(data.position); if (!WithinBounds()) { return; } BackgroundCanvasGroup.transform.position = _newPosition; SetNeutralPosition(_newPosition); _knobTransform.position = _newPosition; _initialZPosition = _newPosition.z; } /// /// Override OnDrag to handle repositionable joystick with rotated camera /// public override void OnDrag(PointerEventData eventData) { OnDragEvent.Invoke(); _newTargetPosition = ConvertToWorld(eventData.position); Vector3 localDelta = TransformToLocalSpace(_newTargetPosition - _neutralPosition); localDelta = Vector2.ClampMagnitude(localDelta, ComputedMaxRange); if (!HorizontalAxisEnabled) { localDelta.x = 0; } if (!VerticalAxisEnabled) { localDelta.y = 0; } RawValue.x = EvaluateInputValue(localDelta.x); RawValue.y = EvaluateInputValue(localDelta.y); _newTargetPosition = _neutralPosition + TransformToWorldSpace(localDelta); _newJoystickPosition = _newTargetPosition; _newJoystickPosition.z = _initialZPosition; _knobTransform.position = _newJoystickPosition; } /// /// 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; } Vector2 screenPoint = _newPosition; if (ParentCanvasRenderMode == RenderMode.ScreenSpaceCamera && TargetCamera != null) { screenPoint = TargetCamera.WorldToScreenPoint(_newPosition); } return RectTransformUtility.RectangleContainsScreenPoint(_rectTransform, screenPoint, TargetCamera); } /// /// 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, this.transform.forward, ComputedMaxRange); } else { Handles.DrawWireDisc(this.transform.position, this.transform.forward, ComputedMaxRange); } } else { Handles.DrawWireDisc(_neutralPosition, this.transform.forward, ComputedMaxRange); } } #endif } } #endif