本文將實現 cvs 表格資料與 UI Toolkit 元素的動態繫結。
如果讀者對 UI Toolkit 不是太瞭解,可以參考以下內容。
本文完整資源見→UI Toolkit資料動態繫結。
樣式和 UI 層級結構如下。
MainLayout.xml
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
<Style src="project://database/Assets/Role/View/StyleSheets/RoleStyle.uss?fileID=7433441132597879392&guid=d93d80f270ec5014c90e97cc8c404d1f&type=3#RoleStyle" />
<ui:VisualElement name="Background" style="flex-grow: 1; background-image: url('project://database/Assets/Role/Img/Background_Sky.png?fileID=2800000&guid=02ebb0e77ccd96143911134d6e39e1db&type=3#Background_Sky'); padding-left: 4%; padding-right: 4%; padding-top: 4%; padding-bottom: 4%; -unity-background-scale-mode: scale-and-crop;">
<ui:Label text="Game Role" display-tooltip-when-elided="true" name="TitleLab" style="height: 10%; margin-bottom: 1%; -unity-text-align: middle-left; font-size: 100px; -unity-font-style: italic; color: rgb(34, 34, 34);" />
<ui:VisualElement name="Body" style="flex-grow: 1; flex-direction: row;">
<ui:VisualElement name="RoleTemplate" style="flex-basis: 25%; margin-left: 10px; margin-right: 10px; margin-top: 10px; margin-bottom: 10px; background-color: rgba(0, 0, 0, 0.2); border-top-left-radius: 10px; border-bottom-left-radius: 10px; border-top-right-radius: 10px; border-bottom-right-radius: 10px;">
<ui:VisualElement name="Image" style="flex-basis: 50%; margin-left: 5%; margin-right: 5%; margin-top: 5%; margin-bottom: 0; background-color: rgba(0, 0, 0, 0.39); border-top-left-radius: 10px; border-top-right-radius: 10px; background-image: url('project://database/Assets/Role/Img/Avatar_1.png?fileID=2800000&guid=95b3aee3bc9bae64f8b70aba356b50b1&type=3#Avatar_1'); -unity-background-scale-mode: scale-and-crop;" />
<ui:Label text="角色" display-tooltip-when-elided="true" name="NameLab" style="margin-left: 3%; margin-right: 3%; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0; background-color: rgb(255, 96, 96); border-top-left-radius: 10px; border-bottom-left-radius: 10px; border-top-right-radius: 10px; border-bottom-right-radius: 10px; flex-shrink: 1; font-size: 35px; -unity-text-align: middle-center; color: rgb(255, 254, 254);" />
<ui:VisualElement name="Properties" style="flex-grow: 1; margin-left: 5%; margin-right: 5%; margin-top: 0; margin-bottom: 5%; background-color: rgba(0, 0, 0, 0.39); border-bottom-right-radius: 10px; border-bottom-left-radius: 10px;">
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="等級" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="行動力" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="最大HP" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="最大MP" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="攻擊力" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="防禦力" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>
RoleStyle.uss
#RoleTemplate:hover {
transition-duration: 0.1s;
translate: 0 -20px;
border-left-width: 5px;
border-right-width: 5px;
border-top-width: 5px;
border-bottom-width: 5px;
border-left-color: rgb(248, 242, 242);
border-right-color: rgb(248, 242, 242);
border-top-color: rgb(248, 242, 242);
border-bottom-color: rgb(248, 242, 242);
}
#Property Label {
font-size: 25px;
color: rgba(0, 0, 0, 255);
-unity-text-align: middle-center;
-unity-font-style: bold;
}
顯示效果如下。
在 Hierarchy 視窗選中 RoleTemplate 元素,右鍵彈出選單,選擇 Create Template,選擇 Resources 目錄下儲存 RoleTemplate.uxml,修改 Grow 為 1。
RoleTemplate.xml
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<ui:VisualElement name="RoleTemplate" style="flex-basis: 25%; margin-left: 10px; margin-right: 10px; margin-top: 10px; margin-bottom: 10px; background-color: rgba(0, 0, 0, 0.2); border-top-left-radius: 10px; border-bottom-left-radius: 10px; border-top-right-radius: 10px; border-bottom-right-radius: 10px; flex-grow: 1;">
<ui:VisualElement name="Image" style="flex-basis: 50%; margin-left: 5%; margin-right: 5%; margin-top: 5%; margin-bottom: 0; background-color: rgba(0, 0, 0, 0.39); border-top-left-radius: 10px; border-top-right-radius: 10px; background-image: url('project://database/Assets/Role/Img/Avatar_1.png?fileID=2800000&guid=95b3aee3bc9bae64f8b70aba356b50b1&type=3#Avatar_1'); -unity-background-scale-mode: scale-and-crop;" />
<ui:Label text="角色" display-tooltip-when-elided="true" name="NameLab" style="margin-left: 3%; margin-right: 3%; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0; background-color: rgb(255, 96, 96); border-top-left-radius: 10px; border-bottom-left-radius: 10px; border-top-right-radius: 10px; border-bottom-right-radius: 10px; flex-shrink: 1; font-size: 35px; -unity-text-align: middle-center; color: rgb(255, 254, 254);" />
<ui:VisualElement name="Properties" style="flex-grow: 1; margin-left: 5%; margin-right: 5%; margin-top: 0; margin-bottom: 5%; background-color: rgba(0, 0, 0, 0.39); border-bottom-right-radius: 10px; border-bottom-left-radius: 10px;">
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="等級" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="行動力" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="最大HP" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="最大MP" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="攻擊力" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:VisualElement name="Property" style="flex-direction: row; margin-left: 5px; margin-right: 5px; margin-top: 5px; margin-bottom: 5px; flex-grow: 0; justify-content: center;">
<ui:Label text="防禦力" display-tooltip-when-elided="true" name="Name" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(185, 251, 192); border-top-left-radius: 10px; border-bottom-left-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
<ui:Label text="1" display-tooltip-when-elided="true" name="Value" style="flex-basis: 50%; flex-shrink: 1; background-color: rgb(255, 200, 200); border-top-right-radius: 10px; border-bottom-right-radius: 10px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>
儲存模板後,刪除 Hierarchy 視窗中的 RoleTemplate 元素,後面會通過指令碼載入 RoleTemplate。
RoleView.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class RoleView : VisualElement {
// 便於在UI Builder中匯入自定義UI, 需要有無參建構函式
public new class UxmlFactory : UxmlFactory<RoleView> {}
private TemplateContainer container; // 模板容器
private List<VisualElement> properties; // 角色屬性
public RoleView() {
container = Resources.Load<VisualTreeAsset>("RoleTemplate").Instantiate();
container.style.flexGrow = 1;
hierarchy.Add(container);
properties = container.Query("Property").ToList();
}
public RoleView(RoleData roleData) : this() {
userData = roleData;
UpdateRoleData();
container.RegisterCallback<MouseDownEvent>(OnClick);
}
private void OnClick(MouseDownEvent mouseDownEvent) { // 單擊角色模板回撥函數
RoleData roleData = (RoleData) userData;
if (mouseDownEvent.button == 0) { // 按下滑鼠左鍵
roleData.RoleLevel++;
} else if (mouseDownEvent.button == 1) { // 按下滑鼠右鍵
roleData.RoleLevel--;
}
UpdateRoleData();
}
private void UpdateRoleData() { // 更新角色資料
RoleData roleData = (RoleData) userData;
container.Q<VisualElement>("Image").style.backgroundImage = roleData.RoleImage;
container.Q<Label>("NameLab").text = roleData.RoleName;
SetProperty(properties[0], roleData.RoleLevel);
SetProperty(properties[1], roleData.LevelData.initiative);
SetProperty(properties[2], roleData.LevelData.maxHp);
SetProperty(properties[3], roleData.LevelData.maxMp);
SetProperty(properties[4], roleData.LevelData.attack);
SetProperty(properties[5], roleData.LevelData.defense);
}
private void SetProperty(VisualElement property, int value) { // 更新角色屬性
property.Q<Label>("Value").text = value.ToString();
}
}
LevelData.cs
public class LevelData { // 等級屬性資料
public int initiative; // 主動權(行動力/速度)
public int maxHp; // 最大生命值
public int maxMp; // 最大魔法值
public int attack; // 攻擊力
public int defense; // 防禦力
}
RoleData.cs
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = ("RoleData"), fileName = ("RoleData_"))]
public class RoleData : ScriptableObject { // 角色屬性資料
private const int roleMaxLevel = 10; // 最大等級
[SerializeField]
private TextAsset levelDataFile; // 等級資料csv檔案
[SerializeField]
private Texture2D roleImage; // 角色頭像
[SerializeField]
private string roleName; // 角色名
[SerializeField, Range(1, roleMaxLevel)]
private int roleStartLevel = 1; // 角色開始等級
[SerializeField]
private List<LevelData> levelDatas; // 等級資料
private int roleLevel; // 角色當前等級
public Texture2D RoleImage => roleImage; // 獲取角色頭像
public string RoleName => roleName; // 獲取角色名
public int RoleLevel { // 獲取/設定角色等級
get => roleLevel;
set {
if (roleLevel == value || value < 1 || value > roleMaxLevel) {
return;
}
roleLevel = value;
}
}
public LevelData LevelData => levelDatas[roleLevel - 1]; // 獲取角色等級資料
private void OnEnable() {
roleLevel = roleStartLevel;
}
private void OnValidate() {
if (levelDataFile == null) {
return;
}
if (levelDatas == null) {
levelDatas = new List<LevelData>();
}
levelDatas.Clear();
string[] textInLines = levelDataFile.text.Split('\n');
for (int i = 1; i < textInLines.Length; i++) {
string[] statsValues = textInLines[i].Split(",");
LevelData levelData = new LevelData();
levelData.initiative = int.Parse(statsValues[0]);
levelData.maxHp = int.Parse(statsValues[1]);
levelData.maxMp = int.Parse(statsValues[2]);
levelData.attack = int.Parse(statsValues[3]);
levelData.defense = int.Parse(statsValues[4]);
levelDatas.Add(levelData);
}
}
}
編譯後,在 Assets 視窗右鍵,依次選擇【Create→RoleData】,建立 4 個物件,對應 4 個角色的設定,分別重新命名為 RoleData_1.asset、RoleData_2.asset、RoleData_3.asset、RoleData_4.asset。
選中 ScriptableObject 組態檔後,在 Inspector 視窗設定角色屬性。
其中 LevelDataFile 是角色每個等級的屬性 cvs 表,內容如下。
RoleLoader.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class RoleLoader : MonoBehaviour {
[SerializeField]
private List<RoleData> roleDatas; // 角色資料
private VisualElement root; // 根容器
private void Awake() {
root = GetComponent<UIDocument>().rootVisualElement;
var bodyContainer = root.Q("Body");
bodyContainer.Clear();
for(int i = 0; i < roleDatas.Count; i++) {
RoleView roleView = new RoleView(roleDatas[i]);
roleView.style.flexBasis = Length.Percent(25.0f);
bodyContainer.Add(roleView);
}
}
}
說明:RoleLoader 指令碼元件掛在 UIDocument 物件上,並且需要將 RoleData_1.asset、RoleData_2.asset、RoleData_3.asset、RoleData_4.asset 賦給 RoleDatas,如下。
執行效果如下,單擊卡片,角色的等級會升 1 級,等級屬性也會按照 cvs 表格中的策略資料同步更新。
宣告:本文轉自【Unity3D】UI Toolkit資料動態繫結。