Forum begins after the advertisement:


[Version 1.0.5] Fix for ID bug with multiple joysticks

Home Forums Unity Assets Support Virtual Joystick Pack [Version 1.0.5] Fix for ID bug with multiple joysticks

Viewing 8 posts - 1 through 8 (of 8 total)
  • Author
    Posts
  • #15929
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Update 28 Sep 2024: The codes have been updated, but you can also get the codes on Version 1.0.7 of the asset here.

    @KaanGulUnity has found a bug with Version 1.0.5 of the asset.

    Hi, you obviously have the most up-to-date joystick bundle in the asset store. But there is a big problem; your system is not detecting joysticks properly. I’m developing a project and your package works fine, but unfortunately I can’t use it because of this problem.

    I have two joysticks on my screen, one of them controls the character and the other one controls the camera. When testing in the editor there is no problem, but on the webgl or android platform the order of the joysticks gets mixed up. So the joystick that should control the character controls the camera and the other one works incorrectly like this.

    Can you solve this problem?

    I’ll be releasing a Version 1.0.6 1.0.7 soon with a new ID attribute for all joysticks, so you can manually set the ID number of your joysticks and ensure that their order does not change in different devices.

    View post on imgur.com

    As it takes a few days for the patch to go live on Unity, I’m posting the code here in advance first. Just replace the codes of the 2 scripts below in your project and you can start using them:

    VirtualJoystick.cs

    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.EventSystems;
    using UnityEngine.UI;
    using System;
    using System.Linq;
    namespace Terresquall {
    
        [System.Serializable]
        [RequireComponent(typeof(Image),typeof(RectTransform))]
        public class VirtualJoystick:MonoBehaviour {
            [Tooltip("The unique tooltip for this joystick. Needs to be unique.")]
            public int ID;
            public Image controlStick;
    
            [Header("Debug")]
            public bool consolePrintAxis = false;
    
            [Header("Settings")]
            public bool onlyOnMobile = true;
            public Color dragColor = new Color(0.9f,0.9f,0.9f,1f);
            //[Tooltip("Sets the joystick back to its original position once it is let go of")] public bool snapToOrigin = false;
            public float sensitivity = 2f;
            [Range(0,2)] public float radius = 0.7f;
            [Range(0,1)] public float deadzone = 0.3f;
    
            [Tooltip("Joystick automatically snaps to the edge when outside the deadzone.")]
            public bool edgeSnap;
            [Tooltip("Number of directions of the joystick. " +
                "\nKeep at 0 for a free joystick. " +
                "\nWorks best with multiples of 4")]
            [Range(0,20)] public int directions = 0;
    
            [Tooltip("Use this to adjust the angle that the directions are pointed towards.")]
            public float angleOffset = 0;
    
            [Tooltip("Snaps the joystick to wherever the finger is within a certain boundary.")]
            public bool snapsToTouch = false;
            public Rect boundaries;
    
            // Private variables.
            internal Vector2 desiredPosition, axis, origin, lastAxis;
            internal Color originalColor; // Stores the original color of the Joystick.
            int currentPointerId = -2;
    
            internal static readonly Dictionary<int, VirtualJoystick> instances = new Dictionary<int, VirtualJoystick>();
    
            public const string VERSION = "1.0.6";
            public const string DATE = "26 September 2024";
    
            Vector2Int lastScreen;
            Canvas canvas;
    
            // Get an existing instance of a joystick.
    		public static VirtualJoystick GetInstance(int id = 0) {
    			// Display an error if an invalid ID is used.
    			if(!instances.ContainsKey(id)) {
    				// If used without any arguments, but no item has an ID of 0,
                    // we get the first item in the dictionary.
    				if(id == 0) {
    					if(instances.Count > 0) {
    						id = instances.Keys.First();
                            Debug.LogWarning($"You are reading Joystick input without specifying an ID, so joystick ID {id} is being used instead.");
                        } else {
    						Debug.LogError("There are no Virtual Joysticks in the Scene!");
    						return null;
    					}
    				} else {
    					Debug.LogError($"Virtual Joystick ID '{id}' does not exist!");
    					return null;
    				}
    			}
    			
    			// If the code gets here, we can get and return an instance.
    			return instances[id];
    		}
    
            // Gets us the number of active joysticks on the screen.
            public static int CountActiveInstances() {
                int count = 0;
                foreach(KeyValuePair<int,VirtualJoystick> j in instances) {
                    if(j.Value.isActiveAndEnabled)
                        count++;
                }
                return count;
            }
    
            public static float GetAxis(string axe, int id = 0) {
                // Show an error if no joysticks are found.
                if (instances.Count <= 0)
                {
                    Debug.LogWarning("No instances of joysticks found on the Scene.");
                    return 0;
                }
    
                switch (axe.ToLower()) {
                    case "horizontal":
                    case "h":
                    case "x":
                        return GetInstance(id).axis.x;
                    case "vertical":
                    case "v":
                    case "y":
                        return GetInstance(id).axis.y;
                }
                return 0;
            }
    
            public Vector2 GetAxisDelta() { return GetAxis() - lastAxis; }
            public static Vector2 GetAxisDelta(int id = 0) {
                // Show an error if no joysticks are found.
                if (instances.Count <= 0) {
                    Debug.LogWarning("No instances of joysticks found on the Scene.");
                    return Vector2.zero;
                }
    
                return GetInstance(id).GetAxisDelta();
            }
    
            public Vector2 GetAxis() { return axis; }
            public static Vector2 GetAxis(int id = 0) {
                // Show an error if no joysticks are found.
                if (instances.Count <= 0)
                {
                    Debug.LogWarning("No active instance of Virtual Joystick found on the Scene.");
                    return Vector2.zero;
                }
                
                return GetInstance(id).axis;
            }
    
            public Vector2 GetAxisRaw() { 
                return new Vector2(
                    Mathf.Abs(axis.x) < deadzone || Mathf.Approximately(axis.x, 0) ? 0 : Mathf.Sign(axis.x),
                    Mathf.Abs(axis.y) < deadzone || Mathf.Approximately(axis.y, 0) ? 0 : Mathf.Sign(axis.y)
                );
            }
    
            public float GetAxisRaw(string axe) {
                float f = GetAxis(axe);
                if(Mathf.Abs(f) < deadzone || Mathf.Approximately(f, 0))
                    return 0;
                return Mathf.Sign(GetAxis(axe));
            }
    
            public static float GetAxisRaw(string axe, int id = 0) {
                // Show an error if no joysticks are found.
                if (instances.Count <= 0)
                {
                    Debug.LogWarning("No active instance of Virtual Joystick found on the Scene.");
                    return 0;
                }
    
                return GetInstance(id).GetAxisRaw(axe);
            }
    
            public static Vector2 GetAxisRaw(int id = 0) {
                // Show an error if no joysticks are found.
                if (instances.Count <= 0)
                {
                    Debug.LogWarning("No instances of joysticks found on the Scene.");
                    return Vector2.zero;
                }
    
                return GetInstance(id).GetAxisRaw();
            }
    
            // Get the radius of this joystick.
            public float GetRadius() {
                RectTransform t = transform as RectTransform;
                if(t)
                    return radius * t.rect.width * 0.5f;
                return radius;
            }
    
            // What happens when we press down on the element.
            public void OnPointerDown(PointerEventData data) {
                currentPointerId = data.pointerId;
                SetPosition(data.position);
                controlStick.color = dragColor;
            }
    
            // What happens when we stop pressing down on the element.
            public void OnPointerUp(PointerEventData data) {
                desiredPosition = transform.position;
                controlStick.color = originalColor;
                currentPointerId = -2;
    
                //Snaps the joystick back to its original position
                /*if (snapToOrigin && (Vector2)transform.position != origin) {
                    transform.position = origin;
                    SetPosition(origin);
                }*/
            }
    
            protected void SetPosition(Vector2 position) {
    
                // Gets the difference in position between where we want to be,
                // and the center of the joystick.
                Vector2 diff = position - (Vector2)transform.position;
    
                // Other variables needed for various functionalities.
                float radius = GetRadius();
                bool snapToEdge = edgeSnap && (diff / radius).magnitude > deadzone;
    
                // If no directions to snap to, joystick moves freely.
                if(directions <= 0) {
                    // If edge snap is on, it will always snap to the edge when outside of the deadzone.
                    if(snapToEdge) {
                        desiredPosition = (Vector2)transform.position + diff.normalized * radius;
                    } else {
                        // Clamp the desired position within the radius.
                        desiredPosition = (Vector2)transform.position + Vector2.ClampMagnitude(diff,radius);
                    }
                } else {
                    // Calculate nearest snap directional vectors
                    Vector2 snapDirection = SnapDirection(diff.normalized, directions, ((360f / directions) + angleOffset) * Mathf.Deg2Rad);
                    
                    // Do we snap to the edge outside of the deadzone?
                    if(snapToEdge) {
                        // Snap to the edge if we are beyond the deadzone.
                        desiredPosition = (Vector2)transform.position + snapDirection * radius;
                    } else {
                        desiredPosition = (Vector2)transform.position + Vector2.ClampMagnitude(snapDirection * diff.magnitude, radius);
                    }
                }
            }
    
            // Calculates nearest directional snap vector to the actual directional vector of the joystick
            private Vector2 SnapDirection(Vector2 vector,int directions,float symmetryAngle) {
                //Gets the line of symmetry between 2 snap directions
                Vector2 symmetryLine = new Vector2(Mathf.Cos(symmetryAngle),Mathf.Sin(symmetryAngle));
                
                //Gets the angle between the joystick dir and the nearest snap dir
                float angle = Vector2.SignedAngle(symmetryLine,vector);
    
                // Divides the angle by the step size between directions, which is 180f / directions.
                // The result is that the angle is now expressed as a multiple of the step size between directions.
                angle /= 180f / directions;
    
                // Angle is then rounded to the nearest whole number so that it corresponds to one of the possible directions.
                angle = (angle >= 0f) ? Mathf.Floor(angle) : Mathf.Ceil(angle);
    
                // Checks if angle is odd
                if((int)Mathf.Abs(angle) % 2 == 1) {
                    // Adds or subtracts 1 to ensure that angle is always even.
                    angle += (angle >= 0f) ? 1 : -1;
                }
    
                // Scale angle back to original scale as we divided it too make a multiple before.
                angle *= 180f / directions;
                angle *= Mathf.Deg2Rad;
    
                // Gets directional vector nearest to the joystick dir with a magnitude of 1.
                // Then multiplies it by the magnitude of the joytick vector.
                Vector2 result = new Vector2(Mathf.Cos(angle + symmetryAngle),Mathf.Sin(angle + symmetryAngle));
                result *= vector.magnitude;
                return result;
            }
    
            // Loops through children to find an appropriate component to put in.
            void Reset() {
                for(int i = 0;i < transform.childCount;i++) {
                    // Once we find an appropriate Image component, abort.
                    Image img = transform.GetChild(i).GetComponent<Image>();
                    if(img) {
                        controlStick = img;
                        break;
                    }
                }
            }
    
            // Function for us to modify the bounds value in future.
            public Rect GetBounds() {
                if(!snapsToTouch) return new Rect(0,0,0,0);
                return new Rect(boundaries.x,boundaries.y,Screen.width * boundaries.width,Screen.height * boundaries.height);
            }
    
            void OnEnable() {
    
                // If we are not on mobile, and this is mobile only, disable.
                if(!Application.isMobilePlatform && onlyOnMobile) {
                    gameObject.SetActive(false);
                    Debug.Log($"Your Virtual Joystick \"{name}\" is disabled because Only On Mobile is checked, and you are not on a mobile platform or mobile emualation.", gameObject);
                    return;
                }
    
                // Gets the Canvas that this joystick is on.
                canvas = GetComponentInParent<Canvas>();
                if(!canvas) {
                    Debug.LogError(
                        $"Your Virtual Joystick \"{name})\" is not attached to a Canvas, so it won't work. It has been disabled.",
                        gameObject
                    );
                    enabled = false;
                }
    
                // If the old input system does not exist, print an error message.
                try {
                    Vector2 v = Input.mousePosition;
                } catch(System.InvalidOperationException) {
                    enabled = false;
                    Debug.LogError("The Virtual Joystick will not work because the old Input system is not available. Please enable it by going to Project Settings > Player > Other Settings > Active Input Handling and setting it to Both.", this);
                }
    
                origin = desiredPosition = transform.position;
                StartCoroutine(Activate());
                originalColor = controlStick.color;
    
                // Record the screen's attributes so we can detect changes to screen size,
                // such a phone changing orientations.
                lastScreen = new Vector2Int(Screen.width,Screen.height);
    
                // Add this instance to the List.
    			if(!instances.ContainsKey(ID))
    				instances.Add(ID, this);
    			else
    				Debug.LogWarning("You have multiple Virtual Joysticks with the same ID on the Scene! You may not be able to retrieve input from some of them.", this);
            }
    
            // Added in Version 1.0.2.
            // Resets the position of the joystick again 1 frame after the game starts.
            // This is because the Canvas gets rescaled after the game starts, and this affects
            // how the position is calculated.        
            IEnumerator Activate() {
                yield return new WaitForEndOfFrame();
                origin = desiredPosition = transform.position;
            }
    
            void OnDisable() {
    			if(instances.ContainsKey(ID))
    				instances.Remove(ID);
                else
    				Debug.LogWarning("Unable to remove disabled joystick from the global Virtual Joystick list. You may have changed the ID of your joystick on runtime.", this);
            }
    
            void Update() {
                PositionUpdate();
                
                // If the screen has changed, reset the joystick.
                if(lastScreen.x != Screen.width || lastScreen.y != Screen.height) {
                    lastScreen = new Vector2Int(Screen.width,Screen.height);
                    OnEnable();
                }
    
                // If the currentPointerId > -2, we are being dragged.
                if(currentPointerId > -2) {
                    // If this is more than -1, the Joystick is manipulated by touch.
                    if(currentPointerId > -1) {
                        // We need to loop through all touches to find the one we want.
                        for(int i = 0;i < Input.touchCount;i++) {
                            Touch t = Input.GetTouch(i);
                            if(t.fingerId == currentPointerId) {
                                SetPosition(t.position);
                                break;
                            }
                        }
                    } else {
                        // Otherwise, we are being manipulated by the mouse position.
                        SetPosition(Input.mousePosition);
                    }
                }
    
                // Record the last axis value before we update.
                // For calculating GetAxisDelta().
                lastAxis = axis;
    
                // Update the position of the joystick.
                controlStick.transform.position = Vector2.MoveTowards(controlStick.transform.position,desiredPosition,sensitivity);
    
                // If the joystick is moved less than the dead zone amount, it won't register.
                axis = (controlStick.transform.position - transform.position) / GetRadius();
                if(axis.magnitude < deadzone)
                    axis = Vector2.zero;
    
                // If a joystick is toggled and we are debugging, output to console.
                if(axis.sqrMagnitude > 0) {
                    string output = string.Format("Virtual Joystick ({0}): {1}",name,axis);
                    if(consolePrintAxis)
                        Debug.Log(output);
                }
            }
    
            // Takes the mouse's or finger's position and registers OnPointerDown()
            // if the position hits any part of our Joystick.
            void CheckForInteraction(Vector2 position, int pointerId = -1) {
                // Create PointerEventData
                PointerEventData data = new PointerEventData(null);
                data.position = position;
                data.pointerId = pointerId;
    
                // Perform raycast using GraphicRaycaster attached to the Canvas
                List<RaycastResult> results = new List<RaycastResult>();
                GraphicRaycaster raycaster = canvas.GetComponent<GraphicRaycaster>();
                raycaster.Raycast(data, results);
                
                // Go through the results, and compare it to 
                foreach (RaycastResult result in results) {
                    // Check if the hit GameObject is the control stick or one of its children
                    if (IsGameObjectOrChild(result.gameObject, gameObject)) {
                        // Start dragging the joystick
                        OnPointerDown(data);
                        break;
                    }
                }
            }
    
            // Utility method to check if <hitObject> is <target> or its children.
            // Used by CheckForInteraction().
            bool IsGameObjectOrChild(GameObject hitObject, GameObject target) {
                if (hitObject == target) return true;
    
                foreach (Transform child in target.transform)
                    if (IsGameObjectOrChild(hitObject, child.gameObject)) return true;
                
                return false;
            }
    
            void PositionUpdate() {
    
                // Handle the joystick interaction on Touch.
                if(Input.touchCount > 0) {
                    // Also detect touch events too.
                    for(int i = 0;i < Input.touchCount;i++) {
                        Touch t = Input.GetTouch(i);
                        switch(t.phase) {
                            case TouchPhase.Began:
    
                                CheckForInteraction(t.position,t.fingerId);
    
                                // If currentPointerId < -1, it means this is the first frame we were
                                // clicked on. Check if we need to Uproot().
                                if(currentPointerId < -1) {
                                    if(GetBounds().Contains(t.position)) {
                                        Uproot(t.position,t.fingerId);
                                        return;
                                    }
                                }
                                break;
                            case TouchPhase.Ended:
                            case TouchPhase.Canceled:
                                if(currentPointerId == t.fingerId)
                                    OnPointerUp(new PointerEventData(null));
                                break;
                        }
                    }
    
                } else if(Input.GetMouseButtonDown(0)) {
                    // Checks if our Joystick is being clicked on.
                    CheckForInteraction(Input.mousePosition, -1);
    
                    // If currentPointerId < -1, it means this is the first frame we were
                    // clicked on. Check if we need to Uproot().
                    if(currentPointerId < -1) {
                        if(GetBounds().Contains(Input.mousePosition)) {
                            Uproot(Input.mousePosition);
                        }
                    }
                }
    
                // Trigger OnPointerUp() when we release the button.
                if(Input.GetMouseButtonUp(0) && currentPointerId == -1) {
                    OnPointerUp(new PointerEventData(null));
                }
            }
    
            // Roots the joystick to a new position.
            public void Uproot(Vector2 newPos,int newPointerId = -1) {
                // Don't move the joystick if we are not tapping too far from it.
                if(Vector2.Distance(transform.position,newPos) < radius)
                    return;
    
                // Otherwise move the virtual joystick to where we clicked.
                transform.position = newPos;
                desiredPosition = transform.position;
    
                // Artificially trigger the drag event.
                PointerEventData data = new PointerEventData(EventSystem.current);
                data.position = newPos;
                data.pointerId = newPointerId;
                OnPointerDown(data);
            }
        }
    }

    VirtualJoystickEditor.cs

    
    using UnityEngine;
    using UnityEditor;
    using System.Collections.Generic;
    namespace Terresquall {
        [CustomEditor(typeof(VirtualJoystick))]
        [CanEditMultipleObjects]
        public class VirtualJoystickEditor : Editor {
    
            VirtualJoystick joystick;
            RectTransform rectTransform;
            Canvas canvas;
    
            private int scaleFactor;
            private static readonly List<int> usedIDs = new List<int>();
    
            void OnEnable() {
                joystick = target as VirtualJoystick;
                rectTransform = joystick.GetComponent<RectTransform>();
                canvas = joystick.GetComponentInParent<Canvas>();
            }
    
            // Does the passed joystick have an ID that is unique to itself?
            bool HasUniqueID(VirtualJoystick vj) {
                foreach(VirtualJoystick v in FindObjectsOfType<VirtualJoystick>()) {
                    if(v == vj) continue;
                    if(v.ID == vj.ID) return false;
                }
                return true;
            }
    
            // Is a given ID value already used by another joystick?
            bool IsAvailableID(int id) {
                foreach(VirtualJoystick v in FindObjectsOfType<VirtualJoystick>()) {
                    if(v.ID == id) return false;
                }
                return true;
            }
    
            // Do all the joysticks have unique IDs.
            bool HasRepeatIDs() {
                usedIDs.Clear();
                foreach(VirtualJoystick vj in FindObjectsOfType<VirtualJoystick>()) {
                    if(usedIDs.Contains(vj.ID)) return true;
                    usedIDs.Add(vj.ID);
                }
                return false;
            }
    
            // Reassign all IDs for all Joysticks.
            void ReassignAllIDs(VirtualJoystick exception = null) {
                foreach(VirtualJoystick vj in FindObjectsOfType<VirtualJoystick>()) {
                    // Ignore joysticks that are already unique.
                    if(exception == vj || HasUniqueID(vj)) continue;
                    ReassignThisID(vj);
                }
            }
    
            // Reassign the ID for this Joystick only.
            void ReassignThisID(VirtualJoystick vj) {
    
                // Save the action in the History.
                Undo.RecordObject(vj, "Generate Unique Joystick ID");
    
                // Get all joysticks so that we can check against it if the ID is valid.
                VirtualJoystick[] joysticks = FindObjectsOfType<VirtualJoystick>();
                for(int i = 0; i < joysticks.Length; i++) {
                    if(IsAvailableID(i)) {
                        vj.ID = i; // If we find an unused ID, use it.
                        EditorUtility.SetDirty(vj);
                        return;
                    }
                }
    
                // If all of the IDs are used, we will have to use length + 1 as the ID.
                vj.ID = joysticks.Length;
                EditorUtility.SetDirty(vj);
            }
    
            override public void OnInspectorGUI() {
    
                // Show an error text box if the new Input System is being used.
                try {
                    Vector2 v = Input.mousePosition;
                } catch(System.InvalidOperationException e) {
    
                    Texture2D icon = EditorGUIUtility.Load("icons/console.erroricon.png") as Texture2D;
    
                    // Create a horizontal layout for the icon and text
                    GUILayout.BeginHorizontal(EditorStyles.helpBox);
                    {
                        // Display the icon
                        GUILayout.Label(icon, GUILayout.Width(40), GUILayout.Height(40));
    
                        // Display the text
                        GUIStyle ts = new GUIStyle(EditorStyles.label);
                        ts.wordWrap = true;
                        ts.richText = true;
                        ts.alignment = TextAnchor.MiddleLeft;
                        ts.fontSize = EditorStyles.helpBox.fontSize;
                        GUILayout.Label("<b>This component does not work with the new Input System.</b> You will need to re-enable the old Input System by going to <b>Project Settings > Player > Other Settings > Active Input Handling</b> and selecting <b>Both</b>.", ts);
                        Debug.LogWarning(e.Message, this);
                    }
                    GUILayout.EndHorizontal();
                }
    
                // Draw a help text box if this is not attached to a Canvas.
                if(!canvas && !EditorUtility.IsPersistent(target)) {
                    EditorGUILayout.HelpBox("This GameObject needs to be parented to a Canvas, or it won't work!", MessageType.Warning);
                }
    
                // Draw all the inspector properties.
                serializedObject.Update();
                SerializedProperty property = serializedObject.GetIterator();
                bool snapsToTouch = true;
                int directions = 0;
                if (property.NextVisible(true)) {
                    do {
                        // If the property name is snapsToTouch, record its value.
                        switch(property.name) {
                            case "m_Script":
                                continue;
                            case "snapsToTouch":
                                snapsToTouch = property.boolValue;
                                break;
                            case "directions":
                                directions = property.intValue;
                                break;
                            case "boundaries":
                                // If snapsToTouch is off, don't render boundaries.
                                if(!snapsToTouch) continue;
                                break;
                            case "angleOffset":
                                if(directions <= 0) continue;
                                break;
                        }
    
                        
                        EditorGUI.BeginChangeCheck();
    
                        // Print different properties based on what the property is.
                        if(property.name == "angleOffset") {
                            float maxAngleOffset = 360f / directions / 2;
                            EditorGUILayout.Slider(property, -maxAngleOffset, maxAngleOffset, new GUIContent("Angle Offset"));
                        } else {
                            EditorGUILayout.PropertyField(property, true);
                        }
    
                        EditorGUI.EndChangeCheck();
    
                        // If the property is an ID, show a button allowing us to reassign the IDs.
                        if(property.name == "ID" && !EditorUtility.IsPersistent(target)) {
                            if(!HasUniqueID(joystick)) {
                                EditorGUILayout.HelpBox("This Virtual Joystick doesn't have a unique ID. Please assign a unique ID or click on the button below.", MessageType.Warning);
                                if(GUILayout.Button("Generate Unique Joystick ID")) {
                                    ReassignThisID(joystick);
                                }
                                EditorGUILayout.Space();
                            } else if(HasRepeatIDs()) {
                                EditorGUILayout.HelpBox("At least one of your Virtual Joysticks doesn't have a unique ID. Please ensure that all of them have unique IDs, or they may not be able to collect input properly.", MessageType.Warning);
                                EditorGUILayout.Space();
                            }
                        }
                        
                    } while (property.NextVisible(false));
                }
    
                serializedObject.ApplyModifiedProperties();
    
                //Increase Decrease buttons
                if(joystick) {
    
                    if(!joystick.controlStick) {
                        EditorGUILayout.HelpBox("There is no Control Stick assigned. This joystick won't work.", MessageType.Warning);
                        return;
                    }
    
                    // Add the heading for the size adjustments.
                    EditorGUI.BeginChangeCheck();
                    EditorGUILayout.LabelField("Size Adjustments");
                    GUILayout.BeginHorizontal();
    
                    // Create the Increase / Decrease Size buttons and code the actions.
                    bool increaseSize = GUILayout.Button("Increase Size", EditorStyles.miniButtonLeft),
                         decreaseSize = GUILayout.Button("Decrease Size", EditorStyles.miniButtonRight);
    
                    if(increaseSize || decreaseSize) {
                        // Calculate the sizes needed for the increment / decrement.
                        int gcd = Mathf.RoundToInt(FindGCD((int)rectTransform.sizeDelta.x, (int)joystick.controlStick.rectTransform.sizeDelta.x));
                        Vector2 denominator = new Vector2(gcd, gcd);
    
                        // Record actions for all elements.
                        RectTransform[] affected = rectTransform.GetComponentsInChildren<RectTransform>();
                        RecordSizeChangeUndo(affected);
    
                        // Increase / decrease size actions.
                        if(increaseSize) {
                            foreach(RectTransform r in affected)
                                r.sizeDelta += r.sizeDelta / denominator;
                        } else if(decreaseSize) {
                            foreach(RectTransform r in affected)
                                r.sizeDelta -= r.sizeDelta / denominator;
                        }
                    }
    
                    GUILayout.EndHorizontal();
                    EditorGUI.EndChangeCheck();
                }
    
                ////Boundaries Stuff
                //GUILayout.Space(15);
                //EditorGUILayout.LabelField("Boundaries:", EditorStyles.boldLabel);
                //joystick.snapsToTouch = EditorGUILayout.Toggle("Snap to Touch", joystick.snapsToTouch);
    
                //EditorGUILayout.LabelField("Boundaries");
                //EditorGUIUtility.labelWidth = 15;
                //GUILayout.BeginHorizontal();
                //joystick.boundaries.x = EditorGUILayout.Slider("X", joystick.boundaries.x, 0, 1);
                //joystick.boundaries.y = EditorGUILayout.Slider("Y", joystick.boundaries.y, 0, 1);
                //GUILayout.EndHorizontal();
    
                //GUILayout.BeginHorizontal();
                //joystick.boundaries.width = EditorGUILayout.Slider("W", joystick.boundaries.width, 0, 1);
                //joystick.boundaries.height = EditorGUILayout.Slider("H", joystick.boundaries.height, 0, 1);
                //GUILayout.EndHorizontal();
    
                ////Bounds Anchor buttons
                //GUILayout.Space(3);
                //EditorGUILayout.LabelField("Bounds Anchor:", EditorStyles.boldLabel);
                //GUILayout.BeginHorizontal();
                //if (GUILayout.Button("Top Left", EditorStyles.miniButtonLeft))
                //{
    
                //}
                //if (GUILayout.Button("Top Right", EditorStyles.miniButtonRight))
                //{
    
                //}
                //GUILayout.EndHorizontal();
    
                //if (GUILayout.Button("Middle"))
                //{
    
                //}
    
                //GUILayout.BeginHorizontal();
                //if (GUILayout.Button("Bottom Left", EditorStyles.miniButtonLeft))
                //{
    
                //}
                //if (GUILayout.Button("Bottom Right", EditorStyles.miniButtonRight))
                //{
    
                //}
                //GUILayout.EndHorizontal();
    
                //if (EditorGUI.EndChangeCheck())
                //{
    
                //}
            }
    
            void OnSceneGUI() {
    
                VirtualJoystick vj = (VirtualJoystick)target;
    
                float radius = vj.GetRadius();
    
                // Draw the radius of the joystick.
                Handles.color = new Color(0, 1, 0, 0.1f);
                Handles.DrawSolidArc(vj.transform.position, Vector3.forward, Vector3.right, 360, radius);
                Handles.color = new Color(0, 1, 0, 0.5f);
                Handles.DrawWireArc(vj.transform.position, Vector3.forward, Vector3.right, 360, radius, 3f);
                
                // Draw the deadzone.
                Handles.color = new Color(1, 0, 0, 0.2f);
                Handles.DrawSolidArc(vj.transform.position, Vector3.forward, Vector3.right, 360, radius * vj.deadzone);
                Handles.color = new Color(1, 0, 0, 0.5f);
                Handles.DrawWireArc(vj.transform.position, Vector3.forward, Vector3.right, 360, radius * vj.deadzone, 3f);
    
                // Draw the boundaries of the joystick.
                if(vj.GetBounds().size.sqrMagnitude > 0) {
                    // Draw the lines of the bounds.
                    Handles.color = Color.yellow;
    
                    // Get the 4 points in the bounds.
                    Vector3 a = new Vector3(vj.boundaries.x, vj.boundaries.y),
                            b = new Vector3(vj.boundaries.x, vj.boundaries.y + Screen.height * vj.boundaries.height),
                            c = new Vector2(vj.boundaries.x + Screen.width * vj.boundaries.width, vj.boundaries.y + Screen.height * vj.boundaries.height),
                            d = new Vector3(vj.boundaries.x + Screen.width * vj.boundaries.width, vj.boundaries.y);
                    Handles.DrawLine(a,b);
                    Handles.DrawLine(b,c);
                    Handles.DrawLine(c,d);
                    Handles.DrawLine(d,a);
                }
    
                // Draw the direction anchors of the joystick.
                if(vj.directions > 0) {
                    Handles.color = Color.blue;
                    float partition = 360f / vj.directions;
                    for(int i = 0; i < vj.directions;i++) {
                        Handles.DrawLine(vj.transform.position, vj.transform.position + Quaternion.Euler(0,0,i*partition + vj.angleOffset) * Vector2.right * radius, 2f);
                    }
                }
            }
    
            // Function to return gcd of a and b
            int GCD(int a, int b) {
                if (b == 0) return a;
                return GCD(b, a % b);
            }
    
            // Function to find gcd of array of numbers
            int FindGCD(params int[] numbers) {
                if (numbers.Length == 0) {
                    Debug.LogError("No numbers provided");
                    return 0; // or handle the error in an appropriate way
                }
    
                int result = numbers[0];
                for (int i = 1; i < numbers.Length; i++) {
    
                    result = GCD(result, numbers[i]);
    
                    if (result == 1) {
                        return 1;
                    } else if (result <= 0) {
                        Debug.LogError("The size value for one or more of the Joystick elements is not more than 0");
                        // You might want to handle this error in an appropriate way
                        return 0; // or handle the error in an appropriate way
                    }
                }
                return result;
            }
    
            void RecordSizeChangeUndo(Object[] arguments) {
                for (int i = 0; i < arguments.Length; i++) {
                    Undo.RecordObject(arguments[i], "Undo Virtual Joystick Size Change");
                }
            }
        }
    }
    #15978
    Kaan Gül
    Level 3
    Participant
    Helpful?
    Up
    0
    ::

    Terence, hello again. First of all thanks again for this great asset. But unfortunately the problem still seems to persist…

    I hope I’m not boring you with this :/ The thing is, I’ve tried to make sure again and again that it’s really not me, but I think the asset is still working incorrectly. This time I will provide a visual to help you and explain the problem first;

    I have two joysticks in my game, both ‘Native’ joysticks. Unlike the previous problem, this time the joystick that should control the character controls the character, but the joystick that should control the character also controls the camera. (Although there is a separate joystick and code for the camera.) Below, I tried to provide as much visuals as I could;

    View post on imgur.com

    Additional information; * The joystick that should control the camera, I tested it with the “Console Print Axis” feature and the inputs work properly. * I do not access the joysticks from anywhere else. * I had manually assigned IDs to the joysticks before, then when this problem occurred, I did this by clicking the button that came with the new update, but it still did not fix. (Maybe I thought the problem was caused by this.)

    #15979
    Kaan Gül
    Level 3
    Participant
    Helpful?
    Up
    0
    ::

    I tested for about 1 hour whether the problem is really related to me or not, I tried to analyze your code even if I don’t understand advanced code, but to no avail. Fortunately, I was able to achieve the following;

    * I see that the joystick inputs work fine thanks to the print console feature of each joystick.

    * MOST IMPORTANTLY, even if I write 1 as id parameter to the GetAxisRaw function, it gives the input of 0.

    #15980
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Would you be comfortable with uploading a copy of your project files online (Google Drive or OneDrive) and sharing the link here? Let me have a look at your project.

    #15994
    Kaan Gül
    Level 3
    Participant
    Helpful?
    Up
    1
    ::

    Hello again, I guess my message that the problem is solved was not uploaded. Honestly, it seems that the problem is solved for me.

    Because I can access the ‘instances’ dictionary of the package directly and pull the input. (Ex: instances[0].axis.y)

    Let me give you some important information about my project;

    * I am using old input system in our project. Player > Input > Old Input is selected in project settings.

    * Our project is a WebGL project, we test our tests on both mobile device, editor and browser platform and this problem was persistent on every platform. (With my current solution, this has ended.)

    * We do not have any special operations related to Canvas and EventTrigger.

    Please let me know if there is any information I can message you from here. Thanks again.

    has upvoted this post.
    #15997
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    EDIT: Correction, this doesn’t fix it. Issue is still there. Will post again when it is found.

    Kaan, thank you for highlighting this issue to us. The issue is actually specific only to the 2 argument GetAxisRaw() function, and it is because we forgot to pass the ID in the second argument downwards! The green highlighted portion is missing, so whenever you use GetAxisRaw(), the 2nd argument is always ignored and it always selects the first joystick:

            public static float GetAxisRaw(string axe, int id = 0) {
                // Show an error if no joysticks are found.
                if (instances.Count <= 0)
                {
                    Debug.LogWarning("No active instance of Virtual Joystick found on the Scene.");
                    return 0;
                }
    
                return GetInstance(id).GetAxisRaw(axe, id);
            }

    Thank you once again for highlighting the issue.

    #16007
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Found the problem. Documented the issue here: https://blog.terresquall.com/community/topic/version-1-0-7-fix-for-horizontal-vertical-getaxisraw-bug/

    Will be releasing an update soon.

    Thanks again for highlighting this Kaan!

    #16024
    Kaan Gül
    Level 3
    Participant
    Helpful?
    Up
    0
    ::

    No problem at all! Thanks for taking care of it!

Viewing 8 posts - 1 through 8 (of 8 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: