新建一个Editor 文件夹用于存放生成的脚本。
启用后库里会导入只有在Editor下才能用的组件
双击UXML文件即可打开编辑器进行编辑界面
也可以用代码的方式创建界面
一般添加了新的ui元素后尽量重新命名下,方便后续的使用,重命名后物体名称都会自动添加#
符号。
VisualElement相当于空物体,可以用来组织ui层级结构。可以修改Flex > Direction 改变子ui的排列方式。
默认是黑色,但需要挪到其它颜色重新选择黑色才起作用!
public void CreateGUI()
{
// Each editor window contains a root VisualElement object
VisualElement root = rootVisualElement;
// Instantiate UXML
m_VisualTreeAsset.CloneTree(root);
var helpBox = new HelpBox("www",HelpBoxMessageType.None);
var helpBox1 = new HelpBox("www",HelpBoxMessageType.Info);
var helpBox2 = new HelpBox("www",HelpBoxMessageType.Warning);
var helpBox3 = new HelpBox("www",HelpBoxMessageType.Error);
//查找已有的ui节点,把代码创建的元素加入到该ui节点下。
var rightVE = root.Q<VisualElement>("right");
rightVE.Add(helpBox);
rightVE.Add(helpBox1);
rightVE.Add(helpBox2);
rightVE.Add(helpBox3);
}
通过代码添加的ui元素不会显示到UI Builder中,需要打开窗体查看
窗体位置可以通过脚本获知
通过监听ui相关事件进行交互逻辑处理。
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
public class SceneObjWindow : EditorWindow
{
[SerializeField]
private VisualTreeAsset m_VisualTreeAsset = default;
private ObjectField objectField;
private Button creatBtn;
private Button refreshBtn;
private ListView listView;
private GameObject[] sceneObjs;
private TextField selectItemName;
private Vector3Field selectLocalPos;
private IntegerField count;
private TextField changeTest;
[MenuItem("Window/UI Toolkit/SceneObjWindow")]
public static void ShowExample()
{
SceneObjWindow wnd = GetWindow<SceneObjWindow>();
wnd.titleContent = new GUIContent("SceneObjWindow");
}
public void CreateGUI()
{
// Each editor window contains a root VisualElement object
VisualElement root = rootVisualElement;
// Instantiate UXML
m_VisualTreeAsset.CloneTree(root);
//代码添加ui
{
var helpBox = new HelpBox("www", HelpBoxMessageType.None);
var helpBox1 = new HelpBox("www", HelpBoxMessageType.Info);
var helpBox2 = new HelpBox("www", HelpBoxMessageType.Warning);
var helpBox3 = new HelpBox("www", HelpBoxMessageType.Error);
var rightVE = root.Q<VisualElement>("right");
rightVE.Add(helpBox);
rightVE.Add(helpBox1);
rightVE.Add(helpBox2);
rightVE.Add(helpBox3);
}
//设置ObjectField的过滤
{
objectField = root.Q<ObjectField>("ObjectField");
objectField.objectType = typeof(GameObject);
objectField.allowSceneObjects = false;
}
//监听按钮点击事件
{
creatBtn = root.Q<Button>("CreatBtn");
creatBtn.clicked += OnCreateGo;
refreshBtn = root.Q<Button>("RefreshBtn");
refreshBtn.clicked += OnRefresh;
}
//ListView动态添加item
{
listView = root.Q<ListView>("listView");
listView.makeItem = MakeListViewItem;
listView.bindItem = BindListViewItem;
//监听item选中事件
listView.onSelectionChange += OnListViewSelectItemChange;
}
//数据绑定:始终与组件上的字段值保持同步
{
selectItemName = root.Q<TextField>("Name");
selectItemName.bindingPath = "m_Name";
selectLocalPos = root.Q<Vector3Field>("LocalPosition");
selectLocalPos.bindingPath = "m_LocalPosition";
count = root.Q<IntegerField>("Count");
count.bindingPath = "count";
}
//数据监听:数据发生改变就会触发回调,能取到改变前后的值
{
changeTest = root.Q<TextField>("ChangeTest");
changeTest.RegisterValueChangedCallback(OnValueChange);
}
}
private void OnValueChange(ChangeEvent<string> evt)
{
Debug.Log($"pre:{evt.previousValue},new:{evt.newValue}");
}
private void OnListViewSelectItemChange(IEnumerable<object> obj)
{
foreach (var item in obj)
{
var go = item as GameObject;
selectItemName.value = go.name;
selectLocalPos.value = go.transform.localPosition;
Selection.activeGameObject = go;
//数据绑定
{
SerializedObject so = new SerializedObject(go);
selectItemName.Bind(so);
SerializedObject so1 = new SerializedObject(go.transform);
selectLocalPos.Bind(so1);
}
var cube = go.GetComponent<MyCube>();
if (cube != null)
{
var so2 = new SerializedObject(cube);
count.Bind(so2);
count.visible = true;
}
else
{
count.Unbind();
count.visible = false;
}
}
}
private void BindListViewItem(VisualElement ve, int index)
{
Label label = ve as Label;
var go = sceneObjs[index];
label.text = go.name;
SerializedObject so = new SerializedObject(go);
label.Bind(so);
}
private VisualElement MakeListViewItem()
{
var label = new Label();
label.style.unityTextAlign = TextAnchor.MiddleLeft;
label.style.marginLeft = 5;
label.bindingPath = "m_Name";
return label;
}
private void OnRefresh()
{
sceneObjs = SceneManager.GetActiveScene().GetRootGameObjects();
//更新ListView
listView.itemsSource = sceneObjs;
Debug.Log($"场景物体:{sceneObjs.Length}个");
listView.RefreshItems();
}
private void OnCreateGo()
{
var prefab = objectField.value as GameObject;
if (prefab == null)
{
Debug.LogError("ObjectField不能为空!");
return;
}
var go = GameObject.Instantiate(prefab);
go.transform.position = new Vector3(Random.Range(-5, 5), Random.Range(-5, 5), Random.Range(-5, 5));
}
}
直接添加对应于库中的组件名称即可对所有该类型的组件进行样式控制,如图所示对Label
字体进行统一的颜色和大小设置。
通过指定ui的id(#xxx
)进行样式的控制,可以进行精准的控制
.xxx
即表示一个自定义的样式类,
配置好后可以拖拽到ui上进行应用
或者在ui上添加样式。
可以通过 >
定义应用了左边样式的ui子节点样式
伪类缩小了选择器的范围,因此它只匹配进入特定状态的元素。
将伪类附加到简单的选择器以匹配处于特定状态的特定元素。例如,以下 USS 规则使用 :hover 伪类在用户将指针悬停在 Button 元素上时更改其颜色。
有些时候使用了选择器却无法起作用
可以在相应的窗体上打开UI Toolkit Debugger进行排除
使用Pick Element
功能进行快速定位
可以看到除了.MyStyleClass > Label
外还有其它的,可以看下其它的样式是不是做了设置
可以看到其它的样式中定义了最小宽度135,
所以宽度无法修改到50,可以覆盖最小宽度为小于50的值
var scheduleItem =rootVisualElement.schedule.Execute(ScheduleAction);
//延迟两秒执行
scheduleItem.ExecuteLater(2000);
新建自定义Inspector脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;
[CustomEditor(typeof(MyCube))]
public class MyCubeInspector : Editor
{
[SerializeField]
private VisualTreeAsset m_VisualTreeAsset = default;
public override VisualElement CreateInspectorGUI()
{
VisualElement root = new VisualElement();
m_VisualTreeAsset.CloneTree(root);
return root;
}
}