A lot of mobile games allow you to change your button layouts which will be preferable for your playing style. This system is hugely adapted by popular games like PUBG and Call of Duty to extend support to their players to play the game with more than two fingers. In this article, we will be creating a similar system to PUBG controls customization in Unity where users will be able to replace, resize and change the opacity of the buttons and then save it for later sessions.
After you complete this article step-by-step, your end result should look like below:
Getting Started with the basics
To start off with the project I created a basic UI with a bunch of buttons and images and placed them in a UI canvas. I wrote a script called GameButton.cs
As you can see the script above implements two interfaces IDragHandler
and IPointerDownHandler
, we are using these two interfaces so that we can get OnDrag
and OnPointerDown
functions. In the OnDrag
the function we set the position of the transform to the position of our mouse cursor that is input.mousePosition
, this is done to move the dragged object with our cursor as OnDrag
is called by events system every time a pointer is dragging something. For more information about OnDrag
and IDragHandler
check Unity's official documentation.
After doing this you need to assign this script to the object you want to move and then you should be able to move your dragged object around the screen. In the update function, we call calculate position every frame in order to clamp the object's movement to the ends of the device's screen. After that is done we will create a function called SetSizeAndAlpha
that takes two floats size and alpha, this will later be used with the uiManager
to set the button's size and transparency.
After the implementation of the Drag, we also implement another interface IPointerDownHandler
which is later used to assign our button's instance and set its data every time we end our drag and leave the button on the screen.
Handling the data with UIManager
In order to change the data like size and transparency of the buttons, we will be using a UIManager
the class that handles all the references related to the data.
We will now be creating an empty gameObject
and assigning this UIManager
script to it, then assign our sliders references through the inspector. Make sure to limit your slider values according to your wish. In this script, you can see we have two references to the slider SizeSlider
and TransparencySlider
, these sliders are used to set our size and transparency values. You can also see a list of type gameButtons we have created this is where we will be saving all our GameButton instances throughout our game runtime. In the start initialization, we have used FindObjectsOfType
<GameButton> which returns us an array of the game button instances in our current runtime and then we change the array into a list and assign it to the GameButtons list. After that, we have added a listener to our size and transparency sliders OnUpdate(float value) which updates every single time we change our slider's value or use it. In OnUpdate
we access our current gameButton
and set its size and alpha to their respective slider's value. If you remember we did this in our gameButton
script every time the pointer was down.
So what we are doing is we are telling the UIManager
that every time we drag on a GameButton
and release it our current edited button is this current gameobject
. This helps us find our reference to the dragged button and know its edited value. We also have called _uiManager
SetButtonData
and sent the button's scale on the X-axis and it's canvas group alpha so that we can set the current values of the button to the slider.
Saving and loading the data
After doing all this stuff you should be able to move, change opacity and scale the buttons as per your wish but after you quit out from your unity play mode you might notice it getting back to its default position, so for that, we will have to create a save and load system. For this save and load system we will be using PlayerPrefs and JSONUtility. So to get started we first create a class with all the necessary data types that need to be saved.
[Serializable]
public class ButtonData
{
public string id="";
public float Size=1f;
public float Alpha=1f;
public float PosX=0f;
public float PosY=0f;
}
Here, we have created a class called ButtonData
that is serializable and contains the necessary data that needs to be saved. id
is our unique button identifier, Size
is our button's size, Alpha
is our button's transparency and PosX
and PosY
are our rect
transforms anchoredPosition
values. In order to save the data for each button, we then create a function called SaveData
in our GameButton
script.
In this SaveData
the function we create a new object of our class ButtonData
and then assign its variables with our data values. Here we assign id
with our gameObject's name, Size with the gameObject current localScaleX
value, Alpha with the gameObject Canvas Group's alpha value, and its PosX and PosY with the button's recttransform
anchored position. After assigning the value we use playerprefs
to save the class into a JSON with its unique id. You can see we are packing the values into a JSON and saving it as a string in prefs so that we will not have to assign all those data individually and it also makes the data managed. You might notice that we have added the – id – to our constant string, we are doing it in order to save our button's data individually and access the unique data with our button's id. After the function is written we go to our UIManager and add a function.
You can later assign this to a button's event and this will loop through the list of all our GameButtons
and call their SaveData
Function.
Now after the Save Part is done, we need to find a way to load the data so that we will create a new function in our GameButton.cs
called LoadData()
.
Here in this function, we do the same thing as SaveData but in reverse, we create a local scope variable with type ButtonData where we use JsonUtility.FromJSON
to unpack the data that we previously saved in our playerprefs
. Then we check if the btnData
is null and if it is we return from there but it isn't null we set the button's size and alpha to btnData.Size and btnData.Alpha
which is accessed through the unpacked JSON to object data, we also set the rectTransfrom's anchored position to a new Vector2 data with the saved PosX and PosY. We call the LoadData
function in the start so that the data is automatically loaded in the first init
.
Resetting the data
The above function is used to reset the button to its default state, you can see we have set the size and alpha to 1f which is our default size and opacity and we have set the rectTransform anchoredPosition
to the DefaultPosition
which can be assigned through the inspector. InOrder to call this function on every button on the reset button click you can add the function below to the UIManager.cs and assign it to the reset button.