The Challenge of My First Tool
In one of my previous posts I mentioned my initial plan for making a level creation tool for designers to use. The tool will significantly cut down on the time it takes for designers to build, create, test, and iterate on levels. The high level functionality of the tool is to be able to create a tile map in the Unity Scene so that designers have something physical to work with and look at. Alongside this, the goal is to move away from loading as much as possible on the start of the scene because the game is on mobile-- which means limited computing resources.
The initial design use for the tool was as follows:
Input how many layers (height) you wanted the level to be.
Select the layer that you would want to work on from a drop down
Set/input the dimensions of the layer
Color a grid of tiles to create the grid on that layer. Colors are each associated with a different tile type and can be color picked in the tool.
Hitting a button would create and store the back end of the level (the tilemap, and their connections) and create the front end (GameObjects) so further work could be done on the level.
It didn’t end up like I wanted it to because of time constraints and what Unity default gives to work with in Editor scripts. The initial idea of “painting” the grid to determine where the tiles are didn’t work because there were no Unity GUI functions that did something like this by default. It would’ve required me to make a custom editor GUI to do this functionality for me, however this wasn’t feasible as this needed to be done ASAP. That’s where the GUI SelectionGrid came in handy; I can just click the button and have it changed based on if I want to change it or not.
This works for what the designers currently need and when I showed it to them they said it would work and cut creation time down heavily.
Alongside this tool I had to re-haul the tiling system that was already in place;It needed to support a node grid so that pathfinding could be efficient. However, it was difficult to read through and add to if need be. The tiling system ended up being a basic tile map that supported multiple grids of tiles (for height) and the ability to get the sky view of the map. This was the data that the tool was trying to store.
Here is where we started running into problems:
1) I was never actually storing the data into anything, so I couldn’t use the data after creating the map it just got released back to memory.
ScriptableObjects! These lovely things are native to Unity and are meant for storing data between Editor and Runtime. You can realistically store anything in these as they are pretty much just writing to a text file that Unity reads later. Persistent, reliable data. Woohoo!
2) It’s not that simple! The data wasn’t actually storing in the ScriptableObjects. You can’t just generically store classes (Tile, TileGrid, TileMap) in a scriptable object. They need to be serialized so that Unity can actually store them.
Just serialize them! In order to serialize classes I need to make sure all my private variables and enums have [SerializeField], public variables are already serialized. I have to make sure that there are no generics (List<T>) in my classes, and that there are no 2D lists or arrays (List<List<>>). There also can’t be static, const, or readonly variables. I have to make sure that there are no recursive nested classes (Connections had Tiles which had Connections etc.) Once all of this is done I just need to stick [System.Serializable] at the top of my class and the class is serializable.
3) It’s still not that simple! The data was storing in the scriptable objects but if you closed and reopened Unity it wouldn’t have the class data anymore. The nested class’s reference was lost in the high level class. If you have nested class the higher class needs to be a ScriptableObject itself.
Change what I’m storing. Changing all of my classes and functionality to acting like ScriptableObjects instead of like classes will take too much time and at this point I didn’t have it. If this doesn’t work out either then there is a chance that I take even more time debugging something I don’t have a lot of experience with. So, instead of storing the tilemap itself I’m just storing the data used to create it instead.
Further Improvements / Solutions
Some things I wanted to add to the tool to make it easier to use and overall better are:
There are currently some minor bugs that can increase the time it takes to create levels using the tool. Fixing those is a good idea.
The way the tiles are loaded is currently hard coded and you can’t hot swap different tiles. That’s as simple as making a structure that can be edited to added prefabs and associated names.
The scriptable object is currently storing strings instead of enums. This can take up an increasingly large amount of space based on the map size. This is easy as setting up enums and storing them instead of the strings.
There’s a big one with usability. Currently you have to click each button individually to change the tile. This adds a crap ton of time. To fix this I would add some support for key bindings or mouse controls that make it so you can change a rectangle of buttons at a time. Another addition is a hotkey or button in the tool that changes all of the tiles to the current selected one.
The last one being loading. There’s currently no support for loading in the data of an old level. This is probably the biggest time saving one that I will do because having to redo the levels when you want to iterate on them is extremely time consuming.
Those are the changes I’ve thought of if I have the time to do them. I also would like to ask the designers on feedback about what they want added to the tool and what they want changed or fixed. In the long run I’m making something that they have to use so what they want changed is more important than what I think should be added or fixed.
All in all I have learned a ton about Unity doing this tool. Between the functionality that is offered natively by editor scripting to how unity scriptable objects store things (or what they can’t). This gives me a better grasp of what I can do and give and how long things will take in the future for more tools.