InGame Tilemap Editing - PART 2: Lines & Rectangles - 2D Level Editor with Unity - Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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.
Info
Channel: Velvary
Views: 353
Rating: undefined out of 5
Keywords:
Id: h50OMdqtbKE
Channel Id: undefined
Length: 11min 53sec (713 seconds)
Published: Wed Jul 28 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.