Unity3d UGUI如何優雅的實現Web框架(Vue/Rect)類似數據綁定功能(含源碼)

前言

Unity3d的UGUI系統與Web前端開發中常見的數據綁定和屬性綁定機制有所不同。UGUI是一個相對簡單和基礎的UI系統,并不內置像Web前端(例如 Vue.js或React中)那樣的雙向數據綁定或自動更新UI的機制。UGUI是一種比較傳統的 UI 系統,它更側重于基于事件的UI更新和手動控制視圖的更新。在 UGUI中,如果數據變化了,開發者需要手動更新UI元素(例如文本、按鈕狀態、進度條等)。這種方式雖然靈活,但需要開發者自己處理每個UI更新的時機和邏輯。
對比數據可以通過雙大括號 {{}} 語法直接綁定到模板中,無需手動處理DOM元素;而Unity3d的Text修改文字內容則需要通過Text.text屬性來修改,對比起來比較麻煩,特比實在數據變量頻繁變更的情況下。要是在Unity的UGUI中實現了數據綁定,可以提高代碼冗余、提高UI數據更新開發效率、解耦數據和UI的關聯。目前實現的功能有Text內{{}}綁定數據、顏色綁定(Graphic.color)、圖片綁定(Image.sprite)、列表綁定和屬性綁定等功能。

盡管它有著諸多的優勢,如減少了代碼冗余、提高了開發效率和提高了應用的可維護性。它通過解耦視圖和數據,使得開發者能夠更關注業務邏輯,而不是繁瑣的 UI 更新操作。這使得開發者能夠更加專注于應用的核心功能,提升了代碼質量和可擴展性。但是數據綁定也有一些挑戰,特別是性能優化方面(尤其是在大規模數據或復雜 UI 交互時)。

關注并私信 U3D數據綁定 免費獲取源碼(底部公眾號)。

效果

文字綁定和列表刷新:
在這里插入圖片描述

滑動條綁定:

在這里插入圖片描述

列表綁定:

在這里插入圖片描述

顏色綁定:

在這里插入圖片描述

實現

大致實現思路如下:
1.先建立一個類(DataContext)作為管理鍵值對的容器,它允許通過鍵(key)來訪問和修改與之相關聯的值(value)。這個類還支持在值發生更改時觸發一個事件(Changed),事件觸發時,會傳遞觸發變化的鍵。

using System;
using System.Collections.Generic;public class DataContext
{public event Action<string> contextChanged = delegate { };private IDictionary<string, object> m_ActiveBinds = new Dictionary<string, object>();public bool ContainsKey(string key){return m_ActiveBinds.ContainsKey(key);}
}

2.再建立一個DataBindContext類,管理和更新數據綁定的類。它通過 DataContext 來存儲數據,并在數據變化時通知相關的 UI 組件更新。:當數據變化時,BindChanged 方法會被觸發,自動更新所有依賴于該數據的 UI 組件。

using UnityEngine;//數據綁定類
public class DataBindContext : MonoBehaviour
{private DataContext m_DataContext;public object this[string key]{get { return m_DataContext[key]; }set{if (m_DataContext == null){m_DataContext = new DataContext();m_DataContext.contextChanged += BindChanged;}m_DataContext[key] = value;}}public void BindChanged(string key){var children = GetComponentsInChildren<IBindable>();if (children == null)return;for (var i = 0; i < children.Length; i++)if (string.IsNullOrEmpty(children[i].key) || children[i].key == key)children[i].Bind(m_DataContext);}}

3.再建立IBindable 接口,IBindable 提供了一個統一的接口來進行綁定鍵的數據更新,包括key屬性(用于綁定的key值)和Bind(DataContext context)方法,Bind方法接收一個 DataContext 參數,UI 組件通過此方法將數據模型綁定到自身,監聽數據變化,并在數據變化時自動更新UI。

public interface IBindable
{string key { get; }void Bind(DataContext context);
}

Text綁定

Text內采用“{{}}”綁定數據,是最最常用的數據綁定,例如{{Number:N0}}{{Test}}綁定了Number和Test的鍵值,當檢測到數據變更會進行刷新。
顯示前:
在這里插入圖片描述

代碼調用變更

DataBindContext.instance?.SetKeyValue("Number", 57729);
DataBindContext.instance?.SetKeyValue("Test", "測試綁定");

顯示后:
在這里插入圖片描述

Text的綁定實現就是通過實現IBindable接口的Bind方法,在其中將匹配的字符串進行替換變量數值,代碼如下:

 m_Text.text = Regex.Replace(m_OriginalText, @"\{\{[^}]*}}", m =>
{var target = m.Value.Substring(2, m.Value.Length - 4).Split(':');var key = target[0];if (context.ContainsKey(key)) {var val = context[key];if (target.Length == 2 && val is IFormattable) {var format = target[1];return ((IFormattable) val).ToString(format, CultureInfo.CurrentCulture);}return val.ToString();
}return "";
});

屬性綁定

屬性綁定是將兩個UGUI的組件屬性直接做一個關聯,當然關聯之前開發者也需要了解兩個屬性間值是否真的能關聯匹配。這里以Slider的value 關聯到 Text的text屬性為例:
在這里插入圖片描述

這樣運行時候Slider的value屬性就會同步到Text.text顯示:
在這里插入圖片描述

同時屬性綁定可以選擇方向和多種更新同步方式:

在這里插入圖片描述

不同更新同步模式的代碼:

private void Update()
{if (m_Update == UpdateMethod.OnUpdate) {UpdateBind();}
}private void FixedUpdate()
{if (m_Update == UpdateMethod.OnFixedUpdate) {UpdateBind();}
}private void LateUpdate()
{if (m_Update == UpdateMethod.OnLateUpdate) {UpdateBind();}
}

屬性更新實現:

public void UpdateBind()
{if (m_SourceProperty == null || m_DestinationProperty == null){return;}if (m_CachedSourceProperty == null || m_CachedSourceProperty.Name != m_SourceProperty|| m_CachedDestinationProperty == null || m_CachedDestinationProperty.Name != m_DestinationProperty){Cache();}switch (m_Direction){case Direction.SourceUpdatesDestination:if (m_CachedDestinationProperty.PropertyType == typeof(string)){m_CachedDestinationProperty.SetValue(m_Destination, m_CachedSourceProperty.GetValue(m_Source, null).ToString(),null);}else{m_CachedDestinationProperty.SetValue(m_Destination, m_CachedSourceProperty.GetValue(m_Source, null), null);}break;case Direction.DestinationUpdatesSource:if (m_CachedSourceProperty.PropertyType == typeof(string)){m_CachedSourceProperty.SetValue(m_Source, m_CachedDestinationProperty.GetValue(m_Destination, null).ToString(),null);}else{m_CachedSourceProperty.SetValue(m_Source, m_CachedDestinationProperty.GetValue(m_Destination, null), null);}break;}
}public void Cache()
{m_CachedSourceProperty = m_Source.GetType().GetProperty(m_SourceProperty);m_CachedDestinationProperty = m_Destination.GetType().GetProperty(m_DestinationProperty);
}

圖片綁定

[SerializeField]
[Header("綁定對象")]
private Image m_Image;
[SerializeField]
[Header("綁定鍵名")]
private string m_Key;public string key
{get { return m_Key; }
}public void Bind(DataContext context)
{if (context.ContainsKey(m_Key)){m_Image.sprite = (Sprite)context[m_Key];}
}

顏色綁定

[SerializeField]
[Header("綁定對象")]
private Graphic m_Graphic;
[SerializeField]
[Header("綁定鍵名")]
private string m_Key;public string key
{get { return m_Key; }
}public void Bind(DataContext context)
{if (context.ContainsKey(m_Key)){m_Graphic.color = (Color)context[m_Key];}
}

滑動條綁定

private Slider m_Slider;[Header("綁定鍵名")]
public string m_Key;public string key
{get { return m_Key; }
}
public void Bind(DataContext context)
{if (m_Slider == null)m_Slider = GetComponent<Slider>();if (context.ContainsKey(key)){m_Slider.value = (float)context[key];}
}

列表綁定

列表的綁定其實需要預設節點名稱和綁定鍵名等設置,如下:

[SerializeField]
[Header("列表節點預設")]
private GameObject m_ItemPrefab;
[SerializeField]
[Header("節點鍵名")]
public string m_ItemKey;
[SerializeField]
[Header("綁定鍵名")]
public string m_Key;

數據綁定刷新的代碼如下:

if (m_ItemPrefab == null)
{Debug.LogWarning("節點預設為空,無法綁定列表!");return;
}m_Context = new DataContext();if (context.ContainsKey(m_Key))
{var list = (ObservableList)context[m_Key];if (list.Count > itemObjList.Count){for (int i = itemObjList.Count; i < list.Count; i++){GameObject go = GameObject.Instantiate(m_ItemPrefab);go.transform.SetParent(transform, false);go.transform.localScale = Vector3.one;go.transform.localEulerAngles = Vector3.zero;go.transform.name = i.ToString("D4") + "item";itemObjList.Add(go);}}elsefor (int i = list.Count; i < itemObjList.Count; i++)itemObjList[i].SetActive(false);for (int i = 0; i < list.Count; i++){var itemData = list[i];var item = itemObjList[i];var bindables = item.GetComponentsInChildren<IBindable>(true);var properties = itemData.GetType().GetProperties();var model = item.GetComponent<IModel>();if (model != null)model.model = itemData;for (var j = 0; j < properties.Length; j++){var p = properties[j];m_Context[m_ItemKey + "." + p.Name] = p.GetValue(itemData, null);}for (var j = 0; j < bindables.Length; j++)bindables[j].Bind(m_Context);itemObjList[i].SetActive(true);}}

其核心思路就是存在該鍵變更時,根據列表數據顯示或者隱藏、并刷新所有子節點。通過鍵的值以列表的形式,節點不夠時克隆節點的預設,生成節點,將單個節點的數據根據綁定配置刷新到對應的組件上,直到所有節點刷新完畢。

同時預設的節點數據需要與定義的類型結構統一,這里以排行榜為例,其數據結構如下:

class RankItem
{public int index { get; set; }public string name { get; set; }public float score { get; set; }
}

index、name和score分別表示排名、玩家名稱和分數。
所以單個節點的預設的綁定應該如下配置:
在這里插入圖片描述

列表的鍵采用Ranks,同時通過如下代碼生成假數據:

int count = 10;
for (int i = count; i > 0; i--)
{m_RankItems.Add(new RankItem{index = count - i + 1,score = i * 100 + Random.Range(0, 7.6f),name = "玩家名稱" + (count - i + 1)});
}DataBindContext.instance?.SetKeyValue("Ranks", m_RankItems);

綁定列表的配置最終如下圖:
在這里插入圖片描述

綁定與變更

綁定值變更數據的鍵值變更采用如下代碼:

DataBindContext m_Context["鍵名"] =;

單例模式也可采用來修改值:

DataBindContext.instance?.SetKeyValue("鍵名",);

演示功能

這里簡單搭建一個覆蓋功能的UI:

在這里插入圖片描述

掛上對應的腳本(注意DataBindContext 需要掛在最外層)后運行效果:
在這里插入圖片描述

項目源碼

https://download.csdn.net/download/qq_33789001/90195629

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/64288.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/64288.shtml
英文地址,請注明出處:http://en.pswp.cn/web/64288.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

OptimisticLock

想象你和你的朋友去了一家很受歡迎的餐廳。你們想要點一份特別的菜品——這家餐廳的招牌菜&#xff0c;但因為這道菜非常受歡迎&#xff0c;所以它的狀態可能會隨時變化&#xff08;比如售罄或重新上架&#xff09;。 傳統方式&#xff08;悲觀鎖&#xff09; 通常情況下&…

10分鐘掌握項目管理核心工具:WBS、甘特圖、關鍵路徑法全解析

一、引言 在項目管理的廣闊天地里&#xff0c;猶如一場精心編排的交響樂演奏&#xff0c;每個樂器、每個音符都需精準配合才能奏響美妙樂章。而 WBS&#xff08;工作分解結構&#xff09;、甘特圖、關鍵路徑法無疑是這場交響樂中的關鍵樂章&#xff0c;它們從不同維度為項目管…

TCP 和 UDP 的區別:解析網絡傳輸協議

引言 在計算機網絡的世界中&#xff0c;TCP&#xff08;Transmission Control Protocol&#xff0c;傳輸控制協議&#xff09;和 UDP&#xff08;User Datagram Protocol&#xff0c;用戶數據報協議&#xff09;是兩種極為重要且應用廣泛的傳輸層協議。它們在功能、特性以及適…

代碼思想之快慢路徑

處理業務代碼的過程中&#xff0c;對業務代碼有了一些調整&#xff0c;后續發現這是一種代碼思想 在一段復雜的邏輯里&#xff0c;我把查詢redis操作寫在了前面&#xff0c; 業務邏輯&#xff1a; 如果需要不打壓就退出本次處理 查詢redis拿到商品需要打壓的次數 如果次數 …

Java 溯本求源之基礎(三十一)——泛型

目錄 1. 泛型的定義與基本概念 2. 泛型的優勢 3. 泛型的基本語法 3.1 泛型類 3.2 泛型方法 3.3 泛型接口 4. 泛型的邊界 4.1 上限通配符&#xff08;? extends T&#xff09; 4.2 下限通配符&#xff08;? super T&#xff09; 5. 泛型的類型擦除 6. 泛型的使用場景…

純 HTML+CSS+JS 實現一個炫酷的圣誕樹動畫特效

純 HTMLCSSJS 實現一個炫酷的圣誕樹動畫特效 前言 圣誕節快到了&#xff0c;今天給大家帶來一個簡單但是效果不錯的圣誕樹動畫特效。這個特效完全使用原生 HTML、CSS 和 JavaScript 實現&#xff0c;包含閃爍的星星、隨機彩燈等元素&#xff0c;非常適合節日氣氛&#xff01;…

Maven:Java項目構建與管理的利器

在Java開發領域&#xff0c;Maven無疑是一個舉足輕重的工具。它不僅簡化了項目的構建和依賴管理&#xff0c;還促進了團隊協作和持續集成。本文將深入探討Maven的核心功能、基本配置以及在實際項目中的應用。 Maven簡介 Maven是Apache基金會下的一個開源項目&#xff0c;旨在…

【ES6復習筆記】Promise對象詳解(12)

1. 什么是 Promise&#xff1f; Promise 是 JavaScript 中處理異步操作的一種機制&#xff0c;它可以讓異步操作更加容易管理和控制。Promise 對象代表一個異步操作的最終完成或失敗&#xff0c;并提供了一種方式來處理操作的結果。 2. Promise 的基本語法 Promise 對象有三…

【RAG實戰】語言模型基礎

語言模型賦予了計算機理解和生成人類語言的能力。它結合了統計學原理和深度神經網絡技術&#xff0c;通過對大量的樣本數據進行復雜的概率分布分析來學習語言結構的內在模式和相關性。具體地&#xff0c;語言模型可根據上下文中已出現的詞序列&#xff0c;使用概率推斷來預測接…

【ES6復習筆記】Map(14)

概念 Map 是 JavaScript 中的一種數據結構&#xff0c;它允許你存儲鍵值對&#xff0c;并且可以通過鍵來訪問對應的值。在本教程中&#xff0c;我們將學習如何聲明、添加、刪除、獲取和遍歷 Map 集合。 ES6 提供了 Map 數據結構。它類似于對象&#xff0c;也是鍵值對的集合。…

富芮坤FR800X系列之PWM輸出程序應用設計

文章目錄 前言1.設計背景2.簡介3.如何設計控制調光的接口呢4.硬件設計5.軟件設計5.1.軟件流程圖5.2.軟件代碼 6.小結 前言 版權歸作者所有、未經允許、請勿轉載。 讀者對象&#xff1a; 本文檔主要適用以下工程師&#xff1a; ?嵌入式系統工程師 ?單片機軟件工程師 ?IOT固…

Ftrans數據擺渡系統 搭建安全便捷跨網文件傳輸通道

一、專業數據擺渡系統對企業的意義 專業的數據擺渡系統對企業具有重要意義&#xff0c;主要體現在以下幾個方面?&#xff1a; 1、?數據安全性?&#xff1a;數據擺渡系統通過加密傳輸、訪問控制和審計日志等功能&#xff0c;確保數據在傳輸和存儲過程中的安全性。 2、?高…

EasyPoi 使用$fe:模板語法生成Word動態行

1 Maven 依賴 <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.0.0</version> </dependency> 2 application.yml spring:main:allow-bean-definition-over…

定義Shape:打造屬于你的獨特圖形

自定義Shape:打造屬于你的獨特圖形 在Android開發中,自定義圖形繪制是一個非常重要的技能,尤其是在需要實現復雜UI或特定設計需求時。Android提供了android.graphics.drawable.shapes包,其中包含了一些基本的形狀類,如RectShape、OvalShape等。然而,有時這些基本形狀無法…

python 滲透開發工具之SQLMapApi Server不同IP服務啟動方式處理 解決方案SqlMapApiServer外網不能訪問的情況

目錄 說在前面 什么是 SQLMapAPI 說明 sqlmapApi能干什么 sqlmapApi 服務安裝相關 kali-sqlmap存放位置 正常啟動sqlmap-api server SqlMapApi-Server 解決外網不能訪問情況 說在前面 什么是sqlmap 這個在前面已經說過了&#xff0c;如果這個不知道&#xff0c;就可以…

【基礎還得練】 KKT 條件

優秀教程-真正理解拉格朗日乘子法和 KKT 條件&#xff1a; link優秀教程-最優化(6)&#xff1a;一般約束優化問題的最優性理論&#xff1a; link KKT條件&#xff08;Karush-Kuhn-Tucker條件&#xff09;是非線性規劃中的一組必要條件&#xff0c;在某些情況下也是最優解的充分…

使用 Webpack 優雅的構建微前端應用?

Module Federation 通常譯作“模塊聯邦”&#xff0c;是 Webpack 5 新引入的一種遠程模塊動態加載、運行技術。MF 允許我們將原本單個巨大應用按我們理想的方式拆分成多個體積更小、職責更內聚的小應用形式&#xff0c;理想情況下各個應用能夠實現獨立部署、獨立開發(不同應用甚…

Boost之log日志使用

不講理論&#xff0c;直接上在程序中可用代碼&#xff1a; 一、引入Boost模塊 開發環境&#xff1a;Visual Studio 2017 Boost庫版本&#xff1a;1.68.0 安裝方式&#xff1a;Nuget 安裝命令&#xff1a; #只安裝下面幾個即可 Install-package boost -version 1.68.0 Install…

【MySQL】十四,MySQL 8.0的隱藏索引

在MySQL 8.0之前的版本中&#xff0c;索引只能直接刪除。如果刪除后發現引起了系統故障&#xff0c;又必須進行創建。當表的數據量比較大的時候&#xff0c;這樣做的代價就會非常高。 在MySQL 8.0中&#xff0c;提供了隱藏索引。如果想刪除某個索引&#xff0c;那么在實際刪除…

【ES6復習筆記】解構賦值(2)

介紹 解構賦值是一種非常方便的語法&#xff0c;可以讓我們更簡潔地從數組和對象中提取值&#xff0c;并且可以應用于很多實際開發場景中。 1. 數組的解構賦值 數組的解構賦值是按照一定模式從數組中提取值&#xff0c;然后對變量進行賦值。下面是一個例子&#xff1a; con…