Placeholder image
Create on-screen joystick in Unity 5
  posted 15 Nov, 2016
written by evansbsr

introduction

One of the many control systems used in mobile gaming is the on-screen joystick. It draws inspiration from real-world joysticks that consist of a knob capable of being rotated to give directional input. In this tutorial we will see how to create a virtual joystick with Unity 5. 

This tutorial will use the Unity 5 UI system so please ensure you have Unity 5.0 and above installed. Unity is downloadable for free from unity3d.com

download assets for tutorial

source assets

scene setup

Create a new Unity project. You should see a new scene open. Save this scene before we continue working with it. 

Copy the circle.png file from the source files provided above into the Assets folder. Click on the circle.png file in the Projects window to view it in the Inspector. In the image's import settings, set texture type to sprite and click apply to save the change.

 

Select the Main Camera object and set its Projection property to Orthographic. Set it's Clear Flags property so Solid Color.

 

creating display objects

Create a Canvas object by selecting Gameobject > UI > Canvas from the main menu. Change the Canvas object's Render Mode property to Screen Space - Camera. Change it's Render Camera property to Main Camera.

 

Create an image UI object by selecting Gameobject > UI > Image from the main menu. Make this Image object a child of the Canvas object. Rename the image to JoystickThis image should appear on-screen as a white box.

With the joystick image selected in the Hierarchy, drag the circle.png file from the Assets folder to the source image slot in the Image component in the Inspector.

You should see the image in the game view change as well

 

Now move the Joystick object to the bottom left of the screen. Set it's Rect Transform properties as follows:

  • Set PosX and posY to 100
  • Set Width and Height to 150
  • Set anchors Min and Max to X : 0.5 and Y : 0.5
  • Set pivot Min and Max to X : 0.5 and Y : 0.5

 

 

Create another image object by selecting Gameobject > UI > Image from the main menu. Rename this image to knob. In the Hierarchy drag this new image object onto the previously made joystick object to make the new image a child of the Joystick. 

With the Knob object selected in the Hierarchy, drag the circle.png file from the Assets folder to the source image slot in the Image component in the Inspector..

Set the Knob image's Rect Transform properties as follows:

  • Set PosX and posY to 0
  • Set Width and Height to 80
  • Set anchors Min and Max to X : 0.5 and Y : 0.5
  • Set pivot Min and Max to X : 0.5 and Y : 0.5

 

Right now the knob looks exactly like the joystick image. Change the knob's color value in the Image component in the Inspector to the following values:

R = 88, G = 83, B = 255, A = 255

Create a text object by selecting Gameobject > UI > Text from the main menu. Rename this text object to output. We will use this text object to display the information that our joystick will give us. In the Hierarchy drag the text object onto the joystick object to make it a child of the joystick. Set it's Rect Transform properties to the following: 

  • Set PosX to 0;
  • Set posY to 100
  • Set Width  to 160
  • Set Height to 30
  • Set anchors Min and Max to X : 0.5 and Y : 0.5
  • Set pivot Min and Max to X : 0.5 and Y : 0.5

Set the following properties on the Text component of the output text object:

  • Set Font Size to 24
  • Set Alignment to Center
  • Set Color to White

With the necessary objects created we can move on to scripting

 

code setup

Create a new folder in the Project window and call it Scripts. Inside it create a new C# file and call it Joystick.cs. Open this file in MonoDevelop and add the code below:

using UnityEngine; 
using UnityEngine.UI;

public class Joystick : MonoBehaviour { 

  Text outputText; 
  bool isPressed;  
 
  void Awake () {
    outputText = GetComponentInChildren< Text >(); 
  }
  
  void Update () {
    outputText.text = isPresseu.ToString();
  }

  // called by the OnPointerEnter event trigger
  public void onPointerEnter() { 
    isPressed = true;
  }

  // called by the onPointerExit event trigger
  public void onPointerExit() { 
    isPressed = false;
  }
}

In the code above we create a Text variable txt_output  to display the output of our Joystick script. We also create an isPressed variable to store the joystick is currently being pressed or hovered over. In the Awake() function we assign the txt_output variable to the gameObject. We then create a LateUpdate() function where we display the value of the isPressed variable. Awake and LateUpdate are both MonoBehaviour functions which Unity will call automatically. 

Next we create two functions: onPointerEnter and onPointerExit. These two functions will be called by Event Triggers which we will set on the Joystick object.  These functions set isPressed to true and false respectively.

 

create event triggers

Event triggers are used to send events to objects based on user input. Add the Event Trigger component to the Joystick object by selecting Component > Event > Event Trigger from the main menu. View the Joystick object in the Inspector and go to the Event Trigger component. Click the Add New Event Type button and select two events: PointerEnter and PointerExit. The component should look like this

 

 

Click on the ( + ) sign at the bottom of the PointerEnter element to add a new function. Drag the Joystick object from the Hierarchy to the new function's object slot. Next select the name of the Joystick script from the dropdown menu that says 'No Function'. In the sub-menu that opens under Joystick, select the onPointerEnter function that we have in the script.

 

Do the same for the PointerExit element but this time from the dropdown menu select the onPointerExit function. If you play the game you should see something like this:

The text object above the circle will say false when the mouse is not hovering over it and will say true when the mouse hovers over it.

 

get position of user input relative to joystick

Add the code below to Joystick.cs: 

{{ SerializeField }} Image imgCircle;
{{ SerializeField }} Image imgKnob;
{{ SerializeField }} Text outputText;

public Vector2 position;

Vector3  worldPos, localPos;
Camera   mainCamera;

float halfWidth, halfHeight;

In the code above we specify three variables which have the [SerializeField] attribute. This tells Unity to serialize the fields which makes them show up in the Inspector.  We set the position variable to Public as this will make it accessible from other scripts. This position variable is where we will store the final output of our Joystick script.

 

Add the following code to the Awake function we defined earlier. the camera in the scene. Make sure you have one camera object which has the tag 'MainCamera' in the scene.

mainCamera = GameObject.FindGameObjectWithTag( "MainCamera" ).GetComponent< Camera >();
halfWidth  = imgCircle.rectTransform.rect.width / 2;
halfHeight = imgCircle.rectTransform.rect.height / 2;

This code above will locate the camera object tagged 'MainCamera' and assign it to the mainCamera variable. We also assign the halfWidth and halfHeight variables to half the width and height of the imgCircle object. 

 

Next we create the LateUpdate() function:

void LateUpdate () { 
  worldPos = localPos = tempPos = new Vector2( 0, 0 );
  if (isPressed) {
    worldPos = mainCamera.ScreenToWorldPoint ( Input.mousePosition );
    localPos = imgCircle.rectTransform.InverseTransformPoint ( worldPos );

    imgKnob.rectTransform.transform.localPosition = new Vector2( localPos.x, localPos.y );

    position.x = localPos.x / halfWidth; 
    position.y = localPos.y / halfHeight;  
  } else {
    imgKnob.rectTransform.transform.localPosition = new Vector2( 0, 0 );
  } 
}

First we reset the values of worldPos, localPos and tempPos (Setting a Vector3 variable to a Vector2 sets the X and Y values but the Z value is set to zero). We then check the current state of the isPressed variable. If isPressed is true, we get the mousePosition property of the Input class and convert it into world space coordinates. The Input.mousePosition value is given in pixel (screen) coordinates. The ScreenToWorldPoint() converts the mouse position value from screen coordinates to world space coordinates. This means converting the coordinates from a position on the screen to its corresponding position in the Unity world. ScreenToWorldPoint() is a function in the Transform class. We store the world space position in the worldPos variable.

We then convert the world space coordinates to local coordinates using the InverseTransformPoint() function which is part of the RectTransform class. This function converts the world space coordinates of the input received into local space coordinates relative to the img_circle RectTransform object. With the local space coordinates we can now set the imgKnob object transform' localPosition property. This will position the object relative to it's parent object's position. This makes it so that when we hover over the Joystick image object with the mouse the imgKnob object will move to the current mouse position. 

The localPos variable gives us the information we need in terms of the position of the mouse relative to the imgCircle object. This is not enough as this position would change if the size of the imgCircle object changes. To get a consistent mouse position irrespective of the image size we divide the localPos.x variable by halfWIdth and the localPos.y variable by halfHeight. Earlier we defined halfWidth and halfHeight as half the width and height of the imgCircle object.

Change the code in the Update function to this:

void Update () {
    outputText.text = position.ToString();
 }

If you run the game at this point you should see the Joystick working. If you hover over the bottom-left of the Joystick object you get (-1,-1) while hovering over the top-right gives you (1,1) Also note that hovering over the center of the Joystick gives you an output of (0,0). Remember, the position variable is public meaning other scripts an access it.

There you have it. A simple Joystick you can use in your next game.This script will work with images of any size meaning you can create joysticks with different dimensions which all return consistent position values.

download completed project files

project files

Here is the code we have written in this tutorial

using UnityEngine; 
using UnityEngine.UI;

public class Joystick : MonoBehaviour { 

  {{ SerializeField }} Image imgCircle;
  {{ SerializeField }} Image imgKnob;
  {{ SerializeField }} Text outputText;

  public Vector2  position;

  Vector3  worldPos, localPos;
  Camera   mainCamera;

  float halfWidth, halfHeight;
   
  bool isPressed;  
 
  void Awake() {
    outputText = GetComponentInChildren< Text >(); 
    mainCamera = GameObject.FindGameObjectWithTag( ["]MainCamera["] ).GetComponent< Camera > (); 

    halfWidth  = imgCircle.rectTransform.rect.width / 2;
    halfHeight = imgCircle.rectTransform.rect.height / 2;

    print (halfWidth + ", " + halfHeight );
  } 

  void Update () {
    outputText.text = position.ToString();
  }

  void LateUpdate () { 
    worldPos = localPos = position = new Vector2( 0, 0 );
    if (isPressed) {
      worldPos = mainCamera.ScreenToWorldPoint ( Input.mousePosition ); 
      localPos = imgCircle.rectTransform.InverseTransformPoint ( worldPos );

      position.x = localPos.x / halfWidth; 
      position.y = localPos.y / halfHeight;  

      imgKnob.rectTransform.transform.localPosition = new Vector2 ( localPos.x, localPos.y );
  } else { 
      imgKnob.rectTransform.transform.localPosition = new Vector2 ( 0, 0 ) ;
  } 
  }
    
  // called by the OnPointerEnter event
  public void onPointerEnter() { 
    isPressed = true;
  }

  // called by the onPointerExit event
  public void onPointerExit() {
    isPressed = false;
  }
}