1
\$\begingroup\$

I wanted to create a virtual analogue stick on Unity. enter image description here

I figured a very simple, 2 part method setting up a tether to the middle of a doughnut-container and a boundary would work. While the tether was quickly solved just by using Vector2.MoveTowards, the boundaries would be much harder to solve.

I initially tried to create a 2D circle collider around the doughnut-container but I didn't know how to reverse its collisions to keep the cursor inside. Later, I tried to set up a square shaped boundary by setting up the limits of the x and y coordinates the cursor cannot exceed (these limits were also made according to the doughnut-container). Thus was done with multiple if statements:

 if (currentPoint.y >= 2.7f){currentPoint.y = 2.7f;}
 if (currentPoint.x >= 2.5f){currentPoint.x = 2.5f;}
 if (currentPoint.y <= -2.7f){currentPoint.y = -2.7f;}
 if (currentPoint.x <= -2.5f){currentPoint.x = -2.5f;}

However, when testing the code out, the cursor still exceeded the boundaries given:

enter image description here

Minimum reproducible code for the cursor:

public class menuScript : MonoBehaviour
{
    public float speed;
    public Vector2 originPoint;
    private Vector2 currentPoint;
    // Start is called before the first frame update
    void Start()
    {
        Vector2 originPoint = transform.position;
    }

    // Update is called once per frame
    void Update()
    {
        currentPoint = transform.position;
        float step = 50 * Time.deltaTime;
        float vertical = Input.GetAxis("Vertical") * Time.deltaTime * speed;
        float horizontal = Input.GetAxis("Horizontal") * Time.deltaTime * speed;

        if (horizontal != 0 || vertical != 0)
        {
            transform.Translate(horizontal, vertical, 0);
            if (currentPoint.y >= 2.7f){currentPoint.y = 2.7f;}
            if (currentPoint.x >= 2.5f){currentPoint.x = 2.5f;}
            if (currentPoint.y <= -2.7f){currentPoint.y = -2.7f;}
            if (currentPoint.x <= -2.5f){currentPoint.x = -2.5f;}
        }
        else
        {
            transform.position = Vector2.MoveTowards(transform.position, originPoint, step);
        }
    }
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Your if-else block for constraining the position of the stick looks like this:

if (currentPoint.y >= 2.7f){currentPoint.y = 2.7f;}
if (currentPoint.x >= 2.5f){currentPoint.x = 2.5f;}
if (currentPoint.y <= -2.7f){currentPoint.y = -2.7f;}
if (currentPoint.x <= -2.5f){currentPoint.x = -2.5f;}

This is going to lock the stick in a square region, not a circular region as you probably intend. There are several ways to enforce a region that is actually circular. Here's a simple one:

float radius = 2.5f;
float distance = Vector3.Distance(transform.position, originPoint);
float excess = distance - radius;
if (excess > 0) transform.position = Vector2.MoveTowards(transform.position, originPoint, excess);

This checks if the distance to the center is further than the maximum radius, and if so moves back towards the center the exact distance that is needed to put it on the edge of the circle. For example, if the radius is 2.5 but the measured distance is 3.7, it will move the stick 1.2 units back towards the center.

It's also worth noting there are numerous virtual joystick packages on the Unity Asset Store, and there is one included in Unity Standard Assets.

EDIT: As noted by DMGregory, you can also do this:

float radius = 2.5f;
Vector2 offset = transform.position - originPoint;
offset = Vector2.ClampMagnitude(offset, radius);
transform.position = originPoint + offset;

or, if stick is centered when its local position is (0, 0)

float radius = 2.5f;
transform.localPosition = Vector2.ClampMagnitude(transform.localPosition, radius);
\$\endgroup\$
2
  • \$\begingroup\$ Worked like a charm. There is a slight issue brought in as the cursor sticks to the edges for about a second or so before coming back, but that would be outside of the constraints of this question. For why I wasn't using virtual joystick pads from the store, I just wanted to make one myself. \$\endgroup\$ Jul 8, 2020 at 5:22
  • 1
    \$\begingroup\$ You can use Vector2.ClampMagnitude for this instead for a slightly more concise solution. \$\endgroup\$
    – DMGregory
    Jul 8, 2020 at 11:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .