In addition to the automation of the tilemaps
we've done last episode, let's now automate the UI panels. Currently they are manually created but whenever
we would add new buildables we also need to update the UI, which is quite annoying. But good thing is, it's not that hard to code
and UI coding stuff is always good to know. So let's start. According to our current UI scheme, we want
to have UI Categories and they contain the button tiles to draw. Theoretically we could reuse the category
scriptable object from the previous episode, but UI handling for the user and tilemap code
handling can be completely different. So it's more practical to separate this and
create new "UI Categories" in form of a scriptable object. Change MonoBehaviour to ScriptableObject and
also add the line to add this item to our context menu. Now I'm going to add two variables with their
getters. The first one is the siblingIndex and is just
an integer. This number represents the order of our categories
from top to bottom. The second is of type Color and will change
the background color of the category labels. Currently they are all white and this feature
is basically just for show and acts as an example how you can change various things. We now have two options. Every UI Category could have a list of "Buildables"
which then will be shown. Or we reverse this by assigning the UI Category
to the object itself, like we've done it with the other category. I'll use the second approach because I think
it's better to store information that belongs to an object there. It also makes sure that an object is only
shown once and can't be assigned to several categories. So let's add the according field and getter
to the BuildingObjectBase. If you named your new scriptable object differently
just make sure to also change the Type. Now let's create some UI categories and then
assign our buildables to them. The
Sibling Index starts by 0 and represents the entry on top of an hierarchy and therefore
at the top of our list. Then just count upwards. When choosing a color make sure to increase
the alpha. It always starts with a default of 0 and that
makes It invisible. Then just fill in the new field we created
for all the buildables. I decided to not create an "Item" UI Category,
so that we can see some differences. Like we initialize the tilemaps on game start
via a script we now need to do this for our UI. I create a new script called BuildingHUD,
because it only represents the UI elements that are responsible for building. Make it a Singleton and also import the UnityEnginge.UI
package. Now taking a look at our current structure
we have the following: The wrapper which contains the different UI Categories, here called "Categories". In there the category element which itself
contains the category label, here badly named "Background" which represents the background
and the title of the category and also the list of buildables which are the buttons we
created in episode 1. To spawn objects with this structure we are
going to work with prefabs. To create those I will change and rename one
of the existing categories. This will act as an universal start point
for all the elements we will spawn later. Make sure to remove the content of the "Items"
because they will be spawn into this separately for every entry. The Text of the title will be changed as well,
so there is just the placeholder "ABC" for now. When done create a new folder called "Prefabs"
and drag and drop the object in there. Now this is our category prefab. To spawn the button we need another one as
well. I'll adjust an example entry like earlier
and remove the specific values like the image and the Building reference, so that it's just
a "blank" building button. Now drag and drop it into your prefabs folder
as well. I move both entries to an "UI" folder, so
that it gets obvious where the prefabs belong to. Now that we have what we need, let's create
the according serialized fields for this, so we can set up the UI creation. Of course we want a list of UICategory to
select the categories we want to display. This is done manually but could also be done
via code, so that you can show different field groups for different needs. Then we need the parent element where all
our categories go into. If you want to set a parent for a game object
always use type "Transform" for the parent. This will automatically get the "Transform"
component of the game object you will drag into the slot. And of course our two prefabs. So the upcoming part might be a bit difficult,
so let's do it step by step. I create a method which will build the UI
and will be called on Start. I create a "foreach" loop to iterate through
every category inside the categories list. So let's instantiate a new object based on
the universal category prefab. We then set the wrapperElement as parent. So basically that spawned our category object. Let's change the hierarchy name to the current
category name, so that we can check if it works correctly. Back in Unity add the script to your manager
object. Add the wrapper element and the two prefabs. Now add all the UI Categories you want to
show. I also hide the prefab and remove the manually
created entries, so that they don't disturb the new creation. When starting play mode the three entries
get created. Taking a look in the hierarchy, you can see
they were named like the category and contain the structure like we want. So the basic setup works. Since we want to add the buttons to those
instantiated elements here, it makes sense to save the instantiated objects referenced
to the category they belong to. This can be done with a Dictionary. As key we use the UICategory and the value
will be the game object we created. So after initialization you can add the key-value
pair to the dictionary like this. In some use cases for changes during runtime
there might already exist this element, so let's check if the current key - the category
- exists in the dictionary. If it does not exist, we create the entry
and add it. Else we assume it was created earlier, but
based on how the code currently works, this will never be the case. Just be aware of this handy approach if you
add live update later on. Now, whether the if-statement was executed
or not, we have a value for the active category. We then can replace the call of the "inst"
variable with the GameObject which is saved in the dictionary, because basically they
are the same. Now we can change the other attributes, like
the text which represents the name of the category. For this we will find the first and only Text
component for the game object. Make sure to use "GetComponent" not "GetComponentS"
"…InChildren". Changing the index is quite simple and will
make sure that the one with an index of zero will be on top of the hierarchy. Changing the background color is similar to
the text but we will get another component, of course. If you have several text components for example,
you might need to get the correct ones based on their direct parent or their position. An example for the latter one can be found
in my tutorial about the UI for the Highscore List. On testing all three elements have now a background
color and the according title. But of course the items are still missing,
so let's fix this. Since we reverted the reference between category
and item, we don't have a simple list of objects we want to show now, unfortunately. One approach now could be to have a serialized
field were we add all the scripted objects we created and then we can loop through tem. But who would want to do this? It would be much cooler to just use all the
scripted objects that are located somewhere inside the according folder, right? Right! The good news: Unity can do this. The bad news: This only works in a folder
called "Resources". Let's just take it as it is and create the
folder. We then can move our complete "Scriptables"
folder in there. I think all bad news should be so easy to
fix. So back in the BuildingHUD script, let's create
a method which will return all Buildables. The return type will be an array of the BuildingObjectBase
or whatever type your buildables are. Then just return Resources.LoadAll and add
your type as type reference. As parameter you then can specify the path
where they are located. Be aware that the "Resources" folder is used
by default, so this must not be added to the path. We can then call this method right after the
loop where we created the categories. Then we can iterate through all buildables. When creating a button we need to assign its
parent correctly, which is the "Items" object inside the category prefab, which is empty
for now. Every parent might be used several times,
so it's better to only search it once and then save it into a variable. I'll do this by saving it into a Dictionary
with the according Game Object as key. The value is of type "Transform" because this
is the type we need for setting a parent. Right after I instantiated an object and fill
the first dictionary, I also set the key and value for the new one, the elementItemSlot. With the "Find" method I can find objects
by name. So, we can now find the according value with
the key, which is the game object. To get the game object, we need to look up
in our first dictionary with the "UICategory" key. You can nest it like this. This will return the "Items" object for the
current UICategory. But spawning an item which is not assigned
to any category would be bad. So let's make sure an UI Category was selected. If this is not the case we will skip the rest
of this code and continue with the next element in our loop. Now we can instantiate a new object like we
did earlier with the categoryPrefab, but now we use the one for the item. Then we can set the parent to the parent element
we retrieved two lines above. Then you can change titles, names and sprites. There is only one problem with the image. We want to get the sprites that are assigned
to the Tiles we created in episode 1. But we don't have an object of type "Tile"
but rather "TileBase" and the latter one does not expose a "Sprite" property. But a "Tile" inherits from "TileBase", so
that's the reason why we can use the tiles here as tileBases. To get access to the sprite property from
the "Tile", we just create a temporary variable of type Tile. For this import the UnityEnginge.Tilemaps
package. With an explicit conversion we can convert
the "TileBase" variable, that comes from the current buildable, to a "Tile". Now we can access the sprite property and
assign it. And we also need to assign the buildable itself
to the BuildingButtonHandler script. Make sure to add a setter for "item" in this
script. Now you can assign the item via code. So now for every existing buildable inside
the given path, we will check if it belongs to an UICategory. If it does, the object will be spawned and
it gets added to the according category. That's it. When starting play mode everything should
look like earlier, but with a little more color. You can see the items exist, they have the
correct sprite and they also reference the correct BuildingObject. So currently the UI only gets created once
during start and not updated. But we laid a good foundation for an updatable
UI. If you need this I recommend to take a look
into my Highscore List video, which contains some useful code parts. In the next episode we'll take a look into
some improvements and useful features for the current code. Thank you for watching and also thank you
to everyone supporting me and the channel. Special thanks to my supporters on Patreon:
Pat Rick & nuxvomo. Thank you all. And as usual: If you enjoyed the content consider
liking and subscribing and I'll see you next time.