457 lines
14 KiB
C#
457 lines
14 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
#if USE_NEW_INPUT
|
|
using UnityEngine.InputSystem.OnScreen;
|
|
using UnityEngine.InputSystem.Layouts;
|
|
#endif
|
|
using Beyond;
|
|
using System.Collections;
|
|
using UnityStandardAssets.CrossPlatformInput;
|
|
using ButtonHandler = Beyond.ButtonHandler;
|
|
|
|
#if USE_NEW_INPUT
|
|
public class Joystick : OnScreenControl, IPointerDownHandler, IDragHandler, IPointerUpHandler
|
|
#else
|
|
public class Joystick : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
|
|
#endif
|
|
{
|
|
#if USE_NEW_INPUT
|
|
[InputControl(layout = "Vector2")]
|
|
[SerializeField]
|
|
protected string m_ControlPath;
|
|
#endif
|
|
[SerializeField]
|
|
protected ControlType m_ControlType = ControlType.Joystick;
|
|
public AnimationCurve inputModifier = AnimationCurve.Linear(-1f, -1f, 1f, 1f);
|
|
public ButtonHandler action;
|
|
private const float activeActionValue = 0.99f * 2f;
|
|
private const float actionStartLerpValue = 0.9f * 2f;
|
|
private CanvasGroup actionCanvasGroup;
|
|
private CanvasGroup activeActionCanvasGroup;
|
|
private Vector2 inputProcessed = Vector2.zero;
|
|
private Vector2 inputProcessedByModifier = Vector2.zero;
|
|
|
|
public string horizontalAxisName = "Horizontal"; // The name given to the horizontal axis for the cross platform input
|
|
public string verticalAxisName = "Vertical"; // The name given to the vertical axis for the cross platform input
|
|
|
|
protected CrossPlatformInputManager.VirtualAxis m_HorizontalVirtualAxis; // Reference to the joystick in the cross platform input
|
|
protected CrossPlatformInputManager.VirtualAxis m_VerticalVirtualAxis; // Reference to the joystick in the cross platform input
|
|
|
|
#if USE_NEW_INPUT
|
|
protected override string controlPathInternal
|
|
{
|
|
get => m_ControlPath;
|
|
set => m_ControlPath = value;
|
|
}
|
|
#endif
|
|
public float Horizontal { get { return (snapX) ? SnapFloat(input.x, AxisOptions.Horizontal) : input.x; } }
|
|
public float Vertical { get { return (snapY) ? SnapFloat(input.y, AxisOptions.Vertical) : input.y; } }
|
|
public Vector2 Direction { get { return new Vector2(Horizontal, Vertical); } }
|
|
|
|
void CreateVirtualAxes()
|
|
{
|
|
// set axes to use
|
|
// create new axes based on axes to use
|
|
if (CrossPlatformInputManager.AxisExists(horizontalAxisName))
|
|
{
|
|
m_HorizontalVirtualAxis = CrossPlatformInputManager.VirtualAxisReference(horizontalAxisName);
|
|
}
|
|
else
|
|
{
|
|
m_HorizontalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(horizontalAxisName);
|
|
CrossPlatformInputManager.RegisterVirtualAxis(m_HorizontalVirtualAxis);
|
|
}
|
|
|
|
|
|
if (CrossPlatformInputManager.AxisExists(verticalAxisName))
|
|
{
|
|
m_VerticalVirtualAxis = CrossPlatformInputManager.VirtualAxisReference(verticalAxisName);
|
|
}
|
|
else
|
|
{
|
|
m_VerticalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(verticalAxisName);
|
|
CrossPlatformInputManager.RegisterVirtualAxis(m_VerticalVirtualAxis);
|
|
}
|
|
}
|
|
|
|
public float HandleRange
|
|
{
|
|
get { return handleRange; }
|
|
set { handleRange = Mathf.Abs(value); }
|
|
}
|
|
|
|
public float DeadZone
|
|
{
|
|
get { return deadZone; }
|
|
set { deadZone = Mathf.Abs(value); }
|
|
}
|
|
|
|
public AxisOptions AxisOptions { get { return AxisOptions; } set { axisOptions = value; } }
|
|
public bool SnapX { get { return snapX; } set { snapX = value; } }
|
|
public bool SnapY { get { return snapY; } set { snapY = value; } }
|
|
|
|
[SerializeField] private float handleRange = 1;
|
|
[SerializeField] private float inputAmp = 1;
|
|
[SerializeField] private float deadZone = 0;
|
|
[SerializeField] private AxisOptions axisOptions = AxisOptions.Both;
|
|
[SerializeField] private float m_clampMin = 0.2f;
|
|
[SerializeField] private bool snapX = false;
|
|
[SerializeField] private bool snapY = false;
|
|
[SerializeField] private bool stepXY = true;
|
|
[SerializeField] private float stepSize = 0.65f;
|
|
|
|
[SerializeField] protected RectTransform background = null;
|
|
[SerializeField] private RectTransform handle = null;
|
|
private RectTransform baseRect = null;
|
|
|
|
private Canvas canvas;
|
|
private Camera cam;
|
|
|
|
private Vector2 input = Vector2.zero;
|
|
|
|
private bool m_Dragging;
|
|
private Vector2 delta;
|
|
private Vector2 prevInput;
|
|
|
|
protected virtual void Start()
|
|
{
|
|
HandleRange = handleRange;
|
|
DeadZone = deadZone;
|
|
baseRect = GetComponent<RectTransform>();
|
|
canvas = GetComponentInParent<Canvas>();
|
|
if (canvas == null)
|
|
Debug.LogError("The Joystick is not placed inside a canvas");
|
|
|
|
Vector2 center = new Vector2(0.5f, 0.5f);
|
|
background.pivot = center;
|
|
handle.anchorMin = center;
|
|
handle.anchorMax = center;
|
|
handle.pivot = center;
|
|
handle.anchoredPosition = Vector2.zero;
|
|
|
|
if (action)
|
|
{
|
|
actionCanvasGroup = action.GetComponent<CanvasGroup>();
|
|
if (actionCanvasGroup)
|
|
{
|
|
actionCanvasGroup.alpha = 0f;
|
|
}
|
|
if (action.transform.childCount > 0)
|
|
{
|
|
activeActionCanvasGroup = action.transform.GetChild(0).GetComponent<CanvasGroup>();
|
|
if (activeActionCanvasGroup)
|
|
{
|
|
activeActionCanvasGroup.alpha = 0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
CreateVirtualAxes();
|
|
}
|
|
|
|
protected virtual void Update()
|
|
{
|
|
if (!m_Dragging)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_ControlType == ControlType.Touchpad)
|
|
{
|
|
delta = input;
|
|
delta.x -= prevInput.x;
|
|
delta.y -= prevInput.y;
|
|
prevInput = input;
|
|
|
|
inputProcessed.x = Horizontal;
|
|
inputProcessed.y = Vertical;
|
|
|
|
ClampValue();
|
|
|
|
if (delta.x == 0f)
|
|
{
|
|
inputProcessed.x = 0f;
|
|
}
|
|
if (delta.y == 0f)
|
|
{
|
|
inputProcessed.y = 0f;
|
|
}
|
|
|
|
#if USE_NEW_INPUT
|
|
SendValueToControl(inputProcessed);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private void ClampValue()
|
|
{
|
|
if (inputProcessed.x > 0)
|
|
{
|
|
inputProcessed.x = Mathf.Max(inputProcessed.x, m_clampMin);
|
|
}
|
|
if (inputProcessed.x < 0)
|
|
{
|
|
inputProcessed.x = Mathf.Min(inputProcessed.x, -m_clampMin);
|
|
}
|
|
|
|
if (inputProcessed.y > 0)
|
|
{
|
|
inputProcessed.y = Mathf.Max(inputProcessed.y, m_clampMin);
|
|
}
|
|
if (inputProcessed.y < 0)
|
|
{
|
|
inputProcessed.y = Mathf.Min(inputProcessed.y, -m_clampMin);
|
|
}
|
|
|
|
if (stepXY)
|
|
{
|
|
float mag = inputProcessed.magnitude;
|
|
var absM = Mathf.Abs(mag);
|
|
var sig = Mathf.Sign(mag);
|
|
if (absM > 0f)
|
|
{
|
|
inputProcessed /= absM;
|
|
if (absM < stepSize)
|
|
{
|
|
inputProcessed *= 0.5f;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (stepX)
|
|
{
|
|
if (inputProcessed.x > 0)
|
|
{
|
|
if (inputProcessed.x < stepSize)
|
|
{
|
|
inputProcessed.x = 0.5f;
|
|
}
|
|
else
|
|
{
|
|
inputProcessed.x = 1f;
|
|
}
|
|
}
|
|
else if (inputProcessed.x < 0)
|
|
{
|
|
if (inputProcessed.x > -stepSize)
|
|
{
|
|
inputProcessed.x = -0.5f;
|
|
}
|
|
else
|
|
{
|
|
inputProcessed.x = -1f;
|
|
}
|
|
}
|
|
}
|
|
if (stepY)
|
|
{
|
|
if (inputProcessed.y > 0)
|
|
{
|
|
if (inputProcessed.y < stepSize)
|
|
{
|
|
inputProcessed.y = 0.5f;
|
|
}
|
|
else
|
|
{
|
|
inputProcessed.y = 1f;
|
|
}
|
|
}
|
|
else if (inputProcessed.y < 0)
|
|
{
|
|
if (inputProcessed.y > -stepSize)
|
|
{
|
|
inputProcessed.y = -0.5f;
|
|
}
|
|
else
|
|
{
|
|
inputProcessed.y = -1f;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
}
|
|
|
|
public virtual void OnPointerDown(PointerEventData eventData)
|
|
{
|
|
m_Dragging = true;
|
|
OnDrag(eventData);
|
|
}
|
|
|
|
void UpdateVirtualAxes(Vector2 value)
|
|
{
|
|
//Debug.Log("Virtual axis: "+value);
|
|
m_HorizontalVirtualAxis.Update(value.x);
|
|
m_VerticalVirtualAxis.Update(value.y);
|
|
}
|
|
|
|
public void OnDrag(PointerEventData eventData)
|
|
{
|
|
cam = null;
|
|
if (canvas.renderMode == RenderMode.ScreenSpaceCamera)
|
|
cam = canvas.worldCamera;
|
|
|
|
Vector2 position = RectTransformUtility.WorldToScreenPoint(cam, background.position);
|
|
Vector2 radius = background.sizeDelta / 2;
|
|
input = (eventData.position - position) / (radius * canvas.scaleFactor);
|
|
|
|
FormatInput();
|
|
HandleInput(input.magnitude, input.normalized, radius, cam);
|
|
handle.anchoredPosition = input * radius * handleRange;
|
|
|
|
if (m_ControlType == ControlType.Joystick)
|
|
{
|
|
inputProcessed.x = Horizontal;
|
|
inputProcessed.y = Vertical;
|
|
|
|
//ClampValue();
|
|
|
|
if (actionCanvasGroup)
|
|
{
|
|
var inputProcessedLength = inputProcessed.magnitude;
|
|
actionCanvasGroup.alpha = Mathf.InverseLerp(actionStartLerpValue, 1f, Mathf.Clamp01(inputProcessedLength));
|
|
if (activeActionCanvasGroup && inputProcessedLength > activeActionValue)
|
|
{
|
|
activeActionCanvasGroup.alpha = 1f;
|
|
}
|
|
else if (activeActionCanvasGroup)
|
|
{
|
|
activeActionCanvasGroup.alpha = 0f;
|
|
}
|
|
}
|
|
ClampValue();
|
|
inputProcessedByModifier.x = inputProcessed.x * inputAmp;
|
|
inputProcessedByModifier.y = inputProcessed.y * inputAmp;
|
|
|
|
//inputProcessedByModifier.x = inputModifier.Evaluate(inputProcessed.x) * inputAmp;
|
|
//inputProcessedByModifier.y = inputModifier.Evaluate(inputProcessed.y) * inputAmp;
|
|
#if USE_NEW_INPUT
|
|
SendValueToControl(inputProcessedByModifier);
|
|
#endif
|
|
UpdateVirtualAxes(inputProcessedByModifier);
|
|
}
|
|
}
|
|
|
|
protected virtual void HandleInput(float magnitude, Vector2 normalised, Vector2 radius, Camera cam)
|
|
{
|
|
if (magnitude > deadZone)
|
|
{
|
|
//if (magnitude > 1)
|
|
// input = normalised;
|
|
}
|
|
else
|
|
input = Vector2.zero;
|
|
}
|
|
|
|
private void FormatInput()
|
|
{
|
|
if (axisOptions == AxisOptions.Horizontal)
|
|
input = new Vector2(input.x, 0f);
|
|
else if (axisOptions == AxisOptions.Vertical)
|
|
input = new Vector2(0f, input.y);
|
|
}
|
|
|
|
private float SnapFloat(float value, AxisOptions snapAxis)
|
|
{
|
|
if (value == 0)
|
|
return value;
|
|
|
|
if (axisOptions == AxisOptions.Both)
|
|
{
|
|
float angle = Vector2.Angle(input, Vector2.up);
|
|
if (snapAxis == AxisOptions.Horizontal)
|
|
{
|
|
if (angle < 22.5f || angle > 157.5f)
|
|
return 0;
|
|
else
|
|
return (value > 0) ? 1 : -1;
|
|
}
|
|
else if (snapAxis == AxisOptions.Vertical)
|
|
{
|
|
if (angle > 67.5f && angle < 112.5f)
|
|
return 0;
|
|
else
|
|
return (value > 0) ? 1 : -1;
|
|
}
|
|
return value;
|
|
}
|
|
else
|
|
{
|
|
if (value > 0)
|
|
return 1;
|
|
if (value < 0)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public virtual void OnPointerUp(PointerEventData eventData)
|
|
{
|
|
//Debug.Log("input " + input);
|
|
//Debug.Log("input.magnitude " + input.magnitude);
|
|
if (input.magnitude >= activeActionValue)
|
|
{
|
|
AdditionalAction();
|
|
StartCoroutine(DelayPointerUp());
|
|
return;
|
|
}
|
|
|
|
PointerUp();
|
|
}
|
|
|
|
private IEnumerator DelayPointerUp()
|
|
{
|
|
yield return new WaitForSeconds(0.1f);
|
|
PointerUp();
|
|
}
|
|
|
|
protected void OnDisable()
|
|
{
|
|
PointerUp();
|
|
}
|
|
|
|
protected void PointerUp()
|
|
{
|
|
if (actionCanvasGroup)
|
|
{
|
|
actionCanvasGroup.alpha = 0f;
|
|
}
|
|
if (activeActionCanvasGroup)
|
|
{
|
|
activeActionCanvasGroup.alpha = 0f;
|
|
}
|
|
m_Dragging = false;
|
|
input = Vector2.zero;
|
|
delta = Vector2.zero;
|
|
prevInput = Vector2.zero;
|
|
handle.anchoredPosition = Vector2.zero;
|
|
UpdateVirtualAxes(input);
|
|
#if USE_NEW_INPUT
|
|
SendValueToControl(input);
|
|
#endif
|
|
}
|
|
|
|
private void AdditionalAction()
|
|
{
|
|
if (action)
|
|
{
|
|
action.Click();
|
|
}
|
|
}
|
|
|
|
protected Vector2 ScreenPointToAnchoredPosition(Vector2 screenPosition)
|
|
{
|
|
Vector2 localPoint = Vector2.zero;
|
|
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(baseRect, screenPosition, cam, out localPoint))
|
|
{
|
|
Vector2 pivotOffset = baseRect.pivot * baseRect.sizeDelta;
|
|
return localPoint - (background.anchorMax * baseRect.sizeDelta) + pivotOffset;
|
|
}
|
|
return Vector2.zero;
|
|
}
|
|
}
|
|
|
|
public enum AxisOptions { Both, Horizontal, Vertical }
|
|
public enum ControlType { Joystick, Touchpad } |