Unity——QFramework框架 內置工具

QFramework 除了提供了一套架構之外,QFramework 還提供了可以脫離架構使用的工具 TypeEventSystem、EasyEvent、BindableProperty、IOCContainer。

這些工具并不是有意提供,而是 QFramework 的架構在設計之初是通過這幾個工具組合使用而成的。

內置工具

TypeEventSystem

基本用法:

using UnityEngine;namespace QFramework.Example
{public class TypeEventSystemBasicExample : MonoBehaviour{public struct TestEventA{public int Age;}private void Start(){TypeEventSystem.Global.Register<TestEventA>(e =>{Debug.Log(e.Age);}).UnRegisterWhenGameObjectDestroyed(gameObject);}private void Update(){// 鼠標左鍵點擊if (Input.GetMouseButtonDown(0)){TypeEventSystem.Global.Send(new TestEventA(){Age = 18});}// 鼠標右鍵點擊if (Input.GetMouseButtonDown(1)){TypeEventSystem.Global.Send<TestEventA>();}}}
}// 輸出結果
// 點擊鼠標左鍵,則輸出:
// 18
// 點擊鼠標右鍵,則輸出:
// 0

事件繼承支持

除了基本用法,TypeEventSystem 的事件還支持繼承關系。

using UnityEngine;namespace QFramework.Example
{public class TypeEventSystemInheritEventExample : MonoBehaviour{public interface IEventA{}public struct EventB : IEventA{}private void Start(){TypeEventSystem.Global.Register<IEventA>(e =>{Debug.Log(e.GetType().Name);}).UnRegisterWhenGameObjectDestroyed(gameObject);}private void Update(){if (Input.GetMouseButtonDown(0)){TypeEventSystem.Global.Send<IEventA>(new EventB());// 無效TypeEventSystem.Global.Send<EventB>();}}}
}// 輸出結果:
// 當按下鼠標左鍵時,輸出:
// EventB

手動注銷

using UnityEngine;namespace QFramework.Example
{public class TypeEventSystemUnRegisterExample : MonoBehaviour{public struct EventA{}private void Start(){TypeEventSystem.Global.Register<EventA>(OnEventA);}void OnEventA(EventA e){}private void OnDestroy(){TypeEventSystem.Global.UnRegister<EventA>(OnEventA);}}
}

接口事件

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;namespace QFramework.Example
{public struct InterfaceEventA{}public struct InterfaceEventB{}public class InterfaceEventModeExample : MonoBehaviour, IOnEvent<InterfaceEventA>, IOnEvent<InterfaceEventB>{public void OnEvent(InterfaceEventA e){Debug.Log(e.GetType().Name);}public void OnEvent(InterfaceEventB e){Debug.Log(e.GetType().Name);}private void Start(){this.RegisterEvent<InterfaceEventA>().UnRegisterWhenGameObjectDestroyed(gameObject);this.RegisterEvent<InterfaceEventB>();}private void OnDestroy(){this.UnRegisterEvent<InterfaceEventB>();}private void Update(){if (Input.GetMouseButtonDown(0)){TypeEventSystem.Global.Send<InterfaceEventA>();TypeEventSystem.Global.Send<InterfaceEventB>();}}}
}// 輸出結果
// 當按下鼠標左鍵時,輸出:
// InterfaceEventA
// InterfaceEventB

同樣接口事件也支持事件之間的繼承。

非 MonoBehavior 腳本如何自動銷毀

public class NoneMonoScript : IUnRegisterList
{public List<IUnRegister> UnregisterList { get; } = new List<IUnRegister>();void Start(){TypeEventSystem.Global.Register<EasyEventExample.EventA>(a =>{}).AddToUnregisterList(this);}void OnDestroy(){this.UnRegisterAll();}
}

如果想手動注銷,必須要創建一個用于接收事件的方法。

而用自動注銷則直接用委托即可。

這兩個各有優劣,按需使用。

另外,事件的定義最好使用 struct,因為 struct 的 gc 更少,可以獲得更好的性能。

EasyEvent

TypeEventSystem 是基于 EasyEvent 實現的。

EasyEvent 也是一個可以脫離架構使用的工具。

基本用法:

using UnityEngine;namespace QFramework.Example
{public class EasyEventExample : MonoBehaviour{private EasyEvent mOnMouseLeftClickEvent = new EasyEvent();private EasyEvent<int> mOnValueChanged = new EasyEvent<int>();public class EventA : EasyEvent<int,int> { }private EventA mEventA = new EventA();private void Start(){mOnMouseLeftClickEvent.Register(() =>{Debug.Log("鼠標左鍵點擊");}).UnRegisterWhenGameObjectDestroyed(gameObject);mOnValueChanged.Register(value =>{Debug.Log($"值變更:{value}");}).UnRegisterWhenGameObjectDestroyed(gameObject);mEventA.Register((a, b) =>{Debug.Log($"自定義事件:{a} {b}");}).UnRegisterWhenGameObjectDestroyed(gameObject);}private void Update(){if (Input.GetMouseButtonDown(0)){mOnMouseLeftClickEvent.Trigger();}if (Input.GetMouseButtonDown(1)){mOnValueChanged.Trigger(10);}// 鼠標中鍵if (Input.GetMouseButtonDown(2)){mEventA.Trigger(1,2);}}}
}// 輸出結果:
// 按鼠標左鍵時,輸出:
// 鼠標左鍵點擊
// 按鼠標右鍵時,輸出:
// 值變更:10
// 按鼠標中鍵時,輸出:
// 自定義事件:1 2

EasyEvent 最多支持三個泛型。?

EasyEvent 的優勢

EasyEvent 是 C# 委托和事件的替代。

EasyEvent 相比 C# 委托和事件,優勢是可以自動注銷。

相比 TypeEventSystem,優勢是更輕量,大多數情況下不用聲明事件類,而且性能更好(接近 C# 委托)。

缺點則是其攜帶的參數沒有名字,需要自己定義名字。

在設計一些通用系統的時候,EasyEvent 會派上用場,比如背包系統、對話系統,TypeEventSystem 是一個非常好的例子。

BindableProperty

BindableProperty 提供 數據 + 數據變更事件 的一個對象。

基本用法:

var age = new BindableProperty<int>(10);age.Register(newAge=>{Debug.Log(newAge)
}).UnRegisterWhenGameObjectDestoryed(gameObject);age++;
age--;// 輸出結果
// 11
// 10

就是當調用 age++ 和 age-- 的時候,就會觸發數據變更事件。

BindableProperty 除了提供 Register 這個 API 之外,還提供了 RegisterWithInitValue API,意思是 注冊時 先把當前值返回過來。

具體用法如下:

var age = new BindableProperty<int>(5);age.RegisterWithInitValue(newAge => {Debug.Log(newAge);});// 輸出結果
// 5

這個 API 就是,沒有任何變化的情況下,age 先返回一個當前的值,比較方便用于顯示初始界面。

使用 BindableProperty 優化 CounterApp 的代碼

using UnityEngine;
using UnityEngine.UI;namespace QFramework.Example
{// 1. 定義一個 Model 對象public class CounterAppModel : AbstractModel{public BindableProperty<int> Count { get; } = new BindableProperty<int>();protected override void OnInit(){var storage = this.GetUtility<Storage>();// 設置初始值(不觸發事件)Count.SetValueWithoutEvent(storage.LoadInt(nameof(Count)));// 當數據變更時 存儲數據Count.Register(newCount =>{storage.SaveInt(nameof(Count),newCount);});}}public class AchievementSystem : AbstractSystem {protected override void OnInit(){this.GetModel<CounterAppModel>() // -+.Count.Register(newCount =>{if (newCount == 10){Debug.Log("觸發 點擊達人 成就");}else if (newCount == 20){Debug.Log("觸發 點擊專家 成就");}else if (newCount == -10){Debug.Log("觸發 點擊菜鳥 成就");}});}}// 定義 utility 層public class Storage : IUtility{public void SaveInt(string key, int value){PlayerPrefs.SetInt(key,value);}public int LoadInt(string key, int defaultValue = 0){return PlayerPrefs.GetInt(key, defaultValue);}}// 2.定義一個架構(提供 MVC、分層、模塊管理等)public class CounterApp : Architecture<CounterApp>{protected override void Init(){// 注冊 System this.RegisterSystem(new AchievementSystem()); // +// 注冊 Modelthis.RegisterModel(new CounterAppModel());// 注冊存儲工具的對象this.RegisterUtility(new Storage());}}// 引入 Commandpublic class IncreaseCountCommand : AbstractCommand {protected override void OnExecute(){var model = this.GetModel<CounterAppModel>();model.Count.Value++; // -+}}public class DecreaseCountCommand : AbstractCommand{protected override void OnExecute(){this.GetModel<CounterAppModel>().Count.Value--; // -+}}// Controllerpublic class CounterAppController : MonoBehaviour , IController /* 3.實現 IController 接口 */{// Viewprivate Button mBtnAdd;private Button mBtnSub;private Text mCountText;// 4. Modelprivate CounterAppModel mModel;void Start(){// 5. 獲取模型mModel = this.GetModel<CounterAppModel>();// View 組件獲取mBtnAdd = transform.Find("BtnAdd").GetComponent<Button>();mBtnSub = transform.Find("BtnSub").GetComponent<Button>();mCountText = transform.Find("CountText").GetComponent<Text>();// 監聽輸入mBtnAdd.onClick.AddListener(() =>{// 交互邏輯this.SendCommand<IncreaseCountCommand>();});mBtnSub.onClick.AddListener(() =>{// 交互邏輯this.SendCommand(new DecreaseCountCommand(/* 這里可以傳參(如果有) */));});// 表現邏輯mModel.Count.RegisterWithInitValue(newCount => // -+{UpdateView();}).UnRegisterWhenGameObjectDestroyed(gameObject);}void UpdateView(){mCountText.text = mModel.Count.ToString();}// 3.public IArchitecture GetArchitecture(){return CounterApp.Interface;}private void OnDestroy(){// 8. 將 Model 設置為空mModel = null;}}
}
  • Model 中的 Count 和 mCount 改成了一個叫做 Count 的 BindableProperty
  • 刪掉了 CountChangeEvent 改用監聽 BindableProperty
  • Controller 在初始化中去掉一次 UpdateView 的主動調用

由于 Count 數據是單個數據 + 事件變更的形式,所以用 BindableProperty 非常合適,可以少寫很多代碼。

一般情況下,像主角的金幣、分數等數據非常適合用 BindableProperty 的方式實現。

IOCContainer

QFramework 架構的模塊注冊與獲取是通過 IOCContainer 實現的。

IOC 的意思是控制反轉,即控制反轉容器。

其技術的本質很簡單,本質就是一個字典,Key 是 Type,Value 是 Object,即:Dictionary<Type,object>。

QFramework 架構中的 IOCContainer 是一個非常簡易版本的控制翻轉容器,僅支持了注冊對象為單例的模式。

基本使用:

using System;
using UnityEngine;namespace QFramework.Example
{public class IOCContainerExample : MonoBehaviour{public class SomeService{public void Say(){Debug.Log("SomeService Say Hi");}}public interface INetworkService{void Connect();}public class NetworkService : INetworkService{public void Connect(){Debug.Log("NetworkService Connect Succeed");}}private void Start(){var container = new IOCContainer();container.Register(new SomeService());container.Register<INetworkService>(new NetworkService());container.Get<SomeService>().Say();container.Get<INetworkService>().Connect();}}
}// 輸出結果:
// SomeService Say Hi
// NetworkService Connect Succeed

使用 IOCContainer 更容易設計出符合依賴倒置原則的模塊。

而 QFramework 架構的用接口設計模塊的支持就是通過 IOCContainer 支持的,同樣使用 IOCContainer 也更容易設計出分層的架構。

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

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

相關文章

Vue3.5 企業級管理系統實戰(二十二):動態菜單

在前幾篇內容中已完成菜單、角色及菜單權限等相關開發&#xff0c;若要在左側菜單根據用戶角色動態展示菜單&#xff0c;需對 Sidebar 中的相關數據進行修改。鑒于其他相關方法及類型已在前文實現&#xff0c;本文不再重復闡述。 1 修改 Sidebar 組件 在 src/layout/componen…

014校園管理系統技術解析:構建智慧校園管理平臺

校園管理系統技術解析&#xff1a;構建智慧校園管理平臺 在教育信息化快速發展的當下&#xff0c;校園管理系統成為提升學校管理效率、優化校園服務的重要工具。該系統集成院校管理、投票管理等多個核心模塊&#xff0c;面向管理員、用戶和院內管理員三種角色&#xff0c;通過…

創新農業社會化服務 中和農信服務小農戶的探索實踐

在實現鄉村振興的道路上&#xff0c;如何讓現代農業發展成果惠及廣大小農戶&#xff0c;是一個重要課題。作為國內領先的綜合助農機構&#xff0c;中和農信多年來深耕農村市場&#xff0c;在服務小農戶方面進行了諸多創新探索&#xff0c;走出了一條具有示范意義的農業社會化服…

6.3 day 35

知識點回顧&#xff1a; 三種不同的模型可視化方法&#xff1a;推薦torchinfo打印summary權重分布可視化進度條功能&#xff1a;手動和自動寫法&#xff0c;讓打印結果更加美觀推理的寫法&#xff1a;評估模式 可視化 理解深度學習網絡最重要的2點&#xff1a; 1.了解損失如何定…

【如何在IntelliJ IDEA中新建Spring Boot項目(基于JDK 21 + Maven)】

AA. 我的開發環境配置與核心工具鏈解析 一、開發環境全覽 C:\Users\Again>java -version java version "21.0.1" 2023-10-17 LTS Java(TM) SE Runtime Environment (build 21.0.112-LTS-29) Java HotSpot(TM) 64-Bit Server VM (build 21.0.112-LTS-29, mixed m…

【C++高級主題】多重繼承下的類作用域

目錄 一、類作用域與名字查找規則&#xff1a;理解二義性的根源 1.1 類作用域的基本概念 1.2 單繼承的名字查找流程 1.3 多重繼承的名字查找特殊性 1.4 關鍵規則&#xff1a;“最近” 作用域優先&#xff0c;但多重繼承無 “最近” 二、多重繼承二義性的典型類型與代碼示…

登錄vmware vcenter報vSphere Client service has stopped working錯誤

一、問題 登錄vmware vcenter時發現報vSphere Client service has stopped working錯誤&#xff0c;導致vcenter控制臺進不去 二、解決辦法 打開vmware vcenter管理https://vcenterIP:5480&#xff0c;選擇VMware vSphere Client&#xff0c;重啟該服務后恢復正常。

MySQL關系型數據庫學習

學習參考鏈接&#xff1a;https://www.runoob.com/mysql/mysql-tutorial.html Windows 安裝MYSQL服務端的步驟&#xff1a;https://www.runoob.com/w3cnote/windows10-mysql-installer.html 1. 概念學習 MySQL 是一種關聯數據庫管理系統&#xff0c;關聯數據庫將數據保存在不…

web攻防之SSTI 注入漏洞

知識簡介 &#xff1a; 模版引擎和框架的區別 ssti的中文翻譯 &#xff1a; 服務端的模版的注入 模版引擎 &#xff1a;前端的用于裝飾優化html的模版 最簡單的就是在騰訊會議中的聊天功能 框架 &#xff1a; 這個是一套獨立存在的邏輯 如TP他是一個區別于php語法的后端邏輯…

【清晰教程】利用Git工具將本地項目push上傳至GitHub倉庫中

Git 是一個分布式版本控制系統&#xff0c;由 Linus Torvalds 創建&#xff0c;用于有效、高速地處理從小到大的項目版本管理。GitHub 是一個基于 Git 的代碼托管平臺&#xff0c;提供了額外的協作和社交功能&#xff0c;使項目管理更加高效。它們為項目代碼管理、團隊協作和持…

極簡以太彩光網絡解決方案4.0正式發布,“彩光”重構園區網絡極簡之道

5月28日下午,銳捷網絡在京舉辦以“光,本該如此‘簡單’”為主題的發布會,正式發布極簡以太彩光網絡解決方案4.0。作為“彩光”方案的全新進化版本,極簡以太彩光4.0從用戶需求出發,聚焦場景洞察,開啟了一場從底層基因出發的極簡革命,通過架構、部署、運維等多維度的創新升級,以強…

Selenium 中 JavaScript 點擊的優勢及使用場景

*在 Selenium 自動化測試中&#xff0c;使用 JavaScript 執行點擊操作&#xff08;如driver.execute_script("arguments[0].click();", element)&#xff09;相比直接調用element.click()有以下幾個主要優勢&#xff1a; 1. 繞過元素不可點擊的限制 問題場景&#x…

CppCon 2014 學習:Cross platform GUID association with types

類型的 GUID&#xff08;全局唯一標識符&#xff09; 是在 COM 編程&#xff08;Component Object Model&#xff09; 和某些大型 C 架構&#xff08;如 Office、DirectX、跨 DLL 接口&#xff09;中關聯類型信息和實現運行時類型識別與動態接口查詢的重要機制。 下面我們分層解…

Android 11以上App主動連接WIFI的完整方案

早期Android版本App內連接指定的WIFI還是比較簡單的&#xff0c;但是隨著Android版本的提升&#xff0c;限制也越來越多。以下是一套完整的Android 11以上的WIFI應用內主動連接方案。 第一步&#xff1a;添加到建議連接&#xff1a; val wifiManager getSystemService(WIFI_…

讓AI彈琴作曲不再是夢:Python+深度學習玩轉自動化音樂創作

讓AI彈琴作曲不再是夢:Python+深度學習玩轉自動化音樂創作 一、AI也能譜出動人的旋律?真不是科幻! 還記得小時候學鋼琴時老師的那句經典:“感覺不到情緒的樂句,是沒靈魂的。” 當時我一邊練琴一邊想:要是有個機器能幫我寫譜、調性又不跑調就好了! 結果幾年后,真被我碰…

機器學習:集成學習概念、分類、隨機森林

本文目錄&#xff1a; 一、集成學習概念**核心思想&#xff1a;** 二、集成學習分類&#xff08;一&#xff09;Bagging集成&#xff08;二&#xff09;Boosting集成(三&#xff09;兩種集成方法對比 三、隨機森林 一、集成學習概念 集成學習是一種通過結合多個基學習器&#…

YOLO機械臂丨使用unity搭建仿真環境,YOLO算法識別,Moveit2控制

文章目錄 前言搭建開發環境在window中安裝Unity創建Docker容器&#xff0c;并安裝相關軟件運行測試改進添加刪除節點前的函數調用 報錯?框選節點的時候報錯?如果無法控制機械臂&#xff0c;查看rviz2的終端&#xff0c;應該會有?規劃路徑超出范圍 參考 前言 本項目介紹通過…

Docker 插件生態:從網絡插件到存儲插件的擴展能力解析

Docker 容器技術以其輕量、快速、可移植的特性,迅速成為構建和部署現代應用的核心工具。然而,盡管 Docker Engine 自身功能強大,但在面對多樣化的生產環境和復雜業務需求時,僅靠核心功能往往無法滿足所有場景。 例如,跨主機的容器網絡通信、異構存儲系統的持久化數據管理…

飛牛fnNAS使用群輝DSM系統

目錄 一、Virtual DSM簡介 二、在飛牛NAS中安裝 1、激活Docker 2、建立路徑 3、創建Compose項目 4、容器啟動 (1)構建容器 (2)容器啟動 5、查看日志 6、登錄DSM地址 7、安裝完成 8、安裝套件示例 9、遠程訪問 10、測試 (1)PC瀏覽器創建筆記 (2)手機創建…

關于FPGA軟核的仿真(一)

MicroBlaze是Xilinx專為FPGA設計的軟核處理器&#xff0c;其本質是通過FPGA的可編程邏輯資源&#xff08;如查找表LUT、觸發器Flip-Flop&#xff09;動態構建的處理器架構&#xff0c;其本質為搭建處理器電路。MicroBlaze上運行嵌入式C代碼程序&#xff0c;通過CoreConnect總線…