The joystick controller is the most used controller to move players in-game. Today we will be creating a joystick with a touch area control that can be enabled and disabled while playing.

We can divide this process into 2 steps.

  1. UI Setup in Unity Editor
  2. Scripting

UI Setup in Unity Editor

Create a canvas from the Unity UI. In the newly created canvas, create an image from UI > Image and give it the name JoyStick. This GameObject will have a joystick script, and its image area will determine the touch area for the joystick.

Inside JoyStick, create another empty GameObject with the name JoyStickParent. This gameObject will be used to enable and disable the joystick.

Inside JoyStickParent, create another image from UI > Image. Name this image OuterCircle and change both its height and width to 300.

Inside OuterCircle, create another image and name it InnerCircle. Set both height and width of this image to 150.

Now, set the Rect Transform of InnerCircle and OuterCircle to the centre.

Joystick Editor Setup

Scripting

Now that we have the UI of the joystick set let's move on to making it actually work. For that, we will need to create a joystick script and a JoystickReader script.

joystick Script

This script will read the user input, set the position of outerCircle and innerCircle, and has action onJoyStickMoved which provides our input as vector2.

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System;
public class joystick : MonoBehaviour, IDragHandler, IPointerUpHandler, IPointerDownHandler
{

	private Image outerCircle;//Outer boundary of joystick.
	private float bgImageSizeX, bgImageSizey;
	private Image innerCircle;//Inner circle of joystick.
	public JoystickDirection joyStickDirection;
	/// <summary>
	/// This defines how far joystick inner circle can move with respect to outer circle.
	/// Since inner circle needs to move only half size distance of outer circle default value is its half size i.e 0.5
	/// </summary>
	float offsetFactorWithBgSize = 0.5f;
	public static event Action<Vector2> onJoyStickMoved;

	public Vector2 InputDirection { set; get; }


	private void Start()
	{

		outerCircle = transform.GetChild(0).GetChild(0).GetComponent<Image>();
		innerCircle = transform.GetChild(0).GetChild(0).GetChild(0).GetComponent<Image>();
		bgImageSizeX = outerCircle.rectTransform.sizeDelta.x;
		bgImageSizey = outerCircle.rectTransform.sizeDelta.y;
	}


	/// <summary>
	/// Unity function called when we are tapping and moving finger in screen.
	/// </summary>
	/// <param name="ped"></param>
	public void OnDrag(PointerEventData ped)
	{
		Vector2 tappedpOint;
		//This if statment gives local position of the pointer at "out touchPoint"
		//if we press or touched inside the area of outerCircle
		if (RectTransformUtility.ScreenPointToLocalPointInRectangle
			(outerCircle.rectTransform, ped.position, ped.pressEventCamera, out tappedpOint))
		{

			//Getting tappedPoint position in fraction where  maxmimum value would be in denominator of below fraction.
			tappedpOint.x = (tappedpOint.x / (bgImageSizeX * offsetFactorWithBgSize));
			tappedpOint.y = (tappedpOint.y / (bgImageSizey * offsetFactorWithBgSize));

			SetJoyStickDirection(tappedpOint.x, tappedpOint.y);//Updates InputDirection value.
															   //Limit value of InputDirection between 0 and 1.
			InputDirection = InputDirection.magnitude > 1 ? InputDirection.normalized : InputDirection;
			//Updating position of inner circle of joystick.
			innerCircle.rectTransform.anchoredPosition =
				new Vector3(InputDirection.x * (outerCircle.rectTransform.sizeDelta.x * offsetFactorWithBgSize),
					InputDirection.y * (outerCircle.rectTransform.sizeDelta.y * offsetFactorWithBgSize));


			onJoyStickMoved?.Invoke(InputDirection);

		}
	}
	public GameObject joyStickparent;
	/// <summary>
	/// Unity function called when we tapped on the screen.
	/// Here we enable joystick at initial press point.
	/// </summary>
	public virtual void OnPointerDown(PointerEventData ped)
	{

		Vector2 initMousePos =ped.pressEventCamera.ScreenToWorldPoint(Input.mousePosition);
		joyStickparent.SetActive(true);

		joyStickparent.transform.position = initMousePos;
		OnDrag(ped);

	}

	/// <summary>
	/// Unity function called when mouse button is not pressed or no touch detected in touch screen device.
	///Disabling joystick and resetting joystick innerCircle to zero position.
	/// </summary>
	public virtual void OnPointerUp(PointerEventData ped)
	{

		InputDirection = Vector2.zero;
		innerCircle.rectTransform.anchoredPosition = Vector3.zero;
		joyStickparent.gameObject.SetActive(false);
		onJoyStickMoved?.Invoke(InputDirection);
	}

	/// <summary>
	/// Changes input direction value based on joy stick direction we have set.
	/// </summary>
	/// <param name="x"></param>
	/// <param name="y"></param>
	public void SetJoyStickDirection(float x, float y)
	{
		if (joyStickDirection == JoystickDirection.Both)
		{
			// For both horizonatal and vertical directional joystick
			InputDirection = new Vector3(x, y);
		}
		else if (joyStickDirection == JoystickDirection.Vertical)
		{
			//for y directional joystick
			InputDirection = new Vector3(0, y);
		}
		else if (joyStickDirection == JoystickDirection.Horizontal)
		{
			//for x dirctional joystick
			InputDirection = new Vector3(x, 0);
		}
	}

}
public enum JoystickDirection
{
	Horizontal,
	Vertical,
	Both
}
JoyStick Control Script

Make sure to read the code comments to see how the code works.

Now attach this script to the JoyStick GameObject in canvas.

JoystickReader Script

This script will get the input of the joystick as vector2. Then, we can use it to move the player or move the camera, whichever is required.

Create an empty GameObject outside the canvas in the editor and name it as JoystickReader. You can now attach the script shown below to the newly created GameObject. Now, when we play this game, we will see the input value in JoystickReader's component as we touch or press in the white image area of JoyStick .


public class JoystickReader : MonoBehaviour
{
     public   Vector2 touchDirection = Vector2.zero;
    private void Start()
    {
    //Subscribe to the action in JoyStick.cs
        joystick.onJoyStickMoved += GetJoyStickDirection;
    }
 
    void GetJoyStickDirection(Vector2 touchPosition)
    {
    //Touch direction updating every time joystick is moved.
        touchDirection = touchPosition;
        //Call player move function here to move player.
    }
}
JoyStick Reader Script

This is how we read input from the JoyStick class. We get values (0,1) when the joystick moves right and (-1,0) when the joystick moves left.

That's all for now and I hope you learned a new thing today! Stay tuned for the next one.