Unity引擎使用HybridCLR(華佗)熱更新

??大家好,我是阿趙。
??阿趙我做手機游戲已經有十幾年時間了。記得剛開始從做頁游的公司轉到去做手游的公司,在面試的時候很重要的一個點,就是會不會用Lua。使用Lua的原因很簡單,就是為了熱更新。
??熱更新游戲內容很重要。如果游戲內容需要改動,如果每次都要去平臺出新的安裝包提審,周期不可控,甚至像iOS這種提審特別麻煩的平臺,還有不過審的風險。比如出個春節活動,提審完可能春節都過完了,那么這個內容也就沒有意義。 但如果游戲的內容自己可以通過某些方法進行修改,不需要通過平臺的審核就能直接改動,那么游戲制作的靈活性就大大增加了。我們使用Unity引擎來開發游戲,游戲資源是可以通過AssetBundle的方式熱更新的,但C#代碼,以前是不能熱更新的,至少在iOS平臺是不能的,安卓和pc有辦法可以熱更新dll。
??Lua作為一個可以通過字符串或者字節資源的形式加載的腳本,在游戲熱更新上起到了重要的作用,起碼在近十年時間是出于統治地位的。但由于性能問題Lua也一直受到各種詬病,特別是在微信小游戲或者抖音小游戲上面,性能的確不是很好。
??最近和UWA溝通的過程中,聽說很多公司已經不用Lua,而改為用了HybridCLR(華佗)熱更新。華佗熱更新的原理是更新c#的程序集,也就是通過加載dll來實現代碼層面的熱更新,而且iOS也能用。不過由于公司的項目都是在用Lua開發的,如果換成華佗,等于整個項目要重新用C#寫一篇,成本還是很高的。
??學多一點東西肯定是有好處的,說不定以后新項目就能用得上。于是阿趙我也來學習一下華佗熱更新的用法,并且記錄一下一些使用的問題。

一、 安裝

HybridCLR華佗熱更新的在線文檔地址是:
文檔地址
??里面有比較詳細的安裝說明,可以根據步驟一步步來安裝。我只記錄一下我遇到的問題。

1、 對應的Unity版本

??在官方文檔里面說,華佗支持的Unity版本有這些:
在這里插入圖片描述

??由于我有一個項目是使用2019.4.24開發的,一看文檔說支持2019.4.x,感覺挺好,但繼續看下去,會看到:
在這里插入圖片描述

??從文檔看,2019只能在2019.4.40上面安裝。其實2019.4前面的版本的確挺多問題的,比如我之前發現的URP的SRPBatcher合并問題等,各位如果還在用2019版本開發的朋友,我也挺建議大家都升級到2019.4.40。無奈的是,如果項目已經上線了,再來換版本,可能會導致AssetBundle打包的資源會全部變更,Unity打包AssetBundle的時候會把版本號寫在文件開頭,所以就算你所有內容都沒變,只是換個Unity版本,打出來的AssetBundle文件也會全部改變的……
??不過幸好,華佗的文檔里面也有針對這種情況的處理辦法,就是先把項目切換到2019.4.40,然后安裝華佗,再切換會原來的版本。
在這里插入圖片描述

??拋開項目已有版本的問題,其實就無所謂了,因為之后的版本很多都支持, 比如直接安裝2022.3.x版本,就沒這個問題了。

2、下載代碼

??由于代碼庫是從git下載,所以必須安裝git。
??然后通過Unity的PackageManager里面的Add package from git URL來安裝
庫地址:
https://gitee.com/focus-creative-games/hybridclr_unity.git

https://github.com/focus-creative-games/hybridclr_unity.git

??我自己嘗試的結果是沒辦法通過Add package from git URL來安裝,安裝了GIT和加了環境變量PATH也不行。我自己用GIT手動克隆,卻是沒問題的,這一點很神奇。
??于是解決這個問題的方法是,可以手動把地址檢出克隆到本地,然后把文件夾改名com.code-philosophy.hybridclr,并復制到項目里面和Assets文件夾同級的Packages文件夾
在這里插入圖片描述

??復制后打開項目,會看到有華佗的菜單
在這里插入圖片描述

??選擇安裝器,然后安裝
在這里插入圖片描述

??按照文檔說明基本都可以自動安裝成功,但我還是失敗了,看報錯還是GIT的問題,于是我根據報錯,自己檢出克隆https://gitee.com/focus-creative-games/hybridclr到項目的HybridCLRData/hybridclr_repo文件夾
在這里插入圖片描述

??檢出后再次點擊Install按鈕,就可以安裝成功了。
在這里插入圖片描述

??HybridCLR菜單下出現了所有的選項子菜單。

二、 淺嘗華佗熱更新

1、 一些概念

??在使用華佗熱更新之前需要先了解一些概念

1. 程序集

??先來操作,最后再說為什么。在項目里面創建一個文件夾,叫做HotUpdate,或者叫其他都行,你自己喜歡:
在這里插入圖片描述

??然后在這個文件夾里面,創建一個Assembly Definition文件:
在這里插入圖片描述

??幫這個文件起個名字,比如我這里就叫做HotUpdate:
在這里插入圖片描述

??注意要把Auto Referenced的勾選去掉。
??然后在這個文件夾里面創建一個C#腳本,我這里隨便命名為Hello:
在這里插入圖片描述

??創建完之后,點選這個Hello腳本,會看到里面多了一個Assembly信息,里面說明了,這個Hello的腳本,是屬于HotUpdate.dll的。
在這里插入圖片描述

??操作到此結束,下面解釋一下:
??這個創建文件夾和Assembly Definition文件的過程,是Unity引擎的程序集功能,其實就是指定了某個文件夾作為一個程序集的范圍。只要在這個文件夾下面的所有文件,包括子文件夾里面的文件,都屬于當前這個Assembly Definition文件的程序集里面的內容。
??一個程序集,字面意思就是程序的集合了,可以理解成是把里面的代碼都打包了,之后需要熱更新代碼,其實就是熱更新這個程序集的dll文件了。

2. AOT程序集和熱更新程序集

??使用Unity引擎制作游戲,各位肯定應該都會寫C#。在項目里面所寫的C#代碼,就算我們不特意的打程序集,它們也會出現在一個程序集里面,就是Assembly-CSharp.dll,然后我們又可以根據自己的需要,創建一些程序集,所以最后打包的時候,除了Assembly-CSharp.dll,還會有一些自己的dll。這些多個程序集,之后會用于華佗熱更新。
??這里有個問題,熱更新是以dll為單位的,那些可以熱更新的程序集,在使用華佗熱更新的時候,是會剝離出去,不會包含在主工程包里面的。而我們需要寫代碼加載這些dll文件,就必須有一些代碼是包含在主工程里面不能熱更新的。
??所以在使用華佗熱更新的時候,需要把程序集分成2部分,第一部分是包含在游戲主包里面不能熱更新的,成為AOT程序集,第二部分是可以熱更新的dll,成為熱更新程序集。

3. 程序集的規劃和程序集之間的引用關系

??由于程序集起碼要有AOT和可熱更兩個,甚至更多,所以在做之前,我們必須先規劃一下它們之間的關系。具體來說,就是總共需要多少個程序集才能滿足我們需要,既能熱更,又可以劃分清楚模塊,做到分塊更新。
??程序集之間的引用,有2種方式,第一種,就是在程序集上面勾上Auto Referenced,這樣它自動被其他程序集引用,可以互相調用里面的方法。
??另外一種,就是在程序集上面指定依賴關系,比如我再建一個HotUpdate2的程序集,不勾選Auto Referenced:
在這里插入圖片描述

??這個時候如果HotUpdate程序集要訪問HotUpdate2程序集,可以選擇HotUpdate程序集,然后添加引用關系:
在這里插入圖片描述

??只要在HotUpdate程序集的Assembly Definition References里面添加了HotUpdate2的引用,那么HotUpdate就能調用HotUpdate2里面的方法了。
??華佗熱更新里面有一個規則,AOT程序集是不能直接引用熱更新程序集的,不然在打包的時候會出錯。所以,我們在創建自己的可熱更程序集的時候,必須把Auto Referenced的勾選去掉,然后自己維護可熱更新程序集之間的引用關系。

2、 嘗試使用華佗熱更新

1. 指定需要熱更新的程序集

??在HybridCLR菜單下面選擇Settings設置:
在這里插入圖片描述

??然后添加可熱更新的程序集:
在這里插入圖片描述

??在這里設置了的程序集,在打主包的時候,程序集是不會包含在主包里面的。

2. 生成必須的東西

在這里插入圖片描述

??在首次使用華佗熱更新的時候,必須先選擇Generate——All,生成所有必須的文件,其實就是All上面的哪些東西了。在之后的使用中,就不一定要生成All,可以根據實際需要來生成上面的內容。

3. 寫熱更新的測試代碼

??首先,為了打包之后看到控制臺的打印,先創建一個腳本:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ConsoleToScreen : MonoBehaviour
{const int maxLines = 50;const int maxLineLength = 120;private string _logStr = "";private readonly List<string> _lines = new List<string>();public int fontSize = 15;void OnEnable() { Application.logMessageReceived += Log; }void OnDisable() { Application.logMessageReceived -= Log; }public void Log(string logString, string stackTrace, LogType type){foreach (var line in logString.Split('\n')){if (line.Length <= maxLineLength){_lines.Add(line);continue;}var lineCount = line.Length / maxLineLength + 1;for (int i = 0; i < lineCount; i++){if ((i + 1) * maxLineLength <= line.Length){_lines.Add(line.Substring(i * maxLineLength, maxLineLength));}else{_lines.Add(line.Substring(i * maxLineLength, line.Length - i * maxLineLength));}}}if (_lines.Count > maxLines){_lines.RemoveRange(0, _lines.Count - maxLines);}_logStr = string.Join("\n", _lines);}void OnGUI(){GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity,new Vector3(Screen.width / 1200.0f, Screen.height / 800.0f, 1.0f));GUI.Label(new Rect(10, 10, 800, 370), _logStr, new GUIStyle() { fontSize = Math.Max(10, fontSize) });}
}

??在場景上面建個空物體,然后把腳本拖上去:
在這里插入圖片描述

??這樣做的目的只是為了讓我們在接下來的測試中,把控制臺打印輸出到屏幕,讓我們知道熱更新有沒有生效。
??然后給Hello腳本修改一下:

using UnityEngine;public class Hello 
{static public void Print(){Debug.Log("Hello World");}
}

??這里只有一個靜態方法,如果執行了,會打印Hello World到控制臺,通過上面的腳本,控制臺的打印就會出現在屏幕。
??最后,要加一個AOT腳本,作為游戲啟動、加載dll和調用dll。正常來說熱更新的dll文件應該放在CDN上,然后下載到本地。這里為了測試,就寫死放在StreamingAssets文件夾了。這里建一個叫做TestLoadDll的C#腳本:

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;public class TestLoadDll : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){Assembly dllLoader;
#if !UNITY_EDITORdllLoader = Assembly.Load(File.ReadAllBytes($"{Application.streamingAssetsPath}/HotUpdate.dll.bytes"));
#elsedllLoader = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate");        
#endifType type = dllLoader.GetType("Hello");type.GetMethod("Print").Invoke(null, null);}// Update is called once per framevoid Update(){}
}

??然后在場景里面建一個空物體,把腳本拖上去:
在這里插入圖片描述

??這時候在編輯器里面運行,會看到Hello World打印出來了:
在這里插入圖片描述

??腳本里面的內容很簡單,只是規定了在編輯器就直接讀取工程里面的HotUpdate程序集,在非編輯器的情況下,就讀取StreamingAssets文件夾下面的HotUpdate.dll.bytes文件。由于Unity的詭異規定,所以dll文件是不能直接讀取的,要把后綴改成bytes。
??然后后面的那段反射代碼

Type type = dllLoader.GetType("Hello");
type.GetMethod("Print").Invoke(null, null);

??不用害怕,這是因為AOT程序集不能直接引用可熱更新的HotUpdate程序集,所以才用反射調用一下,僅此而已,如果沒有特殊情況,是不需要這樣做的。

4. 打包熱更新用的dll

??在HybridCLR菜單選擇CompileDll——ActiveBuildTarget
在這里插入圖片描述

??這時候會把對dll進行打包,打包的結果在
??項目文件夾\HybridCLRData\HotUpdateDlls\對應的平臺文件夾\:
在這里插入圖片描述

??由于我現在的平臺是Windows,所以實際路徑會在StandaloneWindows64文件夾下。這里會看到了項目里面所用到的所有程序集的dll文件,其中就有我們想要熱更新的HotUpdate.dll。我們剛才也指定了HotUpdate2程序集,但由于里面一個腳本都沒有,所以是不會有dll打出來的。
在這里插入圖片描述

??把HotUpdate.dll復制到StreamingAssets文件夾并重命名為HotUpdate.dll.bytes

5. 打包測試

??選擇一個文件夾,常規的打個PC包出來:
在這里插入圖片描述

在這里插入圖片描述

??發現打不出來,因為剛才指定了HotUpdate2程序集,但現在這個程序集是沒有內容的
在這里插入圖片描述

??去華佗設置里面把HotUpdate2程序集從可熱更新的程序集里面去掉。這次就能正常打包了。
在這里插入圖片描述

??運行打出來的包,能看到HelloWorld,證明打包成功,從剛才的讀取dll的代碼我們可以知道,現在是讀取了StreamingAssets里面的HotUpdate.dll.bytes作為代碼執行的。

6. 驗證熱更新修改代碼

??回到Hello腳本,修改一下:

using UnityEngine;public class Hello 
{static public void Print(){Debug.Log("Hello Azhao");}
}

??把原來的Hello World改成Hello Azhao
??然后再次HybridCLR菜單選擇CompileDll——ActiveBuildTarget,打包dll
在這里插入圖片描述

??再次在HybridCLRData\HotUpdateDlls\StandaloneWindows64目錄找到HotUpdate.dll文件,然后拷貝到之前打的PC包的StreamingAssets文件夾:
在這里插入圖片描述

??這時候再次運行之前的PC包
在這里插入圖片描述

??可以看到,現在PC包顯示的內容已經變成了Hello Azhao。到此為止,華佗熱更新的基本流程已經跑通了。

三、 華佗熱更新的深入使用

1、 嘗試AssetBundle加載資源

??接下來,嘗試把C#腳本掛在GameObject上,并通過AssetBundle加載這個GameObject看看:
??在HotUpdate程序集建一個PrintObject的C#腳本:
在這里插入圖片描述

using UnityEngine;public class PrintObject : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){Debug.Log("GameObject:" + gameObject.name);}// Update is called once per framevoid Update(){}
}

??然后建一個cube,把腳本掛上去:
在這里插入圖片描述

??把這個Cube做成Prefab,并且設置AssetBundleName,打包AssetBundle:
在這里插入圖片描述

??然后打包AssetBundle,把AssetBundle文件放到StreamingAssets文件夾:
在這里插入圖片描述

??修改Hello腳本:

using UnityEngine;public class Hello 
{static public void Print(){string path = Application.streamingAssetsPath + "/ab/cube.unity3d";AssetBundle ab = AssetBundle.LoadFromFile(path);if(ab){Object obj = ab.LoadAsset("Cube");if(obj != null){GameObject.Instantiate(obj);}            }}
}

??生成dll,并且和AssetBundle一起拷貝到pc包的StreamingAssets文件夾:
在這里插入圖片描述

??這時候,運行PC包,并沒有出現我們想要的情況,而是有個報錯:
在這里插入圖片描述

??這是為什么呢?

2、關于代碼裁剪

??如果在打包的時候沒有用到某些Unity自帶的API,但后期在熱更新的代碼上加上,就會出現報錯,找不到方法。原因是IL2CPP的情況下,代碼裁剪是不能被禁止的,而之前沒有用過的API,在Unity打包的時候被裁剪掉了。
在這里插入圖片描述

??一般來說,為了防止需要的Unity原生API代碼被裁剪的問題,可以在項目里面建一個link.xml文件,然后把需要保留不被裁剪的內容填進去。不過這樣手動收集是很麻煩的,華佗的工具里面自帶了收集link.xml的功能
在這里插入圖片描述

??只要點一下,就會把項目里面有調用過的API加入到link.xml里面。
這里還有2個問題
1、 需要保留的代碼,除了加在link.xml之外,代碼還要必須顯式的引用過這些類或者函數,不然也還是會被裁剪。
2、 重新收集完link.xml之后,必須重新打包才能生效……
??這樣似乎就回到了使用Lua時的導出接口的操作了,沒有導出過接口的類和方法,不能熱更新……關鍵這一步你在編輯器內還很難發現,畢竟編輯器內的Unity自帶API是不會被裁剪的。
??這是一個我認為使用華佗熱更新最大的問題。畢竟Unity很多API可能在一開始的時候沒考慮到需要使用,后面用到才收集,就不能熱更新了。
??既然是需要重新出包了,所以也就不止是點一下LinkXml了,直接Generate——All,生成所有,那樣就穩妥了。
??全部重新生成之后,再次出包,就可以看到之前的報錯沒有了,可以加載AssetBundle里面的Cube,并且掛在上面的腳本也正常運行了:
在這里插入圖片描述

3、 新增程序集的熱更新

??之前的例子里面只有1個可熱更新的程序集,叫做HotUpdate,現在我想在不重新出包的情況下,增加一個HotUpdate2的可熱更新程序集,試試能不能熱更新。
??由于之前是在AOT代碼里面寫死了需要加載HotUpdate.dll.bytes,所以如果增加新的程序集dll文件,肯定是不能加載的,所以要改成需要加載哪些dll文件要通過可熱更的文件來決定。
??這里為了測試簡單,我放一個dll.txt文本在StreamingAssets文件夾,然后在里面用逗號分隔需要加載的程序集名字。由于HotUpdate.dll需要通過反射來調用,所以我就不寫在txt里面了,這也說明,如果需要有一個程序調用入口,那么至少有一個dll是需要寫在代碼里面加載的。于是加載dll的代碼會變成這樣:

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;public class TestLoadDll : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){Assembly dllLoader;
#if !UNITY_EDITORstring path = Application.streamingAssetsPath + "/dll.txt";string content = File.ReadAllText(path);if(string.IsNullOrEmpty(content)==false) {string[] fileNames = content.Trim().Split(",");for(int i = 0;i < fileNames.Length; i++) { string fileName = fileNames[i];if(string.IsNullOrEmpty(fileName)==false){Assembly.Load(File.ReadAllBytes($"{Application.streamingAssetsPath}/"+fileName+".dll.bytes"));}}}dllLoader = Assembly.Load(File.ReadAllBytes($"{Application.streamingAssetsPath}/HotUpdate.dll.bytes"));
#elsedllLoader = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate");        
#endifType type = dllLoader.GetType("Hello");type.GetMethod("Print").Invoke(null, null);}// Update is called once per framevoid Update(){}
}

??到現在為止,先打個PC包,作為熱更新的基礎包。
??接下來同樣的手法,建立HotUpdate2文件夾和程序集,在里面添加一個PrintGameObject的腳本:
在這里插入圖片描述

using UnityEngine;public class PrintGameObject : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){Debug.Log("HotUpdate2:" + gameObject.name);}// Update is called once per framevoid Update(){}static public void Run(){Debug.Log("This is HotUpdate2");}
}

??把這個PrintGameObject腳本掛在之前的Cube預設上,原來的PrintObject腳本就不掛了:
在這里插入圖片描述

??在HotUpdate程序集添加HotUpdate2程序集的引用:
在這里插入圖片描述

??修改Hello腳本:

using UnityEngine;public class Hello 
{static public void Print(){PrintGameObject.Run();string path = Application.streamingAssetsPath + "/ab/cube.unity3d";AssetBundle ab = AssetBundle.LoadFromFile(path);if(ab){Object obj = ab.LoadAsset("Cube");if(obj != null){GameObject.Instantiate(obj);}            }}
}

??主要是加了一句PrintGameObject.Run();
??接下來還是常規操作,把HotUpdate2加到可熱更新的列表
在這里插入圖片描述

??在dll.txt里面寫入HotUpdate2。然后打包AssetBundle、打包Dll,把這些東西都拷貝到PC包的StreamingAssets,然后運行,會看到:
在這里插入圖片描述

??發現一個神奇的事情,HotUpdate2的代碼其實已經加載了,PrintGameObject里面的Run方法都打印出來This is HotUpdate2了,但掛在Cube上的PrintGameObject腳本卻找不到……
??接下來改一下做法,把Cube上面的PrintGameObject腳本去掉,變成在實例化GameObject之后用AddComponent來添加腳本:

using UnityEngine;public class Hello 
{static public void Print(){PrintGameObject.Run();string path = Application.streamingAssetsPath + "/ab/cube.unity3d";AssetBundle ab = AssetBundle.LoadFromFile(path);if(ab){Object obj = ab.LoadAsset("Cube");if(obj != null){GameObject go = (GameObject)GameObject.Instantiate(obj);go.AddComponent<PrintGameObject>();}            }}
}

??再次打包AssetBundle,打包dll,拷貝到PC包的StreamingAssets文件夾,運行PC包:
在這里插入圖片描述

??會看到,添加成功了,PrintGameObject腳本也運行成功了。
??關于新增的程序集掛到GameObject的AssetBundle熱更的問題,我到最后都沒有解決,不知道是不是有解決辦法。我只能暫時得出結論,如果新增程序集,純代碼調用時沒問題的,但如果掛在GameObject上通過AssetBundle加載,就會有問題。
??這就導致一個問題,我們如果想出了安裝包之后可以長時間的熱更新,不需要重新出包,就必須對可能用到的程序集做好規劃,盡量不要去改變了。

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

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

相關文章

DeepSeek R1-7B 醫療大模型微調實戰全流程分析(全碼版)

DeepSeek R1-7B 醫療大模型微調實戰全流程指南 目錄 環境配置與硬件優化醫療數據工程微調策略詳解訓練監控與評估模型部署與安全持續優化與迭代多模態擴展倫理與合規體系故障排除與調試行業應用案例進階調優技巧版本管理與迭代法律風險規避成本控制方案文檔與知識傳承1. 環境配…

大白話html語義化標簽優勢與應用場景

大白話html語義化標簽優勢與應用場景 大白話解釋 語義化標簽就是那些名字能讓人一看就大概知道它是用來做什么的標簽。以前我們經常用<div>來做各種布局&#xff0c;但是<div>本身沒有什么實際的含義&#xff0c;就像一個沒有名字的盒子。而語義化標簽就像是有名…

軟件工程---構件

在軟件工程中&#xff0c;構件是一個獨立的、可復用的軟件單元&#xff0c;它具有明確的功能、接口和行為&#xff0c;并且可以在不同的環境中加以集成和復用。構件的概念是軟件架構和組件化開發的核心思想之一&#xff0c;其目的是促進軟件系統的模塊化、可維護性和可擴展性。…

MES機聯網4:文檔資料

目錄信息 MES機聯網1&#xff1a;技術方案MES機聯網2&#xff1a;采集網關MES機聯網3&#xff1a;管理后臺MES機聯網4&#xff1a;文檔資料 MQ接入文檔 1、建立連接 mqtt連接地址: 192.168.0.138 mqtt端口: 1883 mqtt用戶名&#xff1a;admin mqtt密碼&#xff1a;123456 …

“此電腦”中刪除WPS云盤方法(百度網盤通用)

&#x1f4e3;此方法適用于卸載WPS云盤后&#xff0c;WPS云盤圖標依然在此電腦中顯示的問題。 原理&#xff1a;通過注冊來進行刪除 步驟&#xff1a; WIN鍵R,打開運行窗口&#xff0c;輸入regedit命令&#xff0c;來打開【注冊表編輯器】&#xff1b; 從左側&#xff0c;依…

【 Vue3 提升:技術解析與實踐】

摘要 Vue.js 3.0 的發布為前端開發帶來了眾多性能提升、新特性和改進。本文將深入探討 Vue3 的提升之處&#xff0c;從性能優化、新特性解析、生態系統發展等多個方面進行解析&#xff0c;并通過實踐案例展示如何在項目中應用這些新特性。 一、認識 Vue3 1. Vue3 的發布背景…

279.完全平方數

279.完全平方數 力扣題目鏈接(opens new window) 給定正整數 n&#xff0c;找到若干個完全平方數&#xff08;比如 1, 4, 9, 16, ...&#xff09;使得它們的和等于 n。你需要讓組成和的完全平方數的個數最少。 給你一個整數 n &#xff0c;返回和為 n 的完全平方數的 最少數…

HTML-網頁介紹

一、網頁 1.什么是網頁&#xff1a; 網站是指在因特網上根據一定的規則&#xff0c;使用 HTML 等制作的用于展示特定內容相關的網頁集合。 網頁是網站中的一“頁”&#xff0c;通常是 HTML 格式的文件&#xff0c;它要通過瀏覽器來閱讀。 網頁是構成網站的基本元素&#xf…

Django模板語法及靜態文件

模板語法及靜態文件 1 多app創建 在主路由當中引入 include include()函數是Django.urls模塊中的一個函數&#xff0c;它的作用是在urls.py文件中引入其他應用的URL模式。 from django.urls import path, include創建多個app python manage.py startapp project_one python ma…

[PWNME 2025] PWN 復現

這種比賽得0也不容易&#xff0c;前邊暖聲還是能作的。 GOT 指針前溢出&#xff0c;可以溢出到GOT表&#xff0c;然后把后門寫上就行 Einstein 這個拿到WP也沒復現成&#xff0c;最后自己改了一下。 int __cdecl handle() {int offset; // [rsp8h] [rbp-38h] BYREFunsigne…

微信小程序將markdown內容轉為pdf并下載

要在微信小程序中將Markdown內容轉換為PDF并下載,您可以使用以下方法: 方法一:使用第三方API服務 選擇第三方API服務: 可以選擇像 Pandoc、Markdown-PDF 或 PDFShift 這樣的服務,將Markdown轉換為PDF。例如,PDFShift 提供了一個API接口,可以將Markdown內容轉換為PDF格式…

MongoDB(一) - MongoDB安裝教程(Windows + Linux)

文章目錄 前言一、Windows安裝單機MongoDB1. 下載并解壓MongoDB安裝包1.1 下載1.2 解壓1.3 相關文件介紹 2. 配置2.1 配置環境變量2.1.1 打開系統屬性設置2.1.2 編輯 PATH 環境變量2.1.3 驗證環境變量是否配置成功 2.2 創建相關目錄和文件2.3 修改MongoDB配置文件 mongodb.conf…

mybatis日期格式與字符串不匹配bug

異常特征&#xff1a;java.lang.IllegalArgumentException: invalid comparison: java.time.LocalDateTime and java.lang.String ### Error updating database. Cause: java.lang.IllegalArgumentException: invalid comparison: java.time.LocalDateTime and java.lang.Str…

【算法學習之路】5.貪心算法

貪心算法 前言一.什么是貪心算法二.例題1.合并果子2.跳跳&#xff01;3. 老鼠和奶酪 前言 我會將一些常用的算法以及對應的題單給寫完&#xff0c;形成一套完整的算法體系&#xff0c;以及大量的各個難度的題目&#xff0c;目前算法也寫了幾篇&#xff0c;題單正在更新&#xf…

快速使用MASR V3版不能語音識別框架

前言 本文章主要介紹如何快速使用MASR語音識別框架訓練和推理&#xff0c;本文將致力于最簡單的方式去介紹使用&#xff0c;如果使用更進階功能&#xff0c;還需要從源碼去看文檔。僅需三行代碼即可實現訓練和推理。 源碼地址&#xff1a;https://github.com/yeyupiaoling/MA…

C語言_數據結構總結5:順序棧

純C語言代碼&#xff0c;不涉及C 想了解鏈式棧的實現&#xff0c;歡迎查看這篇文章&#xff1a;C語言_數據結構總結6&#xff1a;鏈式棧-CSDN博客 這里分享插入一下個人覺得很有用的習慣&#xff1a; 1. 就是遇到代碼哪里不理解的&#xff0c;你就問豆包&#xff0c;C知道&a…

2021 年 6 月青少年軟編等考 C 語言六級真題解析

目錄 T1. 波蘭表達式T2. 多項式相加思路分析T3. 撲克牌排序思路分析T4. 表達式求值思路分析T1. 波蘭表達式 題目鏈接:SOJ D1087 此題為 2023 年 12 月三級第三題原題,見 2023 年 12 月青少年軟編等考 C 語言三級真題解析中的 T3。 T2. 多項式相加 題目鏈接:SOJ D1088 我…

AI數字人| Fay開源項目、UE5數字人、本地大模型

數字人實踐教程 本教程主要是講如何在本地UE部署Fay數字人的開源框架。 最終效果可以與人進行自然語言的對話&#xff0c;花了大概10個h的時間到&#xff0c;踩了很多坑&#xff0c;同樣想實現的朋友可以作為參考 參考文檔&#xff1a;Fay 數字人開源框架 - 飛書云文檔 官方的教…

網絡版漢譯英服務(muduo)

文章目錄 網絡版漢譯英服務&#xff08;muduo&#xff09;muduo庫muduo 庫是什么muduo 庫常見接口介紹muduo::net::EventLoopmuduo::net::TcpConnectionmuduo::net::TcpServermuduo::net::TcpClientmuduo::net::Buffer 漢譯英服務服務端客戶端 網絡版漢譯英服務&#xff08;mud…

在ArcMap中通過Python編寫自定義工具(Python Toolbox)實現點轉線工具

文章目錄 一、需求二、實現過程2.1、創建Python工具箱&#xff08;.pyt&#xff09;2.2、使用catalog測試代碼2.3、在ArcMap中使用工具 三、測試 一、需求 通過插件的形式將點轉線功能嵌入ArcMap界面&#xff0c;如何從零開始創建一個插件&#xff0c;包括按鈕的添加、工具的實…