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.
If you wanna move along, grab the code here.
- Editor window
- Layout with styles
- Add a scroll view
- Add fields
- 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.
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);
}
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!
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);
}
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!