So you wanna do some editor scripting?

I’ve been following UI Elements since its preview release. Often, I tried from their examples repo but couldn’t dig in so much and this time I’ll make a little editor tool walkthrough.

UI Elements

UI Elements is the new kid in town for Unity developers. I give it a go their Recently released some official beginner resources here but I didn’t like that uss and uxml stuff. Since I kinda wanted ‘database-like editor tool’ to hack and learn this, I’ll stick with C# Api this time.

Here is the final product.

final

If you wanna move along, grab the code here.

  1. Editor window
  2. Layout with styles
  3. Add a scroll view
  4. Add fields
  5. Save to json

1. Editor window

This part is familiar, create a folder Editor make a script, I’ll say ItemEditor and hook up your editor window with MenuItem attribute.

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class ItemEditor : EditorWindow
{
    [MenuItem("Tools/Item Editor")]
    public static void OpenWindow()
    {
        var window = GetWindow<ItemEditor>();
        window.titleContent = new GUIContent("Item Editor");
    }
}

Nothing fancy, new menu will pop up in the editor you’ll get a fresh empty window. Next, draw some boxes.

2. Layout with styles

OnEnable call is entry point for my little editor. I’ll draw a box for listing items, and another to hold fields for adding a new item.

private void OnEnable()
{
    var root = this.rootVisualElement;
    
    var itemsListBox = new Box();
    var newItemBox = new Box();

    root.Add(itemsListBox);
    root.Add(newItemBox);
}

Well, if you look your window again, you won’t see anything. So this is where layout involves. I’ll divide my window as 1 portion for list, 3 portion for new item section.

private void OnEnable()
{
    var root = this.rootVisualElement;
    root.style.flexDirection = FlexDirection.Row;
    
    var itemsListBox = new Box();
    itemsListBox.style.flexGrow = 1f;
    itemsListBox.style.flexShrink = 0f;
    itemsListBox.style.flexBasis = 0f;
    itemsListBox.style.flexDirection = FlexDirection.Column;

    var newItemBox = new Box();
    newItemBox.style.flexGrow = 3f;
    newItemBox.style.flexShrink = 0f;
    newItemBox.style.flexBasis = 0f;
    
    root.Add(itemsListBox);
    root.Add(newItemBox);
}

First of all, if you haven’t heard about flex css like me before, it may be a good idea to look at how it works, since it uses a flex engine under the hood.

Now I can see my boxes.

sample 1

https://docs.unity3d.com/Manual/UIE-LayoutEngine.html

And this is the resource that helped me to understand flex.

https://css-tricks.com/snippets/css/a-guide-to-flexbox/

Yeah, mo more StartHorizontal or other IMGUI method for layout your window. You won’t need OnGUI at all.

3. Add a scroll view

It’s so simple, like adding a box I did above.

private ScrollView _scrollView;

private void SetupItemList(Box parent)
{
    _scrollView = new ScrollView();
    _scrollView.showHorizontal = false;
    _scrollView.style.flexGrow = 1f;
    parent.Add(_scrollView);
}

Also, a label fits good.

private void SetupItemList(Box parent)
{
    var listLabel = new Label("Item List");
    listLabel.style.alignSelf = Align.Center;
    listLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
    parent.Add(listLabel);

    _scrollView = new ScrollView();
    _scrollView.showHorizontal = false;
    _scrollView.style.flexGrow = 1f;
    parent.Add(_scrollView);
}

sample 2

4. Add fields

Let’s populate other box with some fields. Name, rarity and price is simple enough for my little tool.

private void SetupFields(Box parent)
{
    var newItemLabel = new Label("New Item");
    newItemLabel.style.alignSelf = Align.Center;
    newItemLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
    parent.Add(newItemLabel);

    var nameField = new TextField("Name: ");
    var rarityField = new EnumField("Rarity: ", ItemRarity.Common);
    var priceField = new IntegerField("Price: ");
    parent.Add(nameField);
    parent.Add(rarityField);
    parent.Add(priceField);
    
    var saveItemButton = new Button();
    saveItemButton.text = "Save Item";
    parent.Add(saveItemButton);
}

Now, I’ll create a method for adding items to my scroll view list and when I click to save item button, it’ll get values from fields.

private void CreateListItem(string name)
{
    var itemElement = new VisualElement();
    itemElement.style.flexDirection = FlexDirection.Row;
    itemElement.focusable = true;
    itemElement.name = name;

    var remove = new Button();
    remove.text = "-";
    itemElement.Add(remove);

    var nameButton = new Button();
    nameButton.text = name;
    nameButton.style.flexGrow = 1f;
    itemElement.Add(nameButton);

    _scrollView.contentContainer.Add(itemElement);
}

I create an empty visual element and afterwards, I add it to scroll view as a whole. Pretty clean looking and easy comparing between IMGUI api.

Next, I’ll create a class ItemData.

[System.Serializable]
public class ItemData
{
    public int id;
    public string name;
    public ItemRarity rarity;
    public int price;
}

Now I can save my item when I hit to save button.

private List<ItemData> _savedItems = new List<ItemData>();

private void SetupFields(Box parent)
{
    var newItemLabel = new Label("New Item");
    newItemLabel.style.alignSelf = Align.Center;
    newItemLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
    parent.Add(newItemLabel);

    var nameField = new TextField("Name: ");
    var rarityField = new EnumField("Rarity: ", ItemRarity.Common);
    var priceField = new IntegerField("Price: ");
    parent.Add(nameField);
    parent.Add(rarityField);
    parent.Add(priceField);
    
    var saveItemButton = new Button();
    saveItemButton.text = "Save Item";
    saveItemButton.clicked += () => {
        if (string.IsNullOrWhiteSpace(nameField.value) == false)
        {
            var item = new ItemData();
            item.id = new Guid().GetHashCode();
            item.name = nameField.value;
            item.rarity = (ItemRarity)rarityField.value;
            item.price = priceField.value;

            _savedItems.Add(item);

            //  set default values to clear fields
            nameField.value = "";
            rarityField.value = ItemRarity.Common;
            priceField.value = 0;

            CreateListItem(item.name);
        }
    };

    parent.Add(saveItemButton);
}

And, I’ll add a couple test items. It works!

Alt Text

5. Save to json

Finally I should be able to save and load my data. I’ll save my items as a json file in Resources folder.

To handle my json data, I’ll create a class and JsonUtility will do the magic.

public class ItemsFile
{
    public List<ItemData> data;
}
public void LoadData()
{
    var path = Application.dataPath + "/Resources/items.json";

    if (System.IO.File.Exists(path))
    {
        var file = System.IO.File.ReadAllText(path);
        _savedItems = JsonUtility.FromJson<ItemsFile>(file).data;
        
        foreach (var item in _savedItems)
        {
            CreateListItem(item.name);
        }
    }
}
public void SaveData()
{
    var path = Application.dataPath + "/Resources/items.json";

    var itemsFile = new ItemsFile();
    itemsFile.data = new List<ItemData>(_savedItems);
    var itemsFileAsJson = JsonUtility.ToJson(itemsFile);
    System.IO.File.WriteAllText(path, itemsFileAsJson);
}

Just load when window opens and save when you hit save button. Now I have a working sharp looking item editor tool that save data as json.

As a final touch, I’ll add removing an item from my list.

private void CreateListItem(ItemData itemData)
{
    var itemElement = new VisualElement();
    itemElement.style.flexDirection = FlexDirection.Row;
    itemElement.focusable = true;

    var remove = new Button();
    remove.text = "-";
    remove.clicked += () => {
        _scrollView.contentContainer.Remove(itemElement);
        _savedItems.Remove(itemData);
        SaveData();
    };
    itemElement.Add(remove);

    var nameButton = new Button();
    nameButton.text = itemData.name;
    nameButton.style.flexGrow = 1f;
    itemElement.Add(nameButton);

    _scrollView.contentContainer.Add(itemElement);
}

sample 5

That’s all for now. UI Elements proved me that it’s better aproach than legacy editor scripting and I’ll enjoyed without uss and uxml so far :)

If you missed above, grab the code here.

Do you like styling and this flex styling in unity? Let me know what do you think.

Thanks for reading!