Creating a game menu using Unity UI (User interface)
Since Unity introduced a new UI system (User interface), it has become much easier and more convenient to create a user interface. Now all the elements of this interface have become separate objects, and since then you can find a special approach to each element in your work.
In this article, using the UI system, we will create a simple, unfolding menu, similar to the animation below.
According to the principle of operation, it somewhat resembles the settings panel in the upper corner of the smartphone screen.
Let's start with canvas – our canvas. We already have a camera on the stage, now we will also add a Canvas object, which can be found in the GameObject -> UI section. Since the canvas will be tied to one camera, in the Canvas component settings we will specify the Screen Space - Camera mode in the Render Mode field, and add the camera to the Render Camera field that appears.
That's it, now we will have canvas to process only events coming from the specified camera. Next, inside the canvas, we will create a simple panel - Panel, which will contain the game menu. We will also add another panel in which we will place the image of the "game".
Settings.
The most important thing in the operation of the masking menu is the correct configuration of its elements. Each UI element has Anchors Presets in the RectTransform component that help bind elements relative to each other.
By default, the Panel element is bound as a “full” stretch, that is, it fills the entire area of the parent element, regardless of the size of the latter. So, let's continue with the panel, where we will add several UI buttons. For each button in the Anchors Presets settings, we will set the offset from the top, since the menu bar will expand from top to bottom.
Add the Mask component to the panel itself to make a mask out of it. This component can be found in the Component -> UI -> Mask section, or found by name via the Add Component menu. You can test the operation of the menu mask by moving its lower edge and changing its size.
Now, in the same place, in the canvas, we will add a new Image element that will serve as a toggle switch, for example, we will select the arrow image. We will use this element to expand the panel. Place it at the top edge of the menu bar.

1
Actions of elements.
The operation of the toggle switch is to move between the lower and upper edges of the menu bar on the canvas. These edges will be determined by the height of the panel itself. And so, after completing the configuration of the canvas elements, you can proceed to the program part.
Let's create a new Custom Slider script inherited from the UIBehaviour class, this class can be found by connecting the EventSystem library.

  1. using UnityEngine.EventSystem;
  2. public sealed class CustomSlider : UIBehaviour {}

Далее объявим новую переменную fillRect, которая будет ссылаться на RectTransform панели меню.

  1. using UnityEngine.EventSystem;
  2. public sealed class CustomSlider : UIBehaviour {
  3. public RectTransform fillRect;
  4. }

Now add the Custom Slider script to the toggle switch and place the menu bar in the fillRect field.
Since we will move the toggle switch itself across the canvas by dragging it across the screen, we will need to use additional tools for handling touch events. To do this, the UI system has a dozen interfaces that will help process every user action, including touching, moving, clicking, and so on. In this example, we will need only three of them, these are IBeginDragHandler – to handle the drag start event, IDragHandler - to handle the drag itself, and IEndDragHandler - to handle the end of dragging the element.

We inherit the Custom Slider script from each of the three selected interfaces and then implement all their methods.
  1. using UnityEngine.EventSystem;
  2. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  3. public RectTransform fillRect;
  4. public void OnBeginDrag(PointerEventData eventData) {}
  5. public void OnDrag(PointerEventData eventData) {}
  6. public void OnEndDrag(PointerEventData eventData) {}
  7. }
The OnBeginDrag method will work before dragging the toggle switch, the onDrag method will handle the dragging process itself, and the OnEndDrag method will work after we finish dragging the element.
Each method takes a PointerEventData parameter, this class contains a lot of useful data from each event, but we will only need two of them: this is the camera from which the touch event was received and the touch position itself.
To check the script's operability, let's try to move the toggle switch in the onDrag method. To do this, you will only need to translate the touch position on the screen to the world point on the stage, and then to the local point relative to the canvas, it looks like this:

  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. public void OnBeginDrag(PointerEventData eventData) {}
  4. public void OnDrag(PointerEventData eventData) {
  5. Camera eventCam = eventData.pressEventCamera;
  6. Vector2 worldPoint = eventCam.ScreenToWorldPoint(eventData.position);
  7. Vector2 localPoint = this.canvas.transform.InverseTransformPoint(worldPoint);
  8. this.transform.localPosition = localPoint;
  9. }
  10. public void OnEndDrag(PointerEventData eventData) {}
  11. }
Having obtained the local position of the localPoint using the InverseTransformPoint Transform canvas method, we apply these coordinates of the toggle switch Transform. Now you can start and try to “move” the toggle switch on the screen.
Great, everything works – events are processed correctly, you can continue working with the script.
To begin with, we will erase everything that we wrote in the onDrag method and add a few new variables: canvasRect and RectTransform, which will point to the canvas and toggle switch Transforms.

  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. public void OnBeginDrag(PointerEventData eventData) {}
  5. public void OnDrag(PointerEventData eventData) {}
  6. public void OnEndDrag(PointerEventData eventData) {}
  7. }
Next, we will need to store the height of the menu bar in the fill Height variable, the height of the imageHeight toggle switch image, the lower limit in the minPosY variable, the upper limit in maxPosY and the current toggle switch height in the targetPosY variable.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, imageHeight, minPosY, maxPosY, targetPosY;
  5. public void OnBeginDrag(PointerEventData eventData) {}
  6. public void OnDrag(PointerEventData eventData) {}
  7. public void OnEndDrag(PointerEventData eventData) {}
  8. }
These variables will be enough for us to start. Next, in the Start method, we will collect data.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
  5. protected override void Start() {}
  6. /*…остальной код…*/
  7. }
Let's start by defining the variables Transform canvas and toggle switch. In order to find the canvas, we will use the GetComponentInParent method.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
  5. protected override void Start() {
  6. Canvas canvas = GetComponentInParent<Canvas>();
  7. this.canvasRect = canvas.transform as RectTransform;
  8. this.rectTransform = this.transform as RectTransform;
  9. }
  10. /*…остальной код…*/
  11. }
Next, in the Start method, we will determine the dimensions of the menu bar and toggle switch, but we will do this in two different ways.
The fact is that the toggle switch in the settings of the RectTransform component specifies its length and height (Width and Height), and the menu bar only has left, top, right and bottom margins (Left, top, right and bottom), that is, the panel will always, regardless of the size of the screen, fill the maximum of the parent space (in this case, the entire canvas). Therefore, it will not work to find out the dimensions of the panel through the length and height, like a toggle switch, for this you will have to use a different approach, namely, to find the location of the edges of the panel on the canvas.
And so, let's start with the height of the toggle switch, everything is simple here – we use the sizeDelta property of its Transform.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
  5. protected override void Start() {
  6. Canvas canvas = GetComponentInParent<Canvas>();
  7. this.canvasRect = canvas.transform as RectTransform;
  8. this.rectTransform = this.transform as RectTransform;
  9. this.imageHeight = this.rectTransform.sizeDelta.y;
  10. }
  11. /*…остальной код…*/
  12. }
To find the edges of the menu bar, we need a small array of Vector3 vectors and the GetLocalCorners method, which will return all four corners of the Transform panel.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
  5. protected override void Start() {
  6. Canvas canvas = GetComponentInParent<Canvas>();
  7. this.canvasRect = canvas.transform as RectTransform;
  8. this.rectTransform = this.transform as RectTransform;
  9. this.imageHeight = this.rectTransform.sizeDelta.y;
  10. Vector3[] fillCorners = new Vector3[4];
  11. this.fillRect.GetLocalCorners(fillCorners);
  12. }
  13. /*…остальной код…*/
  14. }
In general, all corners of any UI element are arranged clockwise starting from the lower left edge of the element.
Now, using the edges of the panel, we will determine its dimensions, as well as the lower and upper limits.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
  5. protected override void Start() {
  6. Canvas canvas = GetComponentInParent<Canvas>();
  7. this.canvasRect = canvas.transform as RectTransform;
  8. this.rectTransform = this.transform as RectTransform;
  9. this.imageHeight = this.rectTransform.sizeDelta.y;
  10. Vector3[] fillCorners = new Vector3[4];
  11. this.fillRect.GetLocalCorners(fillCorners);
  12. this.fillHeight = Mathf.Abs(fillCorners[0].y * 2f);
  13. this.maxPosY = fillCorners[0].y + this.imageHeight / 2f;
  14. this.minPosY = fillCorners[1].y – this.imageHeight / 2f;
  15. }
  16. /*…остальной код…*/
  17. }
For the fillHeight height, we use the lower left corner of the panel under the index 0 of the fillCorners array multiplied by 2. The lower limit of minPosY will be located in the lower left corner of the panel, the upper limit of maxPosY - in the upper left corner, respectively.
That's it, after executing the Start method, we will get all the necessary data about the panel and toggle switch elements.
The very movement of the toggle switch will now be handled in the Update method, since, unlike the onDrag method, the Update method always works, and not only when we perform the "drag" action. Therefore, we will add a new Update method, and two additional methods for handling the movement of the updatePosition toggle switch and filling the UpdateFill panel.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
  5. /*…остальной код…*/
  6. private void Update() {
  7. UpdateFill();
  8. UpdatePosition();
  9. }
  10. private void UpdateFill() {}
  11. private void UpdatePosition() {}
  12. /*…остальной код…*/
  13. }
To move the toggle switch, we will use the variable target PosY, which we will fill in the Ondraw method.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
  5. /*…остальной код…*/
  6. private void Update() {
  7. UpdateFill();
  8. UpdatePosition();
  9. }
  10. private void UpdateFill() {}
  11. private void UpdatePosition() {}
  12. public void OnDrag(PointerEventData eventData) {
  13. Camera eventCam = eventData.pressEventCamera;
  14. Vector2 worldPoint = eventCam.ScreenToWorldPoint(eventData.position);
  15. Vector2 localPoint = this.canvasRect.InverseTransformPoint(worldPoint);
  16. this.targetPosY = localPoint.y;
  17. }
  18. /*…остальной код…*/
  19. }
As before, in the Ondraw method, we first find the world coordinates of the world Point touch, after which we convert them to localPoint relative to the canvas, and set the targetPosY variable to the new position of the toggle switch in height. Then we go to the updatePosition method, where we will move the toggle switch itself.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
  5. /*…остальной код…*/
  6. private void Update() {
  7. UpdateFill();
  8. UpdatePosition();
  9. }
  10. private void UpdateFill() {}
  11. private void UpdatePosition() {
  12. Vector2 currentPos = this.rectTransform.localPosition;
  13. float yPos = Mathf.Clamp(this.targetPosY, this.maxPosY, this.minPosY);
  14. this.rectTransform.localPosition = new Vector2(currentPos.x, yPos);
  15. }
  16. /*…остальной код…*/
  17. }
Here, too, everything is simple, first we put the current position of the toggle switch into the currentPos variable, after which we write a new position into the yPos variable, taking into account the limits of maxPosY and minPosY, and at the end we apply the coordinates of the local position of the toggle switch Transform. And so, now the toggle switch moves along the canvas, taking into account the minimum and maximum height of the panel, it remains only to determine how much we should expand the menu bar depending on the location of the toggle switch on the canvas.
For this task, we will use simple arithmetic: calculate the number of the way traveled by the toggle switch from the upper limit to the lower, convert the resulting value into a percentage and multiply by the height of the panel, which we know in advance.
Let's write a new GetFillValue method in the CustomSlider script, with which we will get the number of paths traveled as a value from 0 to 1, where zero will show that the toggle switch is at the very beginning of the path, and 1 – that at the very end.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
  5. /*…остальной код…*/
  6. private void Update() {
  7. UpdateFill();
  8. UpdatePosition();
  9. }
  10. private void UpdateFill() {}
  11. private float GetFillValue() {
  12. float currentYPos = this.rectTransform.localPosition.y;
  13. float diff = currentYPos – this.minPosY;
  14. float result = -(diff / (this.fillHeight – this.imageHeight));
  15. return result;
  16. }
  17. /*…остальной код…*/
  18. }
To find the ratio between the traveled path and the total distance, it is necessary to know the current position of the currentYPos toggle switch and the diff difference between the lower and upper limit, taking into account the height of the toggle switch.
Go to the UpdateFill method, where we will fill our menu bar taking into account the path traveled.

  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
  5. /*…остальной код…*/
  6. private void Update() {
  7. UpdateFill();
  8. UpdatePosition();
  9. }
  10. private void UpdateFill() {
  11. float value = GetFillValue();
  12. float newSizeY = this.fillHeight * value;
  13. }
  14. /*…остальной код…*/
  15. }
To begin with, we will write the number of the traveled path in the value variable with a toggle switch, then in the new size variable we will set the height to which the menu bar needs to be expanded. It remains only to set the new height of the menu bar, for which we will use the special SetInsetAndSizeFromParentEdge method, which allows you to resize the UI element from a certain edge of the RectTransform.Edge.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. public RectTransform fillRect;
  3. private RectTransform canvasRect, rectTransform;
  4. private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
  5. /*…остальной код…*/
  6. private void Update() {
  7. UpdateFill();
  8. UpdatePosition();
  9. }
  10. private void UpdateFill() {
  11. float value = GetFillValue();
  12. float newSizeY = this.fillHeight * value;
  13. RectTransform.Edge edge = RectTransform.Edge.Top;
  14. this.fillRect.SetInsetAndSizeFromParentEdge(edge, 0f, newSizeY);
  15. }
  16. /*…остальной код…*/
  17. }
In our case, since the panel will expand from top to bottom, it is necessary to use the upper edge of Reset Transform.Edge.Top. Now let's try to test what we've got. If you have done everything the same as in the example, then the result should be something like this.
The toggle switch moves perfectly between borders, and the panel, depending on the location of the toggle switch on the screen, changes its size.

2
Opening and closing the panel.
To perform automatic actions of opening and closing the panel, without dragging the toggle switch manually, you need to add two new methods Open and Close, and a pair of boolean variables isDragging and isOpen.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. /*…остальной код…*/
  3. private bool isDragging, isOpen;
  4. public void Open() {}
  5. public void Close() {}
  6. /*…остальной код…*/
  7. }
By calling any of these methods, the isOpen variable will change, and depending on what value it takes, true or false, the panel will open or close.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. /*…остальной код…*/
  3. private bool isDragging, isOpen;
  4. public void Open() {
  5. if (this.isOpen == false && this.isDragging == false) this.isOpen = true;
  6. }
  7. public void Close() {
  8. if (this.isOpen && this.isDragging == false) this.isOpen = false;
  9. }
  10. /*…остальной код…*/
  11. }
The isDragging variable will indicate whether we are dragging the toggle switch manually or not, and we will change it in the OnBeginDrag and OnEndDrag methods before and after dragging the toggle switch.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. /*…остальной код…*/
  3. private bool isDragging, isOpen;
  4. public void Open() {
  5. if (this.isOpen == false && this.isDragging == false) this.isOpen = true;
  6. }
  7. public void Close() {
  8. if (this.isOpen && this.isDragging == false) this.isOpen = false;
  9. }
  10. public void OnBeginDrag(PointerEventData eventData) {
  11. this.isDragging = true;
  12. }
  13. public void OnEndDrag(PointerEventData eventData) {
  14. this.isDragging = false;
  15. }
  16. /*…остальной код…*/
  17. }
Actions to automatically open or close the panel will be performed in the updatePosition method.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. /*…остальной код…*/
  3. private void UpdatePosition() {
  4. Vector2 currentPos = this.rectTransform.localPosition;
  5. if (this.isDragging == false) {
  6. }
  7. float yPos = Mathf.Clamp(this.targetPosY, this.maxPosY, this.minPosY);
  8. this.rectTransform.localPosition = new Vector2(currentPos.x, yPos);
  9. }
  10. /*…остальной код…*/
  11. }
After determining the current position of the toggle switch in the currentPos variable, we add a new condition that when we do NOT drag the toggle switch manually, that is, the isDragging variable is false, it is necessary to close or open the panel automatically.
  1. public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
  2. /*…остальной код…*/
  3. private void UpdatePosition() {
  4. Vector2 currentPos = this.rectTransform.localPosition;
  5. if (this.isDragging == false) {
  6. float newYPos = (this.isOpen) ? this.maxPosY : this.minPosY;
  7. float speed = Time.deltaTime * 10f;
  8. this.targetPosY = Mathf.Lerp(currentPos.y, newYPos, speed);
  9. }
  10. float yPos = Mathf.Clamp(this.targetPosY, this.maxPosY, this.minPosY);
  11. this.rectTransform.localPosition = new Vector2(currentPos.x, yPos);
  12. }
  13. /*…остальной код…*/
  14. }
To do this, we will write a new toggle switch position to the newYPos variable, depending on whether we perform an opening or closing action. If we open it, then we need to move the toggle switch to the lowest limit of maxPosY, if we close it, then we need to move it to the upper limit of minPosY. After that, using a simple Lerp and delta, we smoothly move the toggle switch to the set border.
Now the panel can be opened and closed simply by calling the Open or Close method, respectively, without having to drag the toggle switch manually.
In the example, it is the "back" button that calls the Close method of the CustomSlider script, after which the panel collapses on its own.
That's how, with a simple script and simple settings of interface elements, you can get a nice and intuitive menu.

3
A small selection on creating a menu in unity.
© Alexander Vinogradov's course project
VS-1 Group
Rybinsk Aviation College
Social network
This site was made on Tilda — a website builder that helps to create a website without any code
Create a website