DEV Community

Cover image for Create an Item Editor with UI Elements without uss and uxml in Unity
Cem Ugur Karacam
Cem Ugur Karacam

Posted on • Edited on

Create an Item Editor with UI Elements without uss and uxml in Unity

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode
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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
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);
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)