• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Exalia · Oct 29, 2013 at 11:17 AM · androidvirtual

On Screen Analogue Stick Circular Boundary

Hi there,

I'm using a version of unity's on screen joystick and have got it working how I would like, however the boundary for the joystick is a rectangle and I would prefer a Circle.

I've looked a bit on the internet and I have tried to implement it myself by trying to set up the boundary as a radius but have failed :(

I was wondering if anyone could point me in the right direction.

here is my code

 using UnityEngine;
 using System.Collections;
 
 class AxisBoundary
 {
     public Vector2 min = Vector2.zero;
     public Vector2 max = Vector2.zero;
 }
 
 public class JoystickScript : MonoBehaviour
 {
     static private Joystick[] joysticks;
     static private bool enumeratedJoysticks = false;
     static private float tapTimeDelta = 0.3f;
 
     public bool touchPad;
     public Rect touchZone;
     public Vector2 deadZone = Vector2.zero;
     public bool normalize = false;
     public Vector2 position;
     public int tapCount;
 
     private int lastFingerId = -1;
     private float tapTimeWindow;
     private Vector2 fingerDownPos;
     private float fingerDownTime;
     private float firstDeltaTime = 0.5f;
 
     private GUITexture gui;
     private Rect defaultRect;
     private AxisBoundary guiBoundary = new AxisBoundary();
     private Vector2 guiTouchOffset;
     private Vector2 guiCenter;
 
     private float resRatio;
     public GameObject globalScripts;
 
     // Use this for initialization
     void Start() 
     {
         //Cache this texture
         gui = gameObject.guiTexture;
 
         globalScripts = GameObject.FindGameObjectWithTag("Global");
         ScreenResolutionScript resScript = globalScripts.GetComponent<ScreenResolutionScript>();
 
         resRatio = resScript.GetResRatio();
 
         gui.pixelInset = new Rect((Screen.width / 6) - (200 * resRatio), (Screen.height / 2 ), (200 * resRatio), (200 * resRatio));
 
         //Store the default rect for the gui
         defaultRect = gui.pixelInset;
 
         defaultRect.x += gameObject.transform.position.x * Screen.width;
         defaultRect.y += gameObject.transform.position.y * Screen.height;
 
         gameObject.transform.position = new Vector3(0.0f, 0.0f, gameObject.transform.position.z);
 
         if (touchPad)
         {
             //If a texture has been assigned, then use the rect from the gui
             if (gui.texture)
                 touchZone = defaultRect;
         }
         else
         {
             //corner of GUI
             guiTouchOffset.x = defaultRect.width * 0.5f;
             guiTouchOffset.y = defaultRect.height * 0.5f;
 
             //cache center of GUI
             guiCenter.x = defaultRect.x + guiTouchOffset.x;
             guiCenter.y = defaultRect.y + guiTouchOffset.y;
 
             //Build GUI boundary
             guiBoundary.min.x = defaultRect.x - guiTouchOffset.x;
             guiBoundary.max.x = defaultRect.x + guiTouchOffset.x;
             guiBoundary.min.y = defaultRect.y - guiTouchOffset.y;
             guiBoundary.max.y = defaultRect.y + guiTouchOffset.y;
         }
     }
 
     void Disable()
     {
         gameObject.SetActive(false);
         enumeratedJoysticks = false;
     }
 
     void ResetJoystick()
     {
         //Release the finger and reset joystick to default position
         gui.pixelInset = defaultRect;
         lastFingerId = -1;
         position = Vector2.zero;
         fingerDownPos = Vector2.zero;
 
         if (touchPad)
         {
             gui.color = new Color (gui.color.r, gui.color.g, gui.color.b, 0.025f);
         }
         else
         {
             gui.color = new Color (gui.color.r, gui.color.g, gui.color.b, 0.2f);
         }
     }
 
     bool IsFingerDown()
     {
         return (lastFingerId != -1);
     }
 
     void LatchedFinger(int fingerId)
     {
         //If another Joystick latched this finger, release it
         if (lastFingerId == fingerId)
         {
             ResetJoystick();
         }
     }
 
     // Update is called once per frame
     void Update()
     {
         if (!enumeratedJoysticks)
         {
             //Collect all joysticks
             joysticks = (Joystick[])FindObjectsOfType(typeof(Joystick));
             enumeratedJoysticks = true;
         }
 
         int count = Input.touchCount;
 
         //Adjust tap time window
         if (tapTimeWindow > 0)
         {
             tapTimeWindow -= Time.deltaTime;
         }
         else
         {
             tapCount = 0;
         }
 
         if (count == 0)
             ResetJoystick();
         else
         {
             for (int i = 0; i < count; i++)
             {
                 Touch touch = Input.GetTouch(i);
                 Vector2 guiTouchPos = touch.position - guiTouchOffset;
 
                 bool shouldLatchFinger = false;
 
                 if (touchPad)
                 {
                     if (touchZone.Contains(touch.position))
                     {
                         shouldLatchFinger = true;
                     }
                 }
                 else if (gui.HitTest(touch.position))
                 {
                     shouldLatchFinger = true;
                 }
 
                 //Latch finger if its a new touch
                 if (shouldLatchFinger && (lastFingerId == -1 || lastFingerId != touch.fingerId))
                 {
                     if (touchPad)
                     {
                         gui.color = new Color(gui.color.r, gui.color.g, gui.color.b, 0.15f);
 
                         lastFingerId = touch.fingerId;
                         fingerDownPos = touch.position;
                         fingerDownTime = Time.time;
                     }
 
                     lastFingerId = touch.fingerId;
 
                     //Count taps if they are within the time window
                     if (tapTimeWindow > 0)
                     {
                         tapCount++;
                     }
                     else
                     {
                         tapCount = 1;
                         tapTimeWindow = tapTimeDelta;
                     }
 
                     //Tell other joysticks finger is latched
                     foreach (Joystick j in joysticks)
                     {
                         if (j != this)
                         {
                             j.LatchedFinger(touch.fingerId);
                         }
                     }
                 }
 
                 if (lastFingerId == touch.fingerId)
                 {
                     if (touch.tapCount > tapCount)
                     {
                         tapCount = touch.tapCount;
                     }
 
                     if (touchPad)
                     {
                         //For touchpads set the position directly
                         position.x = Mathf.Clamp((touch.position.x - fingerDownPos.x) / (touchZone.width / 2), -1, 1);
                         position.y = Mathf.Clamp((touch.position.y - fingerDownPos.y) / (touchZone.height / 2), -1, 1);
                     }
                     else
                     {
                         //Change the position of the joystick GUI to match touch position
                         gui.pixelInset = new Rect(Mathf.Clamp(guiTouchPos.x, guiBoundary.min.x, guiBoundary.max.x), Mathf.Clamp(guiTouchPos.y, guiBoundary.min.y, guiBoundary.max.y), 200 * resRatio, 200 * resRatio);
                     }
 
                     if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
                     {
                         ResetJoystick();
                     }
                 }
             }
         }
 
         if (!touchPad)
         {
             //get a value between -1 and 1 based on joystick gui position
             position.x = (gui.pixelInset.x + guiTouchOffset.x - guiCenter.x) / guiTouchOffset.x;
             position.y = (gui.pixelInset.y + guiTouchOffset.y - guiCenter.y) / guiTouchOffset.y;
         }
 
         //Adjust for deadzone
         float absoluteX = Mathf.Abs(position.x);
         float absoluteY = Mathf.Abs(position.y);
 
         if (absoluteX < deadZone.x)
         {
             //Report the joystick as being at the center
             position.x = 0;
         }
         else if (normalize)
         {
             // Rescale the output after taking the dead zone into account
             position.x = Mathf.Sign(position.x) * (absoluteX - deadZone.x) / (1 - deadZone.x);
         }
 
         if (absoluteY < deadZone.y)
         {
             // Report the joystick as being at the center if it is within the dead zone
             position.y = 0;
         }
         else if (normalize)
         {
             // Rescale the output after taking the dead zone into account
             position.y = Mathf.Sign(position.y) * (absoluteY - deadZone.y) / (1 - deadZone.y);
         }
     }
 }
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
3
Best Answer

Answer by zombience · Nov 04, 2013 at 04:58 PM

One simple (not necessarily the best) way to do it would be to just compare the distance between your touch zone and the touch.

UPDATED ANSWER

this is a function that will allow you to clamp a point (touchPoint) so that it does not go beyond a maximum radius away from touchZone:

 public float maxRad;
     private Vector3 CheckRange(Vector3 touchPoint, Vector3 touchZone)
     {
         if (Vector3.Distance(touchPoint, touchZone) > maxRad)
         {
             touchPoint = touchZone - Vector3.ClampMagnitude((touchZone - touchPoint), maxRad);
         }
         return touchPoint;
     }


ORIGINAL ANSWER:

you could manually compare distances like so:

 float touchZoneRadius = 2f; // an arbitrary number i made up. you'd have to figure this part out
 if (touchPad)
 {
     if (Vector3.Distance(touchZone.position, touch.position) < touchZoneRadius)
     {
         shouldLatchFinger = true;
     }
 }
Comment
Add comment · Show 9 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Exalia · Nov 04, 2013 at 06:05 PM 0
Share

the radius is for the analogue stick not the touchPad but I assume it will working a similar way, I will try it tomorrow thanks :)

avatar image zombience · Nov 04, 2013 at 07:30 PM 1
Share

yup! should work the same. anytime

avatar image zombience · Nov 05, 2013 at 03:44 PM 1
Share

by "performs horribly" do you mean that it is causing speed/performance drop on your device? as far as the distance comparison, yes, that is basically what I meant. Although I wonder if you would get different results if ins$$anonymous$$d of a Vector2 you used a Vector3. I'm not sure. like so:

 if (Vector3.Distance(new Vector3(guiCenter.x, guiCenter.y, guiTouchPos.z), guiTouchPos) <= 100.0f)

Also, I haven't been over your code in depth. So let me see if I now understand what you want:

If you are inside a touch point, and touching, grab the "joystick". if you continue to touch and move, move the joystick with the finger. If the touchpoint exceeds the boundaries of the joystick allowed radius, clamp the joystick position to the radius.

is that correct? if so, i'll put together a small bit of code that would do that. it'll take a little bit of circle math. not too much though :)

avatar image zombience · Nov 05, 2013 at 04:56 PM 1
Share

I have updated my answer above. Sorry it took a $$anonymous$$ute, I got distracted by work ;)

At any rate, if I understand your concern correctly, this should solve the problem for you. It takes a point and constrains it to a maximum radius away from a target point.

avatar image zombience · Nov 06, 2013 at 06:12 PM 1
Share

Hmm, sorry for the confusion. I wrote a generic function that would take in a Vector3(in this case the location of a finger on screen, or touchPoint) and also a target Vector3(a point that could be "touched", such as a joystick).

if the distance between the touchPoint (your finger) exceeds the maximum allowed distance (maxRad) from the target (touchZone), the touchPoint will be clamped to the maximum radius.

you would want this code to be executed ONLY after you had deter$$anonymous$$ed that the touchZone was close enough to be "touched" in the first place, which it looks like you have already written.

I had written this code with the idea of moving the actual transform that contains your gui joystick. in my experience I have preferred to use transform movements since the transform position is relative and will scale well at different resolutions.

using the absolute pixel dimensions can be problematic when changing resolutions on different devices, unless you take care to solve for that.

moving the transform rather than pixelInset will also make it much easier to move the joystick in a circle, since you won't have to calculate circle coordinates manually. you can just use Unity's built-in Vector3 functions.

Show more comments

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

16 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Unity app not running in Android phone. 0 Answers

How can i run a Unity Build on my PC, but use my phone as second screen, (Cheapskate Oculus-Like) 2 Answers

GearVR wont initialize the scene (working fine in Editor) 0 Answers

Configuring android virtual device for Unity 1 Answer

Virtual reality apply music to different parts of the scene based on where user is looking at. 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges