Search Unity

How to link UI joystick to value

Discussion in 'Input System' started by vituba2202, Jun 23, 2022.

  1. vituba2202

    vituba2202

    Joined:
    Jun 8, 2021
    Posts:
    13
    Hello!
    I'm modifying the karting microgame and I have already added controller support, but I also wanted to add on-screen joystick support. I've already managed to create a script that accelerates, brakes, and steers the car, but I can't figure out how to link it to an on-screen joystick. I made this quick video to make what I'm talking about clearer:



    In short, what I need to do is find a way to make it so that moving the joystick changes the "steer" value between -1 and 1 to control how much the car steers.

    I've managed to, in the meanwhile, create functions called by UI buttons to steer the car left or right, which works, but is definitely not ideal because it doesn't allow for very precise movements at all.
    This is the UI input script, attached directly to the player car:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. namespace KartGame.KartSystems
    6. {
    7.     public class UiInput : BaseInput
    8.  
    9.     {
    10.         public bool accelerate;
    11.         public bool brake;
    12.         public float turn;
    13.  
    14.         // Called by OnPointerDown event from Even Trigger attached to UI object
    15.         // https://youtu.be/3NBYqPAA5Es?t=194
    16.         public void StartAccelerate() => accelerate = true;
    17.  
    18.         // Called by OnPointerUp event from Even Trigger attached to UI object
    19.         // https://youtu.be/3NBYqPAA5Es?t=194
    20.         public void StopAccelerate() => accelerate = false;
    21.  
    22.         public void StartBrake() => brake = true;
    23.         public void StopBrake() => brake = false;
    24.  
    25.         public void StartTurnLeft() => turn = Mathf.Max(turn - 1, -1);
    26.         public void StopTurnLeft() => turn = Mathf.Min(turn + 1, 1);
    27.  
    28.         public void StartTurnRight() => turn = Mathf.Min(turn + 1, 1);
    29.         public void StopTurnRight() => turn = Mathf.Max(turn - 1, -1);
    30.  
    31.         public override InputData GenerateInput()
    32.         {
    33.             return new InputData
    34.             {
    35.                 Accelerate = accelerate,
    36.                 Brake = brake,
    37.                 TurnInput = turn
    38.             };
    39.         }
    40.     }
    41. }
    This is the Joystick script I'm using, but if you have an idea for a different one I'm open to it.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.EventSystems;
    5.  
    6. public class Joystick : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
    7. {
    8.     public float Horizontal { get { return (snapX) ? SnapFloat(input.x, AxisOptions.Horizontal) : input.x; } }
    9.     public float Vertical { get { return (snapY) ? SnapFloat(input.y, AxisOptions.Vertical) : input.y; } }
    10.     public Vector2 Direction { get { return new Vector2(Horizontal, Vertical); } }
    11.  
    12.     public float HandleRange
    13.     {
    14.         get { return handleRange; }
    15.         set { handleRange = Mathf.Abs(value); }
    16.     }
    17.  
    18.     public float DeadZone
    19.     {
    20.         get { return deadZone; }
    21.         set { deadZone = Mathf.Abs(value); }
    22.     }
    23.  
    24.     public AxisOptions AxisOptions { get { return AxisOptions; } set { axisOptions = value; } }
    25.     public bool SnapX { get { return snapX; } set { snapX = value; } }
    26.     public bool SnapY { get { return snapY; } set { snapY = value; } }
    27.  
    28.     [SerializeField] private float handleRange = 1;
    29.     [SerializeField] private float deadZone = 0;
    30.     [SerializeField] private AxisOptions axisOptions = AxisOptions.Both;
    31.     [SerializeField] private bool snapX = false;
    32.     [SerializeField] private bool snapY = false;
    33.  
    34.     [SerializeField] protected RectTransform background = null;
    35.     [SerializeField] private RectTransform handle = null;
    36.     private RectTransform baseRect = null;
    37.  
    38.     private Canvas canvas;
    39.     private Camera cam;
    40.  
    41.     private Vector2 input = Vector2.zero;
    42.  
    43.     protected virtual void Start()
    44.     {
    45.         HandleRange = handleRange;
    46.         DeadZone = deadZone;
    47.         baseRect = GetComponent<RectTransform>();
    48.         canvas = GetComponentInParent<Canvas>();
    49.         if (canvas == null)
    50.             Debug.LogError("The Joystick is not placed inside a canvas");
    51.  
    52.         Vector2 center = new Vector2(0.5f, 0.5f);
    53.         background.pivot = center;
    54.         handle.anchorMin = center;
    55.         handle.anchorMax = center;
    56.         handle.pivot = center;
    57.         handle.anchoredPosition = Vector2.zero;
    58.     }
    59.  
    60.     public virtual void OnPointerDown(PointerEventData eventData)
    61.     {
    62.         OnDrag(eventData);
    63.     }
    64.  
    65.     public void OnDrag(PointerEventData eventData)
    66.     {
    67.         cam = null;
    68.         if (canvas.renderMode == RenderMode.ScreenSpaceCamera)
    69.             cam = canvas.worldCamera;
    70.  
    71.         Vector2 position = RectTransformUtility.WorldToScreenPoint(cam, background.position);
    72.         Vector2 radius = background.sizeDelta / 2;
    73.         input = (eventData.position - position) / (radius * canvas.scaleFactor);
    74.         FormatInput();
    75.         HandleInput(input.magnitude, input.normalized, radius, cam);
    76.         handle.anchoredPosition = input * radius * handleRange;
    77.     }
    78.  
    79.     protected virtual void HandleInput(float magnitude, Vector2 normalised, Vector2 radius, Camera cam)
    80.     {
    81.         if (magnitude > deadZone)
    82.         {
    83.             if (magnitude > 1)
    84.                 input = normalised;
    85.         }
    86.         else
    87.             input = Vector2.zero;
    88.     }
    89.  
    90.     private void FormatInput()
    91.     {
    92.         if (axisOptions == AxisOptions.Horizontal)
    93.             input = new Vector2(input.x, 0f);
    94.         else if (axisOptions == AxisOptions.Vertical)
    95.             input = new Vector2(0f, input.y);
    96.     }
    97.  
    98.     private float SnapFloat(float value, AxisOptions snapAxis)
    99.     {
    100.         if (value == 0)
    101.             return value;
    102.  
    103.         if (axisOptions == AxisOptions.Both)
    104.         {
    105.             float angle = Vector2.Angle(input, Vector2.up);
    106.             if (snapAxis == AxisOptions.Horizontal)
    107.             {
    108.                 if (angle < 22.5f || angle > 157.5f)
    109.                     return 0;
    110.                 else
    111.                     return (value > 0) ? 1 : -1;
    112.             }
    113.             else if (snapAxis == AxisOptions.Vertical)
    114.             {
    115.                 if (angle > 67.5f && angle < 112.5f)
    116.                     return 0;
    117.                 else
    118.                     return (value > 0) ? 1 : -1;
    119.             }
    120.             return value;
    121.         }
    122.         else
    123.         {
    124.             if (value > 0)
    125.                 return 1;
    126.             if (value < 0)
    127.                 return -1;
    128.         }
    129.         return 0;
    130.     }
    131.  
    132.     public virtual void OnPointerUp(PointerEventData eventData)
    133.     {
    134.         input = Vector2.zero;
    135.         handle.anchoredPosition = Vector2.zero;
    136.     }
    137.  
    138.     protected Vector2 ScreenPointToAnchoredPosition(Vector2 screenPosition)
    139.     {
    140.         Vector2 localPoint = Vector2.zero;
    141.         if (RectTransformUtility.ScreenPointToLocalPointInRectangle(baseRect, screenPosition, cam, out localPoint))
    142.         {
    143.             Vector2 pivotOffset = baseRect.pivot * baseRect.sizeDelta;
    144.             return localPoint - (background.anchorMax * baseRect.sizeDelta) + pivotOffset;
    145.         }
    146.         return Vector2.zero;
    147.     }
    148. }
    149.  
    150. public enum AxisOptions { Both, Horizontal, Vertical }