using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using MalbersAnimations.Events;
using MalbersAnimations.Scriptables;
namespace MalbersAnimations
{
[HelpURL("https://malbersanimations.gitbook.io/animal-controller/mobile/mobile-joystick")]
[AddComponentMenu("Malbers/Input/Mobile Joystick")]
public class MobileJoystick : MonoBehaviour, IDragHandler, IPointerUpHandler, IPointerDownHandler
{
[Tooltip("Inverts the Horizontal value of the joystick")]
public bool invertX;
[Tooltip("Inverts the Vertical value of the joystick")]
public bool invertY; // Bollean to define whether or not the Y axis is inverted.
[Tooltip("If the Axis Magnitude is lower than this value then the Axis will zero out")]
public FloatReference deathpoint = new FloatReference(0.1f);
/// sensitivity for the X Axis
public FloatReference sensitivityX = new FloatReference(0.05f);
/// sensitivity for the Y Axis
public FloatReference sensitivityY = new FloatReference(0.05f);
[Tooltip("The Joystick Start position will be First click on the Area")]
public bool Dynamic = false;
[Tooltip("If the Joystick is not Moving it will stop moving the Axis ")]
public BoolReference StopJoyStick = new BoolReference( false);
// [Header("References")]
/// Is the Joystick is being pressed.
public BoolReference pressed;
/// Variable to Store the XAxis and Y Axis of the JoyStick
public Vector2Reference axisValue;
private Vector2 DeltaDrag;
// [Header("Events")]
public UnityEvent OnJoystickDown = new UnityEvent();
public UnityEvent OnJoystickUp = new UnityEvent();
public Vector2Event OnAxisChange = new Vector2Event();
public FloatEvent OnXAxisChange = new FloatEvent();
public FloatEvent OnYAxisChange = new FloatEvent();
public BoolEvent OnJoystickPressed = new BoolEvent();
private float BgXSize;
private float BgYSize;
public bool AxisEditor = true;
public bool EventsEditor = true;
public bool ReferencesEditor = true;
[Tooltip("If true, then the joystick will not use the starting position as guide for calculating the movement axis")]
public bool m_Drag = false;
/// Lets use it to see if the mouse has not moved.Zero means that it moves
private int DragRegistered;
/// JoyStick Background
public Graphic bg;
/// Drag Area Background
public Graphic DragRect;
/// JoyStick Button
public Graphic Jbutton;
/// Mutliplier to
private const float mult = 3;
// private Transform m_Cam;
public bool Pressed
{
get => pressed;
set { OnJoystickPressed.Invoke(pressed.Value = value); }
}
public Vector2 AxisValue
{
get => axisValue;
set
{
if (invertX) value.x *= -1;
if (invertY) value.y *= -1;
axisValue.Value = value;
}
}
public float XAxis => AxisValue.x;
public float YAxis => AxisValue.y;
void Start()
{
if (bg == null) bg = GetComponent();
if (Jbutton == null) Jbutton = transform.GetChild(0).GetComponent();
if (DragRect == null) DragRect = GetComponent();
BgXSize = bg.rectTransform.sizeDelta.x;
BgYSize = bg.rectTransform.sizeDelta.y;
}
void Update()
{
if (Pressed)
{
OnAxisChange.Invoke(axisValue);
OnXAxisChange.Invoke(axisValue.Value.x);
OnYAxisChange.Invoke(axisValue.Value.y);
DragRegistered++;
}
if (StopJoyStick.Value && DragRegistered > 1)
{
AxisValue = Vector3.zero;
}
}
// When draging is occuring this will be called every time the cursor is moved.
public virtual void OnDrag(PointerEventData Point)
{
Vector2 TargetAxis = Vector2.zero; ;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(bg.rectTransform, Point.position, Point.pressEventCamera, out Vector2 pos))
{
if (!m_Drag || Dynamic)
{
pos.x /= BgXSize; // Get the Joystick position on the 2 axes based on the Bg position.
pos.y /= BgYSize; // Get the Joystick position on the 2 axes based on the Bg position.
TargetAxis = new Vector3(pos.x * mult * sensitivityX, pos.y * mult * sensitivityY); // Position is relative to the Bg.
TargetAxis = (TargetAxis.magnitude > 1.0f ? TargetAxis.normalized : TargetAxis);
Vector2 JButtonPos = new Vector2(TargetAxis.x * (BgXSize / mult), TargetAxis.y * (BgYSize / mult));
Jbutton.rectTransform.anchoredPosition = JButtonPos;
}
else
{
Jbutton.rectTransform.anchoredPosition = pos;
var relative = pos - DeltaDrag;
TargetAxis =
new Vector3(relative.x * sensitivityX * Screen.width * 0.001f, relative.y * sensitivityY * 0.001f * Screen.height); // Position is relative to the Bg.
DeltaDrag = pos;
}
}
DragRegistered = 0;
if (TargetAxis.magnitude <= deathpoint)
{
AxisValue = Vector2.zero;
}
else
{
AxisValue = TargetAxis;
}
//OnAxisChange.Invoke(axisValue);
//OnXAxisChange.Invoke(axisValue.Value.x);
//OnYAxisChange.Invoke(axisValue.Value.y);
}
// When the virtual analog's press occured this will be called.
public virtual void OnPointerDown(PointerEventData Point)
{
OnJoystickDown.Invoke();
Pressed = true;
DeltaDrag = Vector2.zero;
if (Dynamic && !m_Drag)
{
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(DragRect.rectTransform, Point.position, Point.pressEventCamera, out Vector2 DeltaDrag))
{
DeltaDrag.x -= DragRect.rectTransform.sizeDelta.x; // Get the Joystick Correct X Position
DeltaDrag.y -= DragRect.rectTransform.sizeDelta.y; // Get the Joystick Correct X Position
bg.rectTransform.anchoredPosition = DeltaDrag;
}
}
else
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(bg.rectTransform, Point.position, Point.pressEventCamera, out DeltaDrag);
}
OnDrag(Point);
}
// When the virtual analog's release occured this will be called.
public virtual void OnPointerUp(PointerEventData _)
{
OnJoystickUp.Invoke();
Pressed = false;
AxisValue = Vector2.zero;
Jbutton.rectTransform.anchoredPosition = Vector3.zero;
DeltaDrag = Vector2.zero;
OnAxisChange.Invoke(axisValue);
OnXAxisChange.Invoke(axisValue.Value.x);
OnYAxisChange.Invoke(axisValue.Value.y);
}
}
}