During the first part of this series we build
up to the point where we can select different items and draw them onto a tilemap. Pretty much like - yeah using a Tilemap - but
INGAME, during runtime. Now as a user I don't want to print like every
single floor tile on its own but rather use something like a rectangle tool. As a child I played a game called "SimTown"
and you had to place every single grass tile on its own, I am still traumatized, so let's
make this better. So let's start right where the last episode
ended and begin to implement two new draw styles: lines & rectangles. For now, every item should have one place
type which is assigned on creating those scripted objects. I add a new enum like with the categories
and add the three planed types, where single is the only one currently implemented, that's
why it's placed on top and therefore default selected. Add a serialized field for assigning this
value via the UI and a getter to retrieve the value later on. Now I can go to my objects and update the
place type value. The first value in an enum gets selected automatically
so the value for the door is correct. I set "Rectangle" for the floors and "Line"
for the walls. So since placing a line or a rectangle can't
be done with one single click, we need to extend our player inputs to support hold events
like clicking and then holding the button. I cover it briefly but it's more discussed
in my series about Unitys New Input System, so check this out if you have questions. Updating the Input Action file itself is as
easy as adding two interactions to our left mouse click. A "Tap" and a "Slow Tap" interaction. Save it and the generated script gets updated
automatically. So now we just need to change the way we react
on those event… just, haha, yeah. To detect and differentiate between a simple
click and a hold event we now need to listen to the started and the canceled event of the
left click as well. They will all points to the same method, where
the differentiation is handled. Let me quickly show you how this is done. I add a log statement of the currently happened
interaction (like tap or slow tap) and the phase (which is started, performed, canceled). On a simple click we see the TapInteraction
starts and performs. But if I don't release immediatly the TapInteractions
gets canceled and never shows a performed, instead the SlowTapInteraction starts. On release now it shows a successfully performed
SlowTap. Let's add new variables first. We need a boolean to know if currently an
active hold event is happening. And since we want to draw rectangles or lines
we need to know on which tile the holding started. If the input phase is a started event we will
check if it was a slow tap. This will set our boolean to true. But if it was a performed or canceled event
the holdActive variable will be set to false. If it was true before we then can draw whatever
is currently previewed, but this later. Whenever an input started we will update the
holdStartPosition variable with the currentGridPosition and then start the HandleDrawing method. Now you can see why we created the HandleDrawing
method which only called another method, because we are now going to extend it. An if statement makes sure that there really
is an object selected and if it is we can create a switch-case statement for its variable
"PlaceType" and create cases for our three types. I also add the "default" case to the "Single"
place type, which just makes sense. We already created the placement for the single
items, so we can move this function call up in the case statement. Now comes the complex rectangle drawing part
- well it is a bit more complex if you figure it out yourself, how to draw a rectangle onto
a tilemap - but good thing is I already did this and I will show you that it's quite simple. I create a new method which is only responsible
for rendering the preview of the rectangle. At the beginning we make sure the preview
is cleared and there are no preview tiles anywhere. I then add a new variable on top of the class
which is of type BoundsInt. This type basically contains four Vector3Int
which define the four corners of a box or …you guess it, a rectangle! Now during the hold and drawing process I
just need to assign the four corners based on my starting point and my current point. In consideration of the four possible ways
we could draw such a rectangle and therefore respecting the positive and negative values
we come up with those four assignments. The starting position can be the top left
corner of the box, but it can also be the bottom right one. So this is basically what happens here. You can do the maths with some simple numbers
to understand what exactly is going on here. Now I create another method which is responsible
for drawing the bound variable to the given tilemap. So we can use this method now for drawing
onto the preview map and later onto the real one. What happens here is a loop along the x axis
and in there along the y axis, resulting in this function here getting called for every
existing combination and therefor every space inside our given bounds. Now just call the RectangleRenderer from the
HandleDrawing method. And inside the Update Function: As long as
the hold is active and the mouse moved to another grid cell we want to recalculate the
rectangle. We can now test the functionality. Of course I need to select a floor item and
see there, it works nicely. On release the preview stays and can be overwritten
by just hovering over it - this is fine and will be prevented in the next step - the drawing
step. This will work quite similar. I add a new method for when the hold event
gets released. I copy and paste the code from the HandleDrawing
method and can remove the code for the single type, because it's not available here. Now I can basically skip the RectangleRenderer
part, because we just have the most updated rectangle already rendered, and call the DrawBounds
method - now with the real tilemap we want to use. On release we also need to clear the preview
map. So that it does not stay above the drawn objects. Now just make sure this function gets called
on release and that's it. On testing you can see that the visible tiles
must be on the real tilemap or else they would get nulled on hovering like earlier. Drawing a line is now just kind of like arectangle
without one dimension, because its width or height is always one. That "with OR height" thing is the only tricky
part. Based on the side of the invisible rectangle
which is longer, the direction of the line will change, either horizontal or vertical. But like earlier: Math is already done, just
watch and repeat. I create a Line Renderer which will fill the
bounds variable like with the rectangle. I then calculate the difference between the
starting and the current position for the x as well as the y direction. If the x side is larger than y, the line will
be horizontal, else it's vertical. For each case we need to set the bound lines
a little bit different, but both have in common that it only spans in one direction. The other axis is set to the starting position
for min and max and is therefore just one unit big. The bounds variable is set correctly and we
can call the drawing process for the preview map again. Now just add the LineRenderer to your HandleDrawing
method and also update the release process. We could just copy and paste the code from
the rectangle handling, but better, we can combine those two cases so for both the same
code gets executed. You can now draw lines and rectangles, isn't
that amazing? Now you can calculate what's needed for some
cool circles ;) Well based on how good you tested, you may
have noticed that you can't place lines or rectangles with a single click anymore. Just add the following else-if statement to
your left click handling. This will make sure the DrawRelease function
also is called when a successful tap interaction was performed, and only then. And one thing I noticed while recording but
not while planning this tutorial is that you can't be fast at the beginning or else the
preview will mess up. That's because the Slow Tap Interaction just
starts after half a second per default and the update method only works if the variable
for holding is true. So let's change is handling here a bit to
make this also work under more stressed circumstances. I set the holdActive event to true as soon
as an event starts, so at the moment of the click. But the start position only gets set once
when the Tap event started, or else it would get overwritten when the hold event starts
half a second later. The holdActive variable isn't really useful
here anymore, so I remove this case and rewrite this if statement so that it is true when
a SlowTap ended - I don't care if performed or canceled, because we don't use something
like a "Loading up" event here - or as before, if there was a successful simple tap event. Now we can stress test this and I think it
looks pretty good - not at last due to my beautiful art style. I have the feeling this was enough for this
episode. In the next one we will automate the process
of drawing onto different tilemaps which is in my opinion an essential part, so stay tuned. Thank you for watching and also thank you
to everyone supporting me and the channel even if it's small as a like. 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.