Unity UI Toolkit Beginner's Guide 4: Customizing Slider 1

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hi, this is H.J.. In this video, I'll make a custom slider in Unity UI toolkit. We will create this slider by modifying the default slider. It would be possible to create a slider from scratch. However, modifying the default one will save you time and effort. Furthermore, it helps you understand the principles and underlying structure of the UI toolkit. Let's get started by basic settings for UI Toolkit. FYI, I use Unity version of 2022.3.7f1. First, create a UI Document and a Panel Settings Asset. In the Panel Setting Asset, set the scale mode to Constant Pixel Size. To draw the UI on the scene, create an GameObject and attach a UI Document component. Connect the UI Document and Panel Settings Asset to the component. Double-click the UI Document to open the UI Builder. In the Library pane, you can find the slider controller. Drag it to the hierarchy. Some attributes are exposed in the Inspector. For example, you can change its label or value. However, when you try to change their styles, you may be embarrassed. Because all the style properties are in a dimmed state. In order to change them, you should override its default styles. Create a USS file and make a selector for the label. First, write Slider without period or sharp. It is the type selector. Then write ‘Label’ also without period or sharp. We got the label which is the child of the slider. Adjust some properties. Now we can freely change the styles of the default control. But there is a problem. Drag the default slider to the hierarchy again. The default control itself has been changed. It’s because we changed the default style through the type selector. I want the default control to be unchanged for later uses. I need to narrow down the selector’s scope. Change the slider’s name to My Slider. Then make a new selector. This time I’ll use the name selector which is prepended with sharp. The label is not needed in the video. Set Display to None. Drag the default slider to the hierarchy again. The default control has been safely preserved. It seems I’m ready to create my custom slider. I prepared a texture which is 200 by 80 pixels in size. My slider’s height is going to be 80 pixels, but its width depends on its parent’s width. Select the topmost element, that is My Slider. Set the width 100% and the height 80 pixels. In the background, change the type to sprite. Otherwise, the image will be left stretched like now. Open the Sprite Editor and give it proper border values. Everything seems to be going well. But look closely. The slider is slightly off place. The reason is the default controls have their own styles. The Matching Selectors section shows which selector styles the element. The curly brackets in front of field labels indicates its value is overridden by a selector. If necessary like now, we should find the related properties and reset them. Set all margins to 0. The slider is positioned correctly. Grab the drag container with a new selector. Change the background color to make sure if the selector works. The container will be styled later. Next, grab the tracker. It is the thin line on which the dragger moves. I want it to fill the container. Add a new selector to get the tracker and the slider by their names. Test the selector with some background color and the make it fill the container. Although I set its Grow to 1, it doesn’t change at all. The reason is well explained in the Matching Selector section. It uses absolute positioning and has 50% top margin. Set Position to Relative, and Top margin to 0. Now the Grow property is applied as expected. There still remain properties to reset. Set the border width and margins to 0. It’s the dragger’s turn. I’ll make it fill the height and thicker. Get the dragger with a new selector. Get rid of the grey outline first. Then set it Width to 20 pixels and Height to 100%. Put it back by setting Position Top to 0 and margins to 0. It’s time to shape the tracker. Select the container selector. Give it some margins. 25 pixels for Top and Bottom. 40 pixels Left and Right. Then give the tracker a darker color. Let’s preview it. It works fine. I’ll add two things to the current slider. One thing is a filled bar. Sliders without a filled bar can be confusing for users. The other is a large and distinct dragger with a coherent style. A large dragger may look easy to make at first. If a new visual element were attached to the dragger as a child, it would follow the parent. So simple. But in reality, it can’t be done in the UI Builder. Because the default control’s hierarchy cannot be changed, I need scripting to make what I just said. Create a new C sharp script. Declare UI Elements namespace. Then prepare variables for the slider and some children. We’ll need the root visual element, the slider itself, the dragger and the filled bar. We need to get references to them. In order to use the Uquery, grab the root visual element. Find the slider and dragger by their names. Find the slider and children and store them in the variables. Let’s make a filled bar in a separate method. I’ll create a visual element for the filled bar and attach it to the dragger as a child. Let’s find out if the visual element is created. Attach the script to the GameObject with the UI Document component. Play the scene. Nothing seems to be changed. Unlike U G UI, you can’t see what’s going on in the scene hierarchy. There is a dedicated UI debugger in the UI Toolkit. In the debugger, find the dragger. It has a child without a name. Since we gave it neither a style nor a name, it’s invisible and has no name. I’ll make a temporary style for testing. Name the visual element ‘Bar’ and set 100 pixels for its width and height. Lastly make it red. A red rectangle is created and anchored to the dragger. We created a visual element and put it into a hierarchy at runtime. But C sharp scripting doesn’t seem suitable for detailed styling. The UI Toolkit has USS. First add a new selector to the existing USS. In the method, remove the temporary style. Instead apply the selector through Add To ClassList method. With the selector selected, style the filled bar. Give it an enough width. I’ll put 2000 pixels. The bar vertically fills the parent. Put 100%. And select a dark orange color for its background color. A long bar is anchored to the dragger, but on the other side. It’s an alignment issue. Currently, the left end of the bar is attached to the dragger. Let's switch it to the right end. In the Align Self, select the Flex-End. The bar covers the filled area. Now we just need to trim the unnecessary part of the bar. Select the container selector. Set the Overflow to hidden. It works like the mask component in UGUI. Children are invisible outside the parent’s shape. The filled bar was done easier than expected. Let's move on to creating the larger dragger. Declare a variable for the new dragger. The way a visual element is created and parented to another element will be same as before. Create a new visual element. Make it a child of the dragger and style. It. This time a style is added to the list first. I'll make the corresponding selector right away in the UI builder. Add a new selector for the new dragger. The selector’s name must match the name added in the script in the UI Builder. Set the position to absolute and both width and height to 80 pixels. The new drag will fill the bar vertically. Change its color and play the scene. A blue rectangle is also anchored to the drag, but it's not a 80 by 80 pixels square. The reason lies with the hierarchy. I made it the child of the drag. It means it's also one of the container’s children. The container’s overflow is set to hidden so its children are masked. I shouldn't make it a child of the container, but a sibling. Change its parent to the slider. The new dragger is no longer the dragger’s child and won't be anchored to its parent. It means it should be manually repositioned whenever the slider’s value changes. Fortunately, there is a change event type in the UI toolkit. We can take advantage of it to reposition the new dragger. Register a callback method to the slider with the change event type. The slider’s value will be sent together for later uses. Before positioning it, we need to understand the two coordinate systems, world and local, and their conversion. I’ll briefly explain it with a sample. This visual element is 600 by 200 pixels in size. It has three children. Each child is 200 by 200 in size and uses the relative positioning. I have another visual element, the black square. It’s a sibling of the parent and uses the absolute positioning. You can imagine that the parent is the container and the black square is the new dragger. I will position the black square at the yellow. First, let’s see the yellow’s layout position in the console. It reads (200,0). I’ll directly feed this coordinates to the black’s transform position. The black’s moved to the yellow. It’s so straightforward that no explanation seems necessary. Then move the parent and play the scene. The black is no longer moved to the yellow. It may need explanation. The layout position is relative to its parent. Although the parent’s moved, the yellow doesn’t move in the parent’s coordinate system. Because the yellow’s layout position hasn’t been changed. The black’s position was same as before. Our custom slider should be placed anywhere we like. Otherwise, its usage would be greatly limited. In order to make the new dragger stay where it should be, we need coordinates conversions. First the yellow’s layout position in the parent’s local coordinate system is converted into the world coordinates. Now the coordinates include the parent’s movement. Then they are converted into another local coordinates, the black’s parent. Then the black is positioned where the world coordinates points in its parent’s local coordinate system. This is how we position the new dragger which anchored to the original dragger. The dragger’s transform position is converted into world coordinates first. They are converted into the local coordinates of the new dragger’s parent. Then feed the resulting coordinates in the new draggrer’s transform position. Convert the dragger’s transform position in the world space into the local space of the new dragger’s parent. Then feed the coordinates into the new dragger’s transform position. It's perfectly anchored to the dragger. Now that we have the converted coordinates, our custom slider can be placed safely anywhere we want. Nevertheless, we still have got problems to solve. You may have not noticed, but if you click the new dragger, the slider doesn't respond in the slider. It's the tracker that responds to the click. If you see the hierarchy, the new dragger is at the bottom. It means clicks are interrupted by the dragger itself. Go to where the new dragger is created in the script. Add the following line. It will make the visual element unpickable during mouse events. Next, the blue square is currently off centered. This is because a visual element’s origin is at its top left. So the top-left corner is at the coordinates, when we positioned it. What we want is the new dragger’s center is exactly at the dragger’s center. Calculate the distance between two centers. Then subtract the distance from the final positioning coordinates. The blue square is well positioned at the dragger’s center. Apply a prepared texture to the blue square. It’s off centered again. This time the texture is the cause. If you look at the texture, there is transparent area. Although shadow is depicted there, it should not be included in the positioning calculation. Go to where the distance between two center is calculated, add some offset value to exclude some part of the texture from calculation. Our custom slider is well polished and looks almost complete. Why don’t we actually use it in another UI? Create a UI document and replace the current source asset with it. I’ll place the custom slider at center. As you may remember, our slider fills the empty space horizontally. I’ll make the space which will decide the slider’s width. Go to the Library pane and switch to Project. You can find the UI Document we’ve just worked on. It’s the slider. Drag it into the hierarchy like any other default controls. Finally, I will fix two things and finish this video. The first is completely my mistake. I forgot rounding the tracker’s corners. The container is currently a kind of mask by its Overflow property. So let the container round the corners. Set 15 pixels for the Radius. Second, when we click the slider, the new dragger is positioned correctly. What about before clicking? It’s at the top-left corner of the slider. It’s because I let it be. The position issue gets more obvious when the slider’s layout changes at runtime. In the UI Toolkit, there is an event called GeometryChangedEvent. This event is sent when the position or the dimension of an element changes. It will fix the dragger’s positioning issue. Go back to the script. Register another callback method to the slider. The event type is the GeometryChangedEvent. The content of the method is same. The slider works well in another UI documents and can respond to runtime layout changes. As I finished the slider, a thought suddenly came to my mind. If I made this slider in UGUI, it would take no more than 5 minutes. I sincerely hope the UI Toolkit worth the time and effort. That's all for this video. If you visit my channel, you can see other interesting Unity related works, especially UGUI animation and effects. Thank you for watching.
Info
Channel: Hj
Views: 8,020
Rating: undefined out of 5
Keywords:
Id: pUFG1u6dNQ4
Channel Id: undefined
Length: 19min 52sec (1192 seconds)
Published: Sun Sep 03 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.