Search Unity

Question How can I use 2D freeform directional with Joysticks?

Discussion in 'Scripting' started by Johnscaban, Aug 14, 2021.

  1. Johnscaban

    Johnscaban

    Joined:
    May 9, 2020
    Posts:
    23
    I have a top-down shooter which has two joysticks, the left one for movement and right one for shooting. Right now, my character moves and plays his run animation, and if the right joystick is been dragged upwards, going forward plays the normal run animation, going right plays the strafe right animation, going left plays the strafe left animation, going backwards plays the running backwards animation. But my problem is that when I drag the right joystick to another direction other than upwards, the animations are played as if the right joystick is still going upwards.

    Here is my Joystick script:

    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 }
    I hope that you can understand my objective.

    Edit: Also, here is a video of my problem and here is my PlayerAnimator script:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PlayerAnimator : MonoBehaviour
    6. {
    7.     Player player;
    8.     Animator animator;
    9.  
    10.     public Joystick leftJoystick;
    11.     public Joystick rightJoystick;
    12.  
    13.     void Start()
    14.     {
    15.         player = FindObjectOfType<Player>();
    16.         animator = GetComponentInChildren<Animator>();
    17.     }
    18.  
    19.     void Update()
    20.     {
    21.         if (leftJoystick.Direction == Vector2.zero)
    22.         {
    23.             animator.SetFloat("inputX", 0f);
    24.             animator.SetFloat("inputZ", 0f);
    25.         }
    26.  
    27.         if (rightJoystick.Direction == Vector2.zero)
    28.         {
    29.             animator.SetBool("isAttacking", false);
    30.         }
    31.  
    32.         if (leftJoystick.Direction != Vector2.zero)
    33.         {
    34.             animator.SetFloat("inputX", 0f);
    35.             animator.SetFloat("inputZ", 1f);
    36.         }
    37.  
    38.         if (rightJoystick.Direction != Vector2.zero)
    39.         {
    40.             animator.SetBool("isAttacking", true);
    41.  
    42.             animator.SetFloat("inputX", leftJoystick.Horizontal);
    43.             animator.SetFloat("inputZ", leftJoystick.Vertical);
    44.         }
    45.     }
    46. }
     
    Last edited: Aug 15, 2021
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    I've seen this done by splitting the animation for the top of the guy (the part that shoots) from the bottom of the guy (the part that walks around).

    I've seen that accomplished as two separately-animated characters, but also done as animator layers.

    Check out some tutorials for "twin stick shooters."

    I'd offer more but I never bother to animate that sorta detail, just let the bullets fly out, keeping it nice and simple.
     
  3. Johnscaban

    Johnscaban

    Joined:
    May 9, 2020
    Posts:
    23
    I've checked some tutorials but they all are 2d. I edited my question with a video that shows my problem and my player animator script..
     
    Last edited: Aug 16, 2021