如题,本人使用UITookit编写了一个UXML的UI界面后,将其搭载到了一个UIDocument对象上,并将该对象作为UI相机的子对象以实现UI界面与相机绑定。
在为UIDocument编写交互用的C#时,采用了下面的错误做法,导致UIDocument在随UI相机一同被禁用后,所编写的交互功能全部失效:
using System.Xml.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
public class ModuleUI : MonoBehaviour
{
private VisualElement rootVisualElement; // 用于获取根视图元素
private Button selectedButton; // 用于记录当前选中的按钮
private GameObject lastDisabledObject; // 记录被隐藏的构件
public static Button uploadButton;
public static Button createButton;
public static Button rotateButton;
public static Button translateButton;
public static Button zoomButton;
public static Button distanceButton;
public static Button selectButton;
public static Button roamingButton;
public static Button resetButton;
public static Button HSButton;
public static GroupBox HSIBox;
public static Label objectName;
public static Label objectLength;
public static Label objectWidth;
public static Label objectHeight;
public static StyleColor defaultColor;
public static StyleColor pressedColor = new(Color.blue); // 蓝色
public static bool onButton = false; // 标记鼠标在按钮上的悬停状态
private void start()
{
// 获取根视图元素
rootVisualElement = GetComponent<UIDocument>().rootVisualElement;
// 获取全部的按钮元素
uploadButton = rootVisualElement.Q<Button>("UploadBut");
createButton = rootVisualElement.Q<Button>("CreateBut");
rotateButton = rootVisualElement.Q<Button>("Button1");
translateButton = rootVisualElement.Q<Button>("Button2");
zoomButton = rootVisualElement.Q<Button>("Button3");
distanceButton = rootVisualElement.Q<Button>("Button4");
selectButton = rootVisualElement.Q<Button>("Button5");
roamingButton = rootVisualElement.Q<Button>("Button6");
resetButton = rootVisualElement.Q<Button>("ResetBut");
// 获取GroupBox元素及其子元素
HSIBox = rootVisualElement.Q<GroupBox>("HSIBox");
HSButton = HSIBox.Q<Button>("HSBut");
objectName = HSIBox.Q<Label>("ObjectName");
objectLength = HSIBox.Q<Label>("ObjectLength");
objectWidth = HSIBox.Q<Label>("ObjectWidth");
objectHeight = HSIBox.Q<Label>("ObjectHeight");
}
void Start()
{
// 获取所有的按钮元素
var buttons = rootVisualElement.Query<Button>();
// 为每个按钮添加鼠标悬停和离开事件
buttons.ForEach(button => {
button.RegisterCallback<MouseEnterEvent>(evt => {
onButton = true;
});
});
buttons.ForEach(button => {
button.RegisterCallback<MouseLeaveEvent>(evt => {
onButton = false;
});
});
// 文件上传按钮的功能实现
uploadButton.clicked += OpenFileByWin32.OpenFile;
// 模型生成按钮的功能实现
/*createButton.clicked += () => BackgroundModify(createButton);*/
// 模型旋转按钮的状态修改
rotateButton.clicked += () => BackgroundModify(rotateButton);
// 模型平移按钮的状态修改
translateButton.clicked += () => BackgroundModify(translateButton);
// 模型缩放按钮的状态修改
zoomButton.clicked += () => BackgroundModify(zoomButton);
// 自动测距按钮的状态修改
distanceButton.clicked += () => BackgroundModify(distanceButton);
// 选中组件按钮的状态修改
selectButton.clicked += () => BackgroundModify(selectButton);
// 显隐组件按钮的状态修改
HSButton.clicked += () => ButtonModify(HSButton);
// 室内漫游按钮的功能实现
roamingButton.clicked += FirefighterController.CharacterShow;
roamingButton.clicked += () => CameraController.SwitchCamera("view_Camera");
// 初始时隐藏部分UI元素
resetButton.style.display = DisplayStyle.None;
HSIBox.style.display = DisplayStyle.None;
}
private void BackgroundModify(Button evt)
{
if (selectedButton != null && selectedButton != evt)
{
selectedButton.style.backgroundColor = defaultColor;
}
if (evt.style.backgroundColor != pressedColor)
{
selectedButton = evt;
defaultColor = evt.style.backgroundColor;
evt.style.backgroundColor = pressedColor;
if (HSIBox.style.display == DisplayStyle.Flex)
{
HSIBox.style.display = DisplayStyle.None;
}
else if (resetButton.style.display == DisplayStyle.Flex)
{
resetButton.style.display = DisplayStyle.None;
resetButton.clicked -= ModelController.RetCamera;
}
switch (evt.name)
{
case "Button1":
case "Button2":
case "Button3":
resetButton.style.display = DisplayStyle.Flex;
resetButton.clicked += ModelController.RetCamera;
break;
case "Button5":
HSIBox.style.display = DisplayStyle.Flex;
break;
}
}
else
{
evt.style.backgroundColor = defaultColor;
switch (evt.name)
{
case "Button1":
case "Button2":
case "Button3":
resetButton.style.display = DisplayStyle.None;
resetButton.clicked -= ModelController.RetCamera;
break;
case "Button5":
objectName.text = "组件名称:";
objectLength.text = "组件水平长度:";
objectWidth.text = "组件水平宽度:";
objectHeight.text = "组件垂直高度:";
HSIBox.style.display = DisplayStyle.None;
break;
}
}
}
private void ButtonModify(Button evt)
{
if (evt.style.backgroundColor != pressedColor)
{
// 记录隐藏的构件
lastDisabledObject = ClickController.currentSelectedObject;
// 隐藏当前选中的构件
ClickController.currentSelectedObject.SetActive(false);
// 更换改按钮状态
defaultColor = evt.style.backgroundColor;
evt.style.backgroundColor = pressedColor;
evt.text = "显示";
}
else
{
// 显示上次隐藏的构件
lastDisabledObject.SetActive(true);
// 更换改按钮状态
evt.style.backgroundColor = defaultColor;
evt.text = "隐藏";
}
}
}
出现错误的原因有两个:
- 我们获取到的元素在脚本被禁用一次后,下次重新启用就会丢失(删改代码测试得到,例如根视图元素
rootVisualElement
在脚本重新启用后必须得再次检索),底层原因未知。 - 我们为
button.clicked
设定的回调函数,在禁用后重新激活就会失效,所有需要重新绑定。
要解决上面两个问题其实很简单,就是要弃用 Start
方法,改为重载 OnEnable
方法,下面是修改后的代码:
using System.Xml.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
public class ModuleUI : MonoBehaviour
{
private VisualElement rootVisualElement; // 用于获取根视图元素
private Button lastButton; // 用于记录当前选中的按钮
private GameObject lastDisabledObject; // 记录被隐藏的构件
public static Button uploadButton;
public static Button createButton;
public static Button rotateButton;
public static Button translateButton;
public static Button zoomButton;
public static Button distanceButton;
public static Button selectButton;
public static Button roamingButton;
public static Button resetButton;
public static Button HSButton;
public static GroupBox HSIBox;
public static Label objectName;
public static Label objectLength;
public static Label objectWidth;
public static Label objectHeight;
public static StyleColor defaultColor;
public static StyleColor pressedColor = new(Color.blue); // 蓝色
public static bool onButton = false; // 标记鼠标在按钮上的悬停状态
private void OnEnable()
{
/*Debug.Log("I do en");*/
// 获取根视图元素
rootVisualElement = GetComponent<UIDocument>().rootVisualElement;
// 获取全部的按钮元素
uploadButton = rootVisualElement.Q<Button>("UploadBut");
createButton = rootVisualElement.Q<Button>("CreateBut");
rotateButton = rootVisualElement.Q<Button>("Button1");
translateButton = rootVisualElement.Q<Button>("Button2");
zoomButton = rootVisualElement.Q<Button>("Button3");
distanceButton = rootVisualElement.Q<Button>("Button4");
selectButton = rootVisualElement.Q<Button>("Button5");
roamingButton = rootVisualElement.Q<Button>("Button6");
resetButton = rootVisualElement.Q<Button>("ResetBut");
// 获取GroupBox元素及其子元素
HSIBox = rootVisualElement.Q<GroupBox>("HSIBox");
HSButton = HSIBox.Q<Button>("HSBut");
objectName = HSIBox.Q<Label>("ObjectName");
objectLength = HSIBox.Q<Label>("ObjectLength");
objectWidth = HSIBox.Q<Label>("ObjectWidth");
objectHeight = HSIBox.Q<Label>("ObjectHeight");
// 获取根视图元素
rootVisualElement = GetComponent<UIDocument>().rootVisualElement;
// 获取所有的按钮元素
var buttons = rootVisualElement.Query<Button>();
// 为每个按钮添加鼠标悬停和离开判定
buttons.ForEach(button =>
{
button.RegisterCallback<MouseEnterEvent>(evt =>
{
onButton = true;
});
});
buttons.ForEach(button =>
{
button.RegisterCallback<MouseLeaveEvent>(evt =>
{
onButton = false;
});
});
// 文件上传按钮的功能实现
uploadButton.clicked += OpenFileByWin32.OpenFile;
// 模型生成按钮的功能实现
/*createButton.clicked += () => BackgroundModify(createButton);*/
// 模型旋转按钮的状态修改
rotateButton.clicked += () => BackgroundModify(rotateButton);
// 模型平移按钮的状态修改
translateButton.clicked += () => BackgroundModify(translateButton);
// 模型缩放按钮的状态修改
zoomButton.clicked += () => BackgroundModify(zoomButton);
// 自动测距按钮的状态修改
distanceButton.clicked += () => BackgroundModify(distanceButton);
// 选中组件按钮的状态修改
selectButton.clicked += () => BackgroundModify(selectButton);
// 显隐组件按钮的状态修改
HSButton.clicked += () => ButtonModify(HSButton);
// 室内漫游按钮的功能实现
roamingButton.clicked += FirefighterController.CharacterShow;
roamingButton.clicked += () => CameraController.SwitchCamera("view_Camera");
// 隐藏部分UI元素
resetButton.style.display = DisplayStyle.None;
HSIBox.style.display = DisplayStyle.None;
}
private void BackgroundModify(Button evt)
{
if (lastButton != null && lastButton != evt)
{
lastButton.style.backgroundColor = defaultColor;
}
if (evt.style.backgroundColor != pressedColor)
{
lastButton = evt;
defaultColor = evt.style.backgroundColor;
evt.style.backgroundColor = pressedColor;
if (HSIBox.style.display == DisplayStyle.Flex)
{
HSIBox.style.display = DisplayStyle.None;
}
else if (resetButton.style.display == DisplayStyle.Flex)
{
resetButton.style.display = DisplayStyle.None;
resetButton.clicked -= ModelController.RetCamera;
}
switch (evt.name)
{
case "Button1":
case "Button2":
case "Button3":
resetButton.style.display = DisplayStyle.Flex;
resetButton.clicked += ModelController.RetCamera;
break;
case "Button5":
HSIBox.style.display = DisplayStyle.Flex;
break;
}
}
else
{
evt.style.backgroundColor = defaultColor;
switch (evt.name)
{
case "Button1":
case "Button2":
case "Button3":
resetButton.style.display = DisplayStyle.None;
resetButton.clicked -= ModelController.RetCamera;
break;
case "Button5":
objectName.text = "组件名称:";
objectLength.text = "组件水平长度:";
objectWidth.text = "组件水平宽度:";
objectHeight.text = "组件垂直高度:";
HSIBox.style.display = DisplayStyle.None;
break;
}
}
}
private void ButtonModify(Button evt)
{
if (evt.style.backgroundColor != pressedColor)
{
// 记录隐藏的构件
lastDisabledObject = ClickController.currentSelectedObject;
// 隐藏当前选中的构件
ClickController.currentSelectedObject.SetActive(false);
// 更换改按钮状态
defaultColor = evt.style.backgroundColor;
evt.style.backgroundColor = pressedColor;
evt.text = "显示";
}
else
{
// 显示上次隐藏的构件
lastDisabledObject.SetActive(true);
// 更换改按钮状态
evt.style.backgroundColor = defaultColor;
evt.text = "隐藏";
}
}
}