Unity中的MVC框架

基本概念

MVC全名是Model View Controller
是模型(model)-視圖(view)-控制器(controller)的縮寫
是一種軟件設計規范,用一種業務邏輯、數據、界面顯示 分離的方法組織代碼
將業務邏輯聚集到一個部件里面,在改進和個性化定制界面及用戶交互的同時,不需要重新編寫業務邏輯。

MVC在游戲開發中不是必備的,它主要用于開發游戲UI系統邏輯

前期準備

接下來要實現一個小的UI面板,分別實現不使用MVC框架和使用MVC框架的代碼,以此作為對比。

Canvas設置

非MVC框架實現

主面板邏輯

MainPanel.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class MainPanel : MonoBehaviour
{//1.獲得控件public Text txtName;public Text txtLev;public Text txtMoney;public Text txtGem;public Text txtPower;public Button btnRole;private static MainPanel panel;//2.添加事件//3.更新信息//4.動態顯隱//使用靜態方法,讓NormalMain能夠調用public static void ShowMe(){if(panel == null){//實例化面板對象GameObject res = Resources.Load<GameObject>("UI/MainPanel");GameObject obj = Instantiate(res);//設置父對象obj.transform.SetParent(GameObject.Find("Canvas").transform,false);panel = obj.GetComponent<MainPanel>();}//如果隱藏形式是setacive,則顯示也要setpanel.gameObject.SetActive(true);//顯示完面板 更新panel.UpdateInfo();}public static void HideMe(){if(panel != null){//一. 直接刪// Destroy(panel.gameObject);// panel = null;//二. 隱藏panel.gameObject.SetActive(false);}}// Start is called before the first frame update void Start(){//2.添加事件btnRole.onClick.AddListener(ClickBtnRole);}private void ClickBtnRole(){//打開角色面板的邏輯Debug.Log("按鈕點擊");}//3.更新信息public void UpdateInfo(){//獲取玩家數據 更新玩家信息//獲取玩家數據的方式 1.網絡請求  2.Json  3.xml  4.2進制  5.PlayerPrefs公共類//通過PlayerPrefs來獲取本地存儲的玩家信息 更新到界面上txtName.text = PlayerPrefs.GetString("PlayerName","阿喆不想學習");txtLev.text = "LV." + PlayerPrefs.GetInt("PlayerLev",1).ToString();txtMoney.text = PlayerPrefs.GetInt("PlayerMoney",999).ToString();txtGem.text = PlayerPrefs.GetInt("PlayerGem",888).ToString();txtPower.text = PlayerPrefs.GetInt("PlayerPower",10).ToString();}
}

NormalMain.cs

    void Update(){if(Input.GetKeyDown(KeyCode.M)){//顯示主面板MainPanel.ShowMe();}else if(Input.GetKeyDown(KeyCode.N)){//隱藏主面板MainPanel.HideMe();}}

角色面板邏輯

RolePanel.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class RolePanel : MonoBehaviour
{//1.獲得控件public Text txtLev;public Text txtHp;public Text txtAtk;public Text txtDef;public Text txtCrit;public Text txtMiss;public Text txtLuck;public Button btnClose;public Button btnLevUp;private static RolePanel panel;//2.添加事件//3.更新信息//4.動態顯隱// Start is called before the first frame update public static void ShowMe(){if(panel == null){//實例化面板對象GameObject res = Resources.Load<GameObject>("UI/RolePanel");GameObject obj = Instantiate(res);//設置父對象obj.transform.SetParent(GameObject.Find("Canvas").transform,false);panel = obj.GetComponent<RolePanel>();}//如果隱藏形式是setacive,則顯示也要setpanel.gameObject.SetActive(true);//顯示完面板 更新panel.UpdateInfo();}public static void HideMe(){if(panel != null){//一. 直接刪// Destroy(panel.gameObject);// panel = null;//二. 隱藏panel.gameObject.SetActive(false);}}void Start(){btnClose.onClick.AddListener(()=>{HideMe();});btnLevUp.onClick.AddListener(()=>{//升級就是數據更新//這里就是獲取本地數據int lev = PlayerPrefs.GetInt("PlayerLev",1);int hp= PlayerPrefs.GetInt("PlayerHp",100);; int def= PlayerPrefs.GetInt("PlayerDef",10);int atk= PlayerPrefs.GetInt("PlayerAtk",20);int crit= PlayerPrefs.GetInt("PlayerCrit",20);int miss= PlayerPrefs.GetInt("PlayerMiss",10);int luck= PlayerPrefs.GetInt("PlayerLuck",40);//然后根據升級規則去改變他lev += 1;hp += lev;atk += lev;def += lev;crit += lev;miss += lev;luck += lev;//存起來PlayerPrefs.SetInt("PlayerLev",lev);PlayerPrefs.SetInt("PlayerHp",hp);PlayerPrefs.SetInt("PlayerAtk",atk);PlayerPrefs.SetInt("PlayerDef",def);PlayerPrefs.SetInt("PlayerCrit",crit);PlayerPrefs.SetInt("PlayerMiss",miss);PlayerPrefs.SetInt("PlayerLuck",luck);//同步更新面板上的數據UpdateInfo();//更新主面板的數據MainPanel.Panel.UpdateInfo();});}//更新信息public void UpdateInfo(){txtLev.text = "LV." + PlayerPrefs.GetInt("PlayerLev",1).ToString();txtHp.text = PlayerPrefs.GetInt("PlayerHp",100).ToString();txtAtk.text = PlayerPrefs.GetInt("PlayerAtk",20).ToString();txtDef.text = PlayerPrefs.GetInt("PlayerDef",10).ToString();txtCrit.text = PlayerPrefs.GetInt("PlayerCrit",20).ToString();txtMiss.text = PlayerPrefs.GetInt("PlayerMiss",10).ToString();txtLuck.text = PlayerPrefs.GetInt("PlayerLuck",40).ToString();}
}

MainPanel更新

    public static MainPanel Panel{get{return panel;}} private void ClickBtnRole(){//打開角色面板的邏輯RolePanel.ShowMe();}

MVC框架實現

Model數據腳本

using System.Collections;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
using UnityEngine;
using UnityEngine.Events;/// <summary>
/// 作為一個唯一的數據模型
/// 一般情況下 要不自己是個單例模式對象 
/// 要么自己存在在一個單例模式中
/// </summary>
public class PlayerModel
{//數據內容private string playerName;//用屬性是為了能讓外部得到它但不能改變他public string PlayerName{get{return playerName;}}private int lev;public int Lev{get{return lev;}}private int money;public int Money{get{return money;}}private int gem;public int Gem{get{return gem;}}private int power;public int Power{get{return power;}}private int hp;public int Hp{get{return hp;}}private int atk;public int Atk{get{return atk;}}private int def;public int Def{get{return def;}}private int crit;public int Crit{get{return crit;}}private int miss;public int Miss{get{return miss;}}private int luck;public int Luck{get{return luck;}}//通知外部更新的事件//通過它來與外部建立聯系 而不是直接獲取外部的面板private event UnityAction<PlayerModel> updateEvent;//在外部第一次獲取這個數據 如何獲取//通過單例模式 來達到數據的唯一性 和數據獲取private static PlayerModel data = null;public static PlayerModel Data{get{if(data == null){data = new PlayerModel();data.Init();}return data;}}//數據相關的操作//   初始化public void Init(){playerName = PlayerPrefs.GetString("PlayerName","阿喆不想學習");lev = PlayerPrefs.GetInt("PlayerLev",1);money = PlayerPrefs.GetInt("PlayerMoney",9999);gem = PlayerPrefs.GetInt("PlayerGem",8888);power = PlayerPrefs.GetInt("PlayerPower",99);hp= PlayerPrefs.GetInt("PlayerHp",100);; def= PlayerPrefs.GetInt("PlayerDef",10);atk= PlayerPrefs.GetInt("PlayerAtk",20);crit= PlayerPrefs.GetInt("PlayerCrit",20);miss= PlayerPrefs.GetInt("PlayerMiss",10);luck= PlayerPrefs.GetInt("PlayerLuck",40);}//   更新  在這里是升級public void LevUp(){//升級 改變內容lev += 1;hp += lev;atk += lev;def += lev;crit += lev;miss += lev;luck += lev;//改變后保存SaveData();}//   保存public void SaveData(){//把這些數據內容 存儲到本地PlayerPrefs.SetString("PlayerName",playerName);PlayerPrefs.SetInt("PlayerLev",lev);PlayerPrefs.SetInt("PlayerMoney",money);PlayerPrefs.SetInt("PlayerGem",gem);PlayerPrefs.SetInt("PlayerPower",power);PlayerPrefs.SetInt("PlayerHp",hp);PlayerPrefs.SetInt("PlayerAtk",atk);PlayerPrefs.SetInt("PlayerDef",def);PlayerPrefs.SetInt("PlayerCrit",crit);PlayerPrefs.SetInt("PlayerMiss",miss);PlayerPrefs.SetInt("PlayerLuck",luck);UpdateInfo();}public void AddEventListener(UnityAction<PlayerModel> function){updateEvent += function;}public void RemoveEventListener(UnityAction<PlayerModel> function){updateEvent -= function;}//通知外面更新數據的方法private void UpdateInfo(){//找到對應的 使用數據的腳本 去更新數據if(updateEvent != null){updateEvent(this);}}
}

View界面腳本

MainView.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class MainView : MonoBehaviour
{//1.找控件public Text txtName;public Text txtLev;public Text txtMoney;public Text txtGem;public Text txtPower;public Button btnRole;public Button btnSkill;//2.提供面板更新的方法給外部public void UpdateInfo(PlayerModel data){txtName.text = data.PlayerName;txtLev.text = "LV." + data.Lev;txtMoney.text = data.Money.ToString();txtGem.text = data.Gem.ToString();txtPower.text = data.Power.ToString();}
}

RoleView.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class RoleView : MonoBehaviour
{//1.找控件public Text txtLev;public Text txtHp;public Text txtAtk;public Text txtDef;public Text txtCrit;public Text txtMiss;public Text txtLuck;public Button btnClose;public Button btnLevUp;//2.提供面板更新的相關方法給外部public void UpdateInfo(PlayerModel data){txtLev.text = "LV." + data.Lev;txtHp.text = data.Hp.ToString();txtAtk.text = data.Atk.ToString();txtDef.text = data.Def.ToString();txtCrit.text = data.Crit.ToString();txtMiss.text = data.Miss.ToString();txtLuck.text = data.Luck.ToString();}
}

Controller業務邏輯

MainController

using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEngine;/// <summary>
/// Controller要處理的東西 就是業務邏輯
/// </summary>
public class MainController : MonoBehaviour
{//能夠在Controller中得到界面才行private MainView mainView;//面板之間的交互都是通過Controller來實現,我們不想讓mainView也變成靜態被其它訪問,//因此可以設置個靜態的 Controller,因為Controller也要被外部訪問private static MainController controller = null;public static MainController Controller{get{return controller;}}//1.界面的顯隱public static void ShowMe(){if(controller == null){//實例化面板對象GameObject res = Resources.Load<GameObject>("UI/MainPanel");GameObject obj = Instantiate(res);//設置父對象obj.transform.SetParent(GameObject.Find("Canvas").transform,false);controller = obj.GetComponent<MainController>();}//如果隱藏形式是setacive,則顯示也要setcontroller.gameObject.SetActive(true);}public static void HideMe(){if(controller != null){controller.gameObject.SetActive(false);}}private void Start(){//獲取同樣掛載在一個對象上的 view腳本mainView = this.GetComponent<MainView>();//第一次更新mainView.UpdateInfo(PlayerModel.Data);//2.界面 事件的監聽 來處理對應的業務邏輯mainView.btnRole.onClick.AddListener(ClickRoleBtn);//PlayerModel.Data.AddEventListener(mainView.UpdateInfo);PlayerModel.Data.AddEventListener(UpdateInfo);}private void ClickRoleBtn(){RoleController.ShowMe();}//3. 界面的更新private void UpdateInfo(PlayerModel data){if(mainView != null){mainView.UpdateInfo(data);}    }private void OnDestroy() {PlayerModel.Data.RemoveEventListener(UpdateInfo);}
}

RoleController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Timeline;public class RoleController : MonoBehaviour
{private RoleView roleView;private  static RoleController controller = null;public static RoleController Controller{get{return controller;}}public static void ShowMe(){if(controller == null){//實例化面板對象GameObject res = Resources.Load<GameObject>("UI/RolePanel");GameObject obj = Instantiate(res);//設置父對象obj.transform.SetParent(GameObject.Find("Canvas").transform,false);controller = obj.GetComponent<RoleController>();}//如果隱藏形式是setacive,則顯示也要setcontroller.gameObject.SetActive(true);}public static void HideMe(){if(controller != null){controller.gameObject.SetActive(false);}}void Start(){roleView = this.GetComponent<RoleView>();//第一次更新面板roleView.UpdateInfo(PlayerModel.Data);roleView.btnClose.onClick.AddListener(ClickCloseBtn);roleView.btnLevUp.onClick.AddListener(ClickLevUpBtn);//PlayerModel.Data.AddEventListener(roleView.UpdateInfo);PlayerModel.Data.AddEventListener(UpdateInfo);}private void ClickCloseBtn(){HideMe();}private void ClickLevUpBtn(){//通過數據模塊 進行升級 達到數據改變PlayerModel.Data.LevUp();}// Update is called once per frameprivate void UpdateInfo(PlayerModel data){if(roleView != null){roleView.UpdateInfo(data);}    }private void OnDestroy() {PlayerModel.Data.RemoveEventListener(UpdateInfo);}
}

對比與總結

好處

1.各司其職,互不干涉 --編程思路更清晰
2.有利開發中的分工?-- 多人協同開發時,同步并行
3.有利于組件重用??-- 項目換皮時,功能變化小時,提高開發效率

壞處

1.增加了程序文件的體量? -- 腳本由一變三
2.增加了結構的復雜性? --對于不清楚MVC原理的人不友好
3.效率相對較低? ? ?-- 對象之間的相互跳轉,始終伴隨著一定開銷

擴展

MVC的美中不足

M和V之間存在著聯系,也就是數據和界面之間存在著耦合性,當數據結構改變時會牽扯界面邏輯隨之改動。
在MVC中當需求變化時,需要維護的對象數量會增加

如這一段

改變了PlayerModel中的變量的話,則MainView里的這個函數可能也要調整。

MVX

數據和界面是必備的內容
我們可以通過改變X元素來優化原本的MVC
也就是改變聯系和處理M(數據)和V(界面)的方式

MVP:切斷View和Model的耦合,讓Presenter處理一切
MVVM:MVP的升級版,讓ViewModel和V進行雙向數據綁定,更新VM等同于更新V,反之同理
MVE:用EventCenter事件中心來分發消息

學習MVX的目的
不要拘泥于框架結構和設計模式要找到一個適合自己項目
一個穩定的,有序的,能滿足項目需求的實現方式

MVP

全稱為模型(Model)-視圖(View)一主持人(Presenter)

Model提供數據,View負責界面,Presenter負責邏輯的處理

它是MVC的一種變式,是針對MVC中M和V存在耦合的優化

與MVC的區別:

在MVC中View會直接從Model中讀取數據而不是通過 Controller
而在MVP中View并不直接使用Model,它們之間的通信是通過Presenter來進行的,所有的交互都發生在Presenter內部。

同樣是上面的項目

MVP_MainView.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class MVP_MainView : MonoBehaviour
{//1.找控件public Text txtName;public Text txtLev;public Text txtMoney;public Text txtGem;public Text txtPower;public Button btnRole;public Button btnSkill;// //2.提供面板更新的方法給外部// public void UpdateInfo(string name, int lev, int money, int gem, int power){//     txtName.text = name;//     txtLev.text = "LV." + lev;//     txtMoney.text = money.ToString();//     txtGem.text = gem.ToString();//     txtPower.text = power.ToString();// }
}

MVP_RoleView.cs

public class MVP_RoleView : MonoBehaviour
{//1.找控件public Text txtLev;public Text txtHp;public Text txtAtk;public Text txtDef;public Text txtCrit;public Text txtMiss;public Text txtLuck;public Button btnClose;public Button btnLevUp;//2.提供面板更新的相關方法給外部//方法可選 到時候可以直接在P里面通過訪問控件 去修改}

MainPresenter.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class MainPresenter : MonoBehaviour
{//能夠在Presenter中得到界面才行private MVP_MainView mainView;//面板之間的交互都是通過Controller來實現,我們不想讓mainView也變成靜態被其它訪問,//因此可以設置個靜態的 Controller,因為Controller也要被外部訪問private static MainPresenter presenter = null;public static MainPresenter Presenter{get{return presenter;}}//1.界面的顯隱public static void ShowMe(){if(presenter == null){//實例化面板對象GameObject res = Resources.Load<GameObject>("UI/MainPanel");GameObject obj = Instantiate(res);//設置父對象obj.transform.SetParent(GameObject.Find("Canvas").transform,false);presenter = obj.GetComponent<MainPresenter>();}//如果隱藏形式是setacive,則顯示也要setpresenter.gameObject.SetActive(true);}public static void HideMe(){if(presenter != null){presenter.gameObject.SetActive(false);}}private void Start(){//獲取同樣掛載在一個對象上的 view腳本mainView = this.GetComponent<MVP_MainView>();//第一次更新//mainView.UpdateInfo(PlayerModel.Data);//通過P自己的更新方法來更新UpdateInfo(PlayerModel.Data);//2.界面 事件的監聽 來處理對應的業務邏輯mainView.btnRole.onClick.AddListener(ClickRoleBtn);//PlayerModel.Data.AddEventListener(mainView.UpdateInfo);PlayerModel.Data.AddEventListener(UpdateInfo);}private void ClickRoleBtn(){//RoleController.ShowMe();RolePresenter.ShowMe();}//3. 界面的更新private void UpdateInfo(PlayerModel data){if(mainView != null){//mainView.UpdateInfo(data);//以前是把數據M傳到V中去更新,現在全部由P來做mainView.txtName.text = data.PlayerName;mainView.txtLev.text = "LV." + data.Lev;mainView.txtMoney.text = data.Money.ToString();mainView.txtGem.text = data.Gem.ToString();mainView.txtPower.text = data.Power.ToString();}    }private void OnDestroy() {PlayerModel.Data.RemoveEventListener(UpdateInfo);}
}

RolePresenter.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class RolePresenter : MonoBehaviour
{private MVP_RoleView roleView;private  static RolePresenter presenter = null;public static RolePresenter Presenter{get{return presenter;}}public static void ShowMe(){if(presenter == null){//實例化面板對象GameObject res = Resources.Load<GameObject>("UI/RolePanel");GameObject obj = Instantiate(res);//設置父對象obj.transform.SetParent(GameObject.Find("Canvas").transform,false);presenter = obj.GetComponent<RolePresenter> ();}//如果隱藏形式是setacive,則顯示也要setpresenter.gameObject.SetActive(true);}public static void HideMe(){if(presenter != null){presenter.gameObject.SetActive(false);}}void Start(){roleView = this.GetComponent<MVP_RoleView>();//第一次更新面板UpdateInfo(PlayerModel.Data);roleView.btnClose.onClick.AddListener(ClickCloseBtn);roleView.btnLevUp.onClick.AddListener(ClickLevUpBtn);//PlayerModel.Data.AddEventListener(roleView.UpdateInfo);PlayerModel.Data.AddEventListener(UpdateInfo);}private void ClickCloseBtn(){HideMe();}private void ClickLevUpBtn(){//通過數據模塊 進行升級 達到數據改變PlayerModel.Data.LevUp();}// Update is called once per frameprivate void UpdateInfo(PlayerModel data){if(roleView != null){//直接在p中得到V界面的控件,斷開M和V的聯系roleView.txtLev.text = "LV." + data.Lev;roleView.txtHp.text = data.Hp.ToString();roleView.txtAtk.text = data.Atk.ToString();roleView.txtDef.text = data.Def.ToString();roleView.txtCrit.text = data.Crit.ToString();roleView.txtMiss.text = data.Miss.ToString();roleView.txtLuck.text = data.Luck.ToString();}    }private void OnDestroy() {PlayerModel.Data.RemoveEventListener(UpdateInfo);}
}

MVP同樣由缺點,Presenter中的邏輯很多。

但是之后邏輯修改也只用在Presenter中。

MVVM

全稱為模型(Model)-視圖(View)-視圖模型(ViewModel)
Model提供數據,View負責界面,ViewModel負責邏輯的處理
MVVM的由來是MVP(Model-View-Presenter)模式與WPF結合應用時發展演變過來的一種新型框架

數據綁定

將一個用戶界面元素(控件)的屬性 綁定到 一個類型(對象)實例上的某個屬性的方法。

如果開發者有一個MainViewMode類型的實例,那么他就可以把MainViewMode的“Lev”屬性綁定到一個Ul中Text的“Text”屬性上。“綁定”了這2個屬性之后,對Text的Text屬性的更改“傳播”MainViewMode的Lev屬性,而對MainViewMode的Lev屬性的更改同樣會“傳播”到Text的Text屬性

MVVM在Unity中水土不服

因為View對象始終由我們來書寫,沒有UI配置文件(如WPF中的XAML),要想實現傳播需要事件/委托,很麻煩。

硬要在Unity中實現MVVM,需要寫三模塊,并且還要對V和VM進行數據綁定,工作量大,好處也不夠明顯

Unity的第三方MVVM框架
Loxodon Framework
https://github.com/vovgou/loxodon-framework
uMVVM
https://github.com/MEyes/uMVVM

MVE

全稱為模型(Model)-視圖(View)-事件中心(EventCenter)
Model提供數據,View負責界面,EventCenter負責數據傳遞

好處:

利用事件中心的觀察者模式
讓M和V層的之間的關系更加靈活多變
減少了目前數據層的負載
將數據層事件全部交由事件中心處理

總結

鐵打的M和V,流水的X
數據和界面是必備的內容
我們可以通過改變X元素來優化原本的MVC
也就是改變聯系和處理M(數據)和V(界面)的方式

不要拘泥于框架結構和設計模式要找到一個適合自己項目的
一個穩定的,有序的,能滿足項目需求的實現方式

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

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

相關文章

【嵌入式硬件】DRV8874電機驅動

目錄 1 芯片介紹 1.1 特性簡介 1.2 引腳配置 1.3 最佳運行條件 2 詳細說明 2.1 PMODE配置控制模式 2.1.1 PH/EN 控制模式 2.1.2 PWM 控制模式 2.1.3 獨立半橋控制模式 2.2 電流感測和調節 2.2.1 IPROPI電流感測 2.2.2 IMODE電流調節 3.應用 3.1設計要求 3.2 設計…

AI換臉FaceFusion一鍵云部署指南

大家好&#xff0c;從我開始分享到現在&#xff0c;收到很多朋友的反饋說配置很低玩不了AI。本篇是一個云端部署AI項目的指南&#xff0c;幫助大家在云端進行AI項目的部署。我會從云平臺的選擇、代碼部署、保存鏡像幾個方面進行詳細的介紹。沒有代碼基礎的小白也不用擔心&#…

exe4j innosetup

exe4j:jdk: 打包時&#xff1a;需要的文件最好放到單獨的一個文件夾下&#xff0c;主機安裝32位jdk,exe4j用32位的。 附帶jre: jre用32位的&#xff08;jdk下的jre&#xff09;可使用X86,X64.用相對路徑。 只打64位時&#xff0c;需要選擇32-bit or 64-bit (generate 64…

樂觀鎖 or 悲觀鎖 你怎么選?

你有沒有聽過這樣一句話&#xff1a;悲觀者正確&#xff0c;樂觀者成功?。那么今天我來分享下什么是樂觀鎖?和悲觀鎖。 樂觀鎖和悲觀鎖有什么區別&#xff0c;它們什么場景會用 樂觀鎖 樂觀鎖基于這樣的假設&#xff1a;多個事務在同一時間對同一數據對象進行操作的可能性很…

fps游戲中如何將矩陣轉換為二維屏幕上的矩形坐標

fps游戲中如何將矩陣轉換為二維屏幕上的矩形坐標 matrix[4][4]: 4x4 矩陣&#xff0c;通常用于3D變換&#xff08;如模型視圖投影矩陣&#xff09;。 float* location: 一個指向位置坐標的指針&#xff0c;表示要轉換的3D位置。 int Window_w, int Window_h: 窗口的寬度和高…

工廠模式詳情

一.介紹工廠模式的用途與特點 工廠方法模式是一種創建型設計模式&#xff0c; 其在父類中提供一個創建對象的方法&#xff0c; 允許子類決定實例化對象的類型。定義工廠方法模式(Fatory Method Pattern)是指定義一個創建對象的接口&#xff0c;但讓實現這個接口的類來決定實例…

Python導出Jira列表

import requests import urllib3 urllib3.disable_warnings() from jira import JIRA import pandas as pd def login_jira(username,password):jira JIRA("https://jira.cn/",basic_auth(username,password))#projectsjira.project(id13)# jqlproject"云鏈-…

基于強化學習的控制率參數自主尋優

1.介紹 針對控制建模與設計場景中控制參數難以確定的普遍問題&#xff0c;提出了一種基于強化學習的控制律參數自主優化解決方案。該方案以客戶設計的控制律模型為基礎&#xff0c;根據自定義的控制性能指標&#xff0c;自主搜索并確定最優的、可狀態依賴的控制參數組合。 可…

unity打包的WebGL部署到IIS問題

部署之后會出錯&#xff0c;我遇到的有以下幾種&#xff1b; 進度條卡住不動 明明已經部署到了IIS上&#xff0c;為什么瀏覽網頁的時候還是過不去或者直接報錯。 進度條卡住不動的問題其實就是wasm和data的錯誤。 此時在瀏覽器上按F12進入開發者模式查看錯誤&#xff08;下圖…

前端知識點雜記

本文章用于記錄前端學習中遇到的瑣碎問題及解決方法&#xff0c;歡迎大家一起學習補充~ 前端如何獲取UUID發送至后端&#xff1f; 1. 命令行下載uuid庫 npm install uuid 2. 工程導入uuid庫 import { v4 as uuidv4 } from "uuid"; 3. 使用方法生成uuid實例 co…

付費工具邏輯

1.付費推廣目的&#xff1a; 傳播信息心理暗示&#xff1b;擴大銷售&#xff0c;指導消費&#xff1b;樹立形象&#xff0c;塑道品牌&#xff1b; 2.付費和免費廣告&#xff1a; 付費主要為了增加曝光&#xff1b;免費廣告一般比付費廣告轉化率高&#xff1b; 3.平臺廣告交…

《Kubernetes部署篇:基于麒麟V10+ARM64架構部署harbor v2.4.0鏡像倉庫》

總結&#xff1a;整理不易&#xff0c;如果對你有幫助&#xff0c;可否點贊關注一下&#xff1f; 更多詳細內容請參考&#xff1a;企業級K8s集群運維實戰 一、環境信息 K8S版本 操作系統 CPU架構 服務版本 1.26.15 Kylin Linux Advanced Server V10 ARM64 harbor v2.4.0 二、部…

chrome谷歌瀏覽器開啟Gemini Nano模型

前提 確保您的操作系統語言設置為英語(美國) 可能還需要將 Chrome 瀏覽器的語言更改為英語(美國)。 下載dev或Canary版本Chrome Chrome Canary Chrome Dev 注意:確認您的版本高于 127.0.6512.0。 其中一個Chrome版本不行就切換另外一個版本 繞過性能檢查 Tab輸入: …

中國美業元宇宙-探索美容行業的未來

隨著科技的不斷進步和數字化轉型的深入&#xff0c;元宇宙作為一種全新的虛擬現實交互平臺&#xff0c;正逐漸成為推動多個行業革新的重要力量。在這種背景下&#xff0c;中國美業也在積極擁抱元宇宙&#xff0c;希望通過這一新興技術為傳統美容行業帶來創新與發展。 #### 中國…

結構體相關習題的補充

結構體相關習題的補充 題目1&#xff1a; 如有以下代碼&#xff1a; struct student {int num;char name[32];float score; }stu;則下面的敘述不正確的是&#xff1a;( ) A.struct 是結構體類型的關鍵字 B.struct student 是用戶定義的結構體類型 C.num, score 都是結構體…

正邦科技(day4)

燒錄 一、燒錄固件二、 通訊模塊升級1&#xff1a;USB的方式升級固件2&#xff1a;通過mqtt的方式升級固件3&#xff1a;切換環境 三、 燒錄WiFi1&#xff1a;短接2&#xff1a;燒錄腳本 設備注意事項&#xff1a; 第一種方式&#xff1a;通信模組和MCU都可以統一燒錄BoodLoade…

Oracle Hint /*+APPEND*/插入性能總結

oracle append用法 Oracle中的APPEND用法主要用于提高數據插入的效率。 基本用法&#xff1a;在使用了APPEND選項后&#xff0c;插入數據會直接加到表的最后面&#xff0c;而不會在表的空閑塊中插入數據。這種做法不需要尋找freelist中的free block&#xff0c;從而避免了在…

【計算機畢設】基于Spring Boot的課程作業管理系統 - 源碼免費(私信領取)

免費領取源碼 &#xff5c; 項目完整可運行 &#xff5c; v&#xff1a;chengn7890 誠招源碼校園代理&#xff01; 1. 研究目的 課程作業管理系統旨在為教師和學生提供一個便捷的平臺&#xff0c;用于發布、提交和評定課程作業。本系統旨在提高作業管理的效率&#xff0c;促進教…

Golang反射

文章目錄 基本介紹reflect包reflect.Typereflect.Valuereflect.Kind具體類型、空接口與reflect.Value的相互轉換 反射應用場景修改變量的值訪問結構體的字段信息調用變量所綁定的方法實現函數適配器創建任意類型的變量 基本介紹 基本介紹 在Go中&#xff0c;反射&#xff08;re…

錯誤 0x80070570:文件或目錄損壞且無法讀取/無法訪問[拒絕訪問]-解決方法

1.起因&#xff1a;在挪動&#xff35;盤文件時&#xff0c;出現無法移動的報錯提示&#xff1a; and無法訪問[拒絕訪問]: 2.原因&#xff3b;大多是胡亂拔出&#xff35;盤&#xff3d; &#xff3b;來自0x80070570 文件或目錄損壞且無法讀取 CHKDSK 修復方法-CSDN博客&#…