Tile Editor for Unity: Part I

NEW IMAGE & NEW WEBSITE

Before starting to talk about today’s topic, I would like to announce we have a new image: new logo, appeareance and website. This new website includes released games, current projects and prototypes that will hopefully become released projects soon. Hope you like it!

TILE EDITOR

So, today’s post consists of the first of a series of short posts about how the tile editor for Unity was created and explain the basic functionality of it.

For those who only want to use it and are not really interested about how it was built, as soon as the Asset is available in the Unity Asset Store, you will be able to download it for free and use it.

This is only the beginning of this tool, I would like to keep improving it along with the development of the game I’m currently creating.

The whole explanation is divided in the following parts:

  1. Editor Window Creation
  2. Saving Status
  3. Menus
  4. Game Objects Actions: Create, Delete, Edit
  5. Layers
  6. Grid
  7. Snapping
  8. Transformations
  9. Snapping: Extended

For this post we will cover only the first two parts, the rest of the explanation will be covered in the upcoming weeks.

EDITOR WINDOW CREATION

The first thing we need to do is to create a folder called “Editor” in our Project manager, this is a special folder designed to extend the functionality of the basic Editor.

As you can see, I added the folder inside a folder called “FSLevelEditor”, you can really put this folder wherever you want and you can also create multiple folders in different locations, if they are named “Editor”, that should be enough for it to work well.

After creating this folder, inside of it, we want to create a new class which will be the main class for our extension. In this particular case, I called the main class “LevelCreatorEditor”, you can customize this name as you like.

  1. using UnityEditor;
  2. using UnityEngine;
  3. public class LevelCreatorEditor : EditorWindow
  4. {
  5.    // Add menu named "My Window" to the Window menu
  6.    [MenuItem("Window/Level Editor")]
  7.    static void Init()
  8.    {
  9.       //Get existing open window or if none, make a new one:
  10.       LevelCreatorEditor window;
  11.       window = (LevelCreatorEditor)EditorWindow.GetWindow(typeof(LevelCreatorEditor));
  12.       window.Show();
  13.    }
  14. }

This new class should inherit from “EditorWindow” which is the base class for new extensions in Unity. You can see more details about this class in the manual. The class EditorWindow belongs to the UnityEditor library so we need to import it as well.

In this class a new static method called “Init” has to be added to show the new window and assign a name for it in the menu. In line 6 in the code above you can see that I set the path of the new menu to “Window/Level Editor”, the location “Window” represents the Window menu in the Unity editor, you can choose whichever menu you prefer to have your new extension. You can also customize the name of the new item in the menu by changing the “Level Editor” for whatever you like.

  1. int gridSize;
  2. float gridX;
  3. float gridY;
  4. Color gridColor = Color.white * 0.8f;
  5. Color cursorColor = Color.red;
  6. Color selectColor = Color.blue;
  7. bool showGrid = true;
  8. void OnGUI()
  9. {
  10.    //Header
  11.    BuildMenuHeader();
  12. }
  13. void BuildMenuHeader()
  14. {
  15.    GUILayout.Label("GRID SETTINGS", EditorStyles.boldLabel);
  16.    gridSize = EditorGUILayout.IntField("Grid Size (px)", gridSize);
  17.    gridX = gridSize / 100f;
  18.    gridY = gridSize / 100f;
  19.    gridColor = EditorGUILayout.ColorField("Grid Color", gridColor);
  20.    cursorColor = EditorGUILayout.ColorField("Cursor Color", cursorColor);
  21.    selectColor = EditorGUILayout.ColorField("Select Color", selectColor);
  22.    showGrid = EditorGUILayout.Toggle("Show Grid",showGrid);
  23. }

The “OnGUI” function is the one that renders all the elements you want inside your new window. In this particular case, I’m adding general properties for the grid just to illustrate how it works. You can directly put the GUI code inside this function but I’m getting the exact code I made for the tile editor, that’s why I added the “BuildMenuHeader” function.

The “BuildMenuHeader” basically includes all the elements that affect the grid and change the variable values according to whatever the user chooses.

We can see in line 16 a short example about how the variables are modified in real time, “EditorGUILayout.ColorField” takes a couple of parameters, the label for that element and also the current value, this function returns the modified value in real time, that’s why we use and modify the “gridSize” variable in the same line.

SAVING THE STATUS

The previous section was about creating a very basic window with few elements that make our variables change in real time. The other important step for this post is about how to take those values we change and keep using them after we close the window. If we don’t do this, everytime that we close and re-open our new extension’s window, all the parameters will be initiallized again.

  1. bool loaded;
  2. void OnEnable()
  3. {
  4.    LoadData();
  5. }
  6.  
  7. void OnDisable()
  8. {
  9.    SaveData();
  10. }
  11.  
  12. void OnDestroy()
  13. {
  14.    SaveData();
  15. }
  16.  
  17. ///
  18. /// Saves the current editor's status
  19. ///
  20. public void SaveData()
  21. {
  22.    string id;
  23.  
  24.    id = PlayerSettings.productName;
  25.    EditorPrefs.SetInt(id + "-GridSize", gridSize);
  26.    EditorPrefs.SetString(id + "-CursorColor", FromColorToString(cursorColor));
  27.    EditorPrefs.SetString(id + "-GridColor", FromColorToString(gridColor));
  28.    EditorPrefs.SetString(id + "-SelectColor", FromColorToString(selectColor));
  29.    EditorPrefs.SetBool(id + "-ShowGrid", showGrid);
  30. }
  31.  
  32. ///
  33. /// Convert an object from class Color into a serialized string
  34. ///
  35. public string FromColorToString(Color color)
  36. {
  37.    return color.r + "," + color.g + "," + color.b + "," + color.a;
  38. }
  39.  
  40. ///
  41. /// Convert a string into an object from Color
  42. ///
  43. public Color FromStringToColor(string color)
  44. {
  45.    string[] desColor;
  46.    desColor = color.Split(',');
  47.  
  48.    return new Color(float.Parse(desColor[0]), float.Parse(desColor[1]), float.Parse(desColor[2]), float.Parse(desColor[3]));
  49. }
  50.  
  51. ///
  52. /// Restore the editor's status
  53. ///
  54. public void LoadData()
  55. {
  56.    string id;
  57.  
  58.    id = PlayerSettings.productName;
  59.  
  60.    if (EditorPrefs.HasKey(id + "-GridSize"))
  61.       gridSize = EditorPrefs.GetInt(id + "-GridSize");
  62.  
  63.    if (EditorPrefs.HasKey(id + "-ShowGrid"))
  64.       showGrid = EditorPrefs.GetBool(id + "-ShowGrid");
  65.  
  66.    if (EditorPrefs.HasKey(id + "-CursorColor"))
  67.       cursorColor = FromStringToColor(EditorPrefs.GetString(id + "-CursorColor"));
  68.  
  69.    if (EditorPrefs.HasKey(id + "-GridColor"))
  70.       gridColor = FromStringToColor(EditorPrefs.GetString(id + "-GridColor"));
  71.  
  72.    if (EditorPrefs.HasKey(id + "-SelectColor"))
  73.       selectColor = FromStringToColor(EditorPrefs.GetString(id + "-SelectColor"));
  74.  
  75.    loaded = true;
  76. }

We have the “OnEnable” function that is executed every time the window is enabled; the “OnDisable” function that runs everytime the window is disabled and finally the “OnDestroy” function that runs when the window is closed. In these functions the status of the editor is saved and restored.

There are a couple of helpers in this part that make easier to save and restore objects from the Color class. “FromStringToColor” and “FromColorToString” are functions that transform the data to make the saving and restoring process easier.

The “SaveData” function basically stores all the important values in an internal storage (Editor Preferences). Depending on the type of data we want to save, we use different functions.

One important thing that has to be highlighted here is this: EditorPrefs.SetInt(id + “-variable id”, variable), the reason why we are using the “id” variable (this represents the name of the project) in the variable’s name is that this internal storage does not distinguish between projects, it means that if we do not add a project identifier, those values will be replaced if you try to use the editor again and we don’t want that.

The “LoadData” function just restores the data in the same way that the we save it but using the appropriate functions.

SUMMARY

In this first post of a series on how to create a tile editor extension for Unity, we covered two basic steps: how to create a new window and how to store the data we modify using that window.

With these basic concepts you can create whatever you want, add more elements to the menu, include a different functionality or develop a completely different extension, I recommend testing all the elements you can add to a menu and play with it to understand better how it works, that’s what I did.

Upcoming posts will have details about the rest of the code and I will let you know as soon as the full extension is approved in the Unity Asset Store so you can download it and use it for your projects.

Tile Editor for Unity: Part I

Leave a Reply

Your email address will not be published.

*