InGame Tilemap Editing - Part 5: Delete Tiles and whatever tools you want - Unity Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I know, I know… you all have been waiting for this. Who am I to show you, how you can add all those tiles and all those features without this essential part? Yes, you know what I am talking about, well, I guess you read it in the title, but yeaaah we are going to delete tiles, finally. Deleting itself is quite easy, but of course we want to integrate it in a nice way. Therefore I will show you a way, how you can add different tools in a similar way to adding new tiles. But using a tool will not only add a tile on click but rather can execute any custom code you defined. Let's stop talking and jump right to where left in episode 4. For our tools we will create a new ScriptableObject. I create a script called "BuildingTool". But instead of letting it inherit from ScriptableObject, we will use our BuildingObjectBase as parent. This will automatically include all the fields from it to this class and of course, it will be a ScriptableObject. We also add a new menu entry for this class. Let's give it a look by creating one of those tools. But to make sure it fits to our design, I need to add a Category as well as an UICategory for all the upcoming tools. Note that we will not need the Category for the upcoming code, but of course it should be separated from the other categories. To show the new UI Category in the UI we need to add it to the according script in the manager object. Let's take a look… ah yeah, my UI design is perfect and so flexible, it does definitely not break when adding new categories… Yes. In the according folder I can now create a BuildingTool and of course, it will be an eraser. Select the new categories and you can also select a PlaceType. Now even that we don't want to add a new Tile we need to add a TileBase. On one hand the according image will be used for the UI and - you might missed it - we will add a tile. Every time we show the preview of the selected tile. So for this I got a simple icon online. Like we did in episode 1 I use the image to create a Tile from it and save it in my Tiles folder. Now I can select the Tile Base I want. If you added everything correctly you should now see your new eraser button, you can see the preview and you can even draw with it - wait, what? Does this count? I guess not… Like I said we are going to use custom code to handle the logic behind all our tools. Now your first thought might be to add this code into the BuildingTool class, but ScriptableObjects are not meant for this. They don't have an awake or a start method and basically are just not MonoBehaviour classes… so why not using one of those, right? Let's add a new script "ToolController". For now let's just create a public method called "Eraser" and log a message. We will also make this class a singleton. Now we just need to call this method when selecting and clicking with the BuildingTool, you know, instead of drawing with it. But for this, we need to know that the eraser object we created is meant to call exactly this method. Let's start by adding an enum to the BuildingTool. Later on this should be the list where all of your tools are listed. We then add an according field to the class, where we can select one of those tool types. This will give us the opportunity to connect the created object with the custom function we want. I add a new public method I call "use". Whenever this method will be called the custom code gets executed. At first let's get the one and only object of the ToolController. Remember that we can only write it like this, because we made it a Singleton. And since we don't have an awake function available here, we will just do it in this method. With a simple switch statement, we then compare the selection of "toolType" with the available ToolTypes from the enum. Now we can just call the eraser function from the tool controller. If no "toolType" has been set, I will log an error. Back in Unity we can now set the ToolType for our eraser. But at the moment we don't use the "Use" method, we still just draw a tile. So let's take a look in our BuildingCreator script. At first we need to make sure, that there is a single point of truth for drawing. Currently we have different places in the script, where we call "SetTile". Therefore let's expand the "DrawItem" method with new parameters for the map, the position and the TileBase. Now we can call this method instead of the SetTile method. Just replace your calls with the according parameters. I will leave the preview map updates as they are, because the preview does not affect the placing of the tools, so it can stay. Also update the initial call of the DrawItem method as this method needs three parameters now which were previously defined inside the method. Now that every real tile placement will be executed by this function, we can adjust to logic to execute the custom code for the tools. If you were curious why all the code works, even though the BuildingTool is not a BuildingObjectBase: Since we inherit from BuildingObjectBase, a BuildingTool can always act as one, because it contains all the fields like its parent. Vice versa this won't work, because the BuildingObjectBase misses the toolType attribute for example. So our BuildingTool currently acts as BuildingObjectBase and is saved in the "selectedObj" variable. But we can check the type of the "selectedObj" variable to find out if it might be a BuildingTool. And based on the result we can make different things. In code this will look like this. But our BuildingTool still looks like a BuildingObjectBase and won't offer us the "use" method we created earlier. For this we need to explicitly cast it to type BuildingTool and now we can just call the use method. But if it is not a tool we will just call the default "SetTile" method like previously. Now let's test it and see if we will see the log inside the eraser-method of the ToolController. Great, as you can see the log appears and also nothing gets drawn. I guess we could… starting erasing now? Finally? Yes. Let's fill the method with some beautiful logic. Or, well… not quite yet. To delete something on the tilemaps, we need the tilemaps first, don't we? Import the tilemaps package. Now I can add a variable for the list of tilemaps we will retrieve from the hierarchy during the start method. Therefore I use the FindObjectsOfType method. Be careful with the "s" to retrieve an array. But I want a list, because lists are much more nice, so I add "ToList()". This method comes from the System.Linq package, which is always a little bit annoying to remember. But good for you is me mentioning it :) Instead of just saving it to the tilemaps variable, I save it to a temporary variable. So I can loop through it and make some checks to filter it. In my simple case I just don't want to add the PreviewMap due to obvious reasons. I simply compare it by name. If the map is not the PreviewMap I add it to the tilemaps list. Now remember from episode 4? The tilemaps get created during another start method, so we need to make sure that this one here runs after the mentioned one. In Unity go to Edit > Project Settings > Script Execution Order. Now add the ToolController script somewhere after this "Default Time" slot and hit "apply". Now when using the eraser we should have all tilemaps we want available. I am sure you now really want to delete something, so let's start with erasing the tile on the clicked position for ALL maps. Crazy, isn't it? To start with this we need the position inside our Eraser function. So let's add this as parameter. Now let's go back to where this method is called - inside our BuildingTool. Here we need to repeat this step. Add the position as parameter and also pass it to our Eraser function. And one step further back we are in our BuildingCreator. When calling the "use" method we have the position already available so we can just pass it. Now let's loop through all the maps and just replace the tile at our position with null. This one line here basically is the deletion part. Had you guessed it? Well let's give it a try. I add some floors and walls and then… everything gets removed. I just realized that you can click and hold with the "Single" Place Type. That was not the behavior I initially planned, does this count as bug or feature now? Ok great, we can delete things. But I see it might be not practical to remove everything. So let's change the code to only erase the tile on top. One approach could be to use Raycasting but I don't have colliders on my tiles, so I will show you a different way. What tilemap is above one another is determined by this sorting order. We define the value when creating the categories from where we create the tilemaps automatically. By using this value we can determine which map is first and which one last. Then we just loop through them until we find the first one with a tile at the given position. Sounds easy? Yes, because it is. All we need to do is to sort the maps after retrieving them based on the mentioned value. Therefore we will use C#s Sort function. Since the sorting order value is inside the TilemapRenderer component, not the Tilemap component we need to get this first. Then we can just compare the two values against each other and return the result. The sorting process is then done for you. I use b as the first value, so that higher values are on top and low ones at the end. By swapping those two, you would invert your order. Now we can loop through them. I am going to use the Any method here. That means it will stop looping when it returns a true. So if the map has a tile at the given position I remove it and return true. Else I will return false and the Any-method will run this code for the next tilemap. Yep that's everything. Let's test it. As you can see, if I remove a wall the floor below becomes visible again. And testing the feature-bug of holding the single event I consider it as bug. Let's fix it. Inside the BuildCreator we can find the problem. When holding we call the HandleDrawing method everytime during a update. This is fine for keeping the line and rectangle preview up to date, but we also find the handling for the single place type here. Let's just move it inside the release handling. Now a single item only gets placed when releasing after clicking, which is totally fine in my opinion and the bug is gone. Now the more-feature-than-bug click and hold eraser won't work anymore. So let's change the place type to "Rectangle". Now this won't work like you would expect. It just deletes, but we want the preview to be drawn and delete on releasing the click, right? If you take a look in the BuildingCreator you can see the RectangleRenderer calls the DrawBounds method with the preview map. And what happens here? Yes, our DrawItem gets called and it will be handled as … a tool. By comparing the map parameter with our variable of the previewMap we make sure that the if statement only matches if we are not drawing the preview currently. Testing it again, it will work as before but for a rectangle. Note that this is not limited by the kind of tilemaps, so it might remove floor and wall at the same time. But if you have managed it to this point, expanding the Erase method is no magic :) I really tried to find some nice ideas for other "Tools" and one might guess my almost 1000 hours in Rimworld could have helped, but I leave it at that with the eraser. If you have some suggestions for the tools you want to see coded, let me know in the comments or leave a message in my Discord channel. Maybe there will be a "All The Tools - The Absolute Special Episode" video one day - who knows? ;) And for all of you still being here, I will offer you the priceless view of this octopus I painted. During sketching I was so focused on drawing eight arms - you know "octo" - "eight" - that I only painted six. And I just realized it after coloring. So, this is my hexpus, hexopus Yeah, hexopus… great. 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.
Info
Channel: Velvary
Views: 29
Rating: undefined out of 5
Keywords:
Id: 1Sah23KPEfU
Channel Id: undefined
Length: 15min 50sec (950 seconds)
Published: Sun Dec 19 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.