With our level builder we can draw tiles during
runtime. Points, lines and rectangles. But there are a lot of use cases where you want
to stack items - specifically having a floor and then something on top of it. This
is not working that well at the moment. Ok, it doesn't work at all. But it will soon…
I am going to create a separation of categories for this, but you might use whatever you want
and what fits for your needs. Then just do what I do with another variable if "category"
will have a different use case for you.
In simple words: For every category
I will automatically create a Tilemap and every item belonging to that category
will be drawn onto this tilemap.
To handle the categories along if their assigned
Tilemaps I will create a Scriptable Object for it. This is basically like we did
it for the BuildingObjects.
Also open this BuildingObjectBase file. Here
we will cut and paste the PlaceType enum to the new file. This is more of a structural
decision and will not directly affect the code because the enum is public and therefore available
everywhere. Just make sure you don't have two enums with the same name. I add a new entry
"None" right at the top. More about this later.
Now we want to derive from ScriptableObject
and we will also create an entry for the create-context menu. This is mainly
what we already did before.
I add three variables to this scriptable object.
The first one is the place type exactly like in the BuildingObjectBase. The "sortingOrder"
will be relevant later and decide which tilemap will be above or below. Both values will be
set via the Unity UI. The connected tilemap is not a serialized field because we will set
it by code. Don't worry, you will understand the fields when we are going to use them. And
we also have the getters for those variables.
Now go to your BuildingObjectBase and replace the
"Category" which was an enum previously with the newly created Class "BuildingCategory".
This enum can be removed now.
Now all the BuildingObjects derive the PlaceType
from their assigned BuildingCategory. This is useful but maybe some items should have a
different one. This is why we added the "None" value to the PlaceType enum. Every BuildingObject
itself has a PlaceType value as well. If this is "None", then we use the one from the
category else we will use the specific one.
So let's adjust the PlaceType
getter with a ternary operator. If the buildables specific
placeType variable is "None", then return the one from the category
else return the placeType variable.
So that's it. We can now create the categories.
I add a new folder besides the "Buildables" one and can now create the scripted objects.
For now just let the "Sorting Order" with its default value of zero and just
assign the correct PlaceTypes.
You also need to update your Building Objects.
Assign our freshly created BuildingCategories and also reset the "PlaceType" of the buildables
to "None" or something other if you want to override the place type of the category.
So now we have the needed information of HOW to place the buildables with their categories,
but we still need the WHERE. We already created a variable where we can save a tilemap, we just
don't have any yet. Since our ScriptedObjects are not inside of a scene we can't just create
tilemaps by hand and assign their values. So let's have some fun by automatically
spawning all the tilemaps we need.
For this I'll create a new script called
"TilemapInitializer". This script will spawn the tilemaps and also assign them
to the category they should belong to.
Add the tilemaps package and change the code
so that this class derives from Singleton, because we want to make sure that an
object of this class only exist once.
This script will be placed inside our scene
so I can create a serialized field for all the BuildingCategories I want to initialize. Now
inside a CreateMaps method I want to handle the tilemap creation for every category inside this
list. Before continuing make sure to call this method inside the Start method of the class.
When manually creating a tilemap you basically just create an empty object and add
two components: the Tilemap itself and a Tilemap Renderer. And this is also how to do
it via script. We are going to create an object, then create and assign the specific features for
a tilemap. To make sure it will be placed inside the "Grid" we also need to assign a parent.
When creating a new GameObject the name is the first parameter inside the brackets. Now
this is just the filename of the category, but you could also prefix
"Tilemap_" for example.
With the "AddComponent" method of the object
we can create and assign the components and also save them inside a variable,
because we will need them later.
For assigning the parent we
need a variable as reference. Parents are always assigned as "Transform", so use
this as type. Then just assign it like this.
Now it's time for settings you may want to apply.
In our case there also is one: the Sorting Order. This comes from the Tilemap Renderer, so we
use the variable for this and just set the value from the one we added to the category.
Great, now theoretically we created a bunch of tilemaps. We just need to assign them to the
category. Add the according setter to your BuildingCategory script for the tilemap. This
will simply replace the value of the private Tilemap variable with the given one.
Now we can do this with this line. Note that we use the Tilemap component of
the object, not the object itself.
Let's see if this works. Back in Unity assign
the Initializer script to your manager object. Assign the "Grid" of the tilemaps as parent
and inside the list add the categories you want to create a tilemap for. I'll just
use two to show you that it works.
When you now start the game two new Tilemaps get
added and they are named to our schema. Now to see if the drawing process will also work, we need to
adjust the BuildingCreator script a little bit.
Let's create a getter where we can
dynamically retrieve the tilemap. If there is an object selected and the
objects assigned category has a tilemap, we will return it, else we will use the
default map. In our case the "Items" category does not have a Tilemap and therefore
should be drawn to the default map.
Since we used the default map until now,
I can just search for the code parts I used this variable and replace it with our new
tilemap variable. Well it should work now.
I switch to Play Mode. When I now draw
a floor and then hide the Floor Tilemap it disappears. So we know it was drawn into
the correct map. But all maps have currently the same "Order in Layer" value of zero. We can
stack items but they are not in the right order.
Good thing, we already prepared everything for
this. Just go back to where you defined your BuildingCategories and change the number.
The floor should always be on the bottom, so I give it a negative value. This
value then gets assigned to the TilemapRenderer here in this code part.
You can verify it here, after switching to play mode, the number changed. When drawing
I can now draw on top. So currently the Wall Tilemap is above the default one, which leads to
this behaviour: The door gets not drawn on top, BUT it still gets drawn. In this case we can just
remove the creation of a separate wall map from the list and then, both would get drawn onto the
default map. But this is not always practical.
Well, an option to check if we can draw with
this object at this position is not that easy. You might draw a door on top of a floor but not
on top of a wall? So I hope you see what I mean, because we won't discuss this topic in this
video and also focus on basic features for the next episodes. So stay tuned for an
upcoming solution to this problem.
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.