莱州做网站/全球最牛的搜索引擎

莱州做网站,全球最牛的搜索引擎,深圳网站建设培训学校,做网站建设有前途那在安装完huatuo热更新插件后就要开始学习如何使用了。 1.创建主框渐Main 新建文件夹Main(可自定义),然后按下图创建文件,注意名称与文件夹名称保持一致 然后新建场景(Init场景),添加3个空物体…

在安装完huatuo热更新插件后就要开始学习如何使用了。

1.创建主框渐Main

新建文件夹Main(可自定义),然后按下图创建文件,注意名称与文件夹名称保持一致

 然后新建场景(Init场景),添加3个空物体分别为LoadDllManager,SceneLoadManager以及PrefabsLoadManager(这部分可根据实际开发需求拓展,此教程只做简单演示,只有切换场景,创建预制体,加载Dll需求),然后在Main文件夹下创建对应名称脚本文件并挂在相应物体上。

注意,Main里的脚本是框架类脚本,不做具体功能需求,所以不支持热更新,一般实现后不会再做修改,一旦修改了就需要重新Build。

下面是3个脚本具体实现。

注意,需要用到一个BetterStreamingAssets加载AB包的类,下载地址:Huatuo热更新使用教程-BetterStreamingAssets资源-CSDN文库

解压后放到Plugins文件夹下即可。

实现Manager脚本时会发现BetterStreamingAssets类提示报错,这是因为Main中没有添加BetterStreamingAssets,进行如下图所示操作即可,之后就会发现报错解决了。同理,其他Assembly Definition文件在使用其他Assembly Definition文件中的类时,也需要进行同样设置,比如之后添加的UIPart需要添加Main。

LoadDllManager

using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Events;/// <summary>
/// 加载Dll的管理器
/// </summary>
public class LoadDllManager : MonoBehaviour
{private static LoadDllManager _instance;/// <summary>/// 单例/// </summary>public static LoadDllManager Instance{get{return _instance;}}private void Awake(){_instance = this;}void Start(){Debug.Log("LoadDllManager start");BetterStreamingAssets.Initialize();DontDestroyOnLoad(gameObject);//加载初始Dll-UIPartLoadDll("UIPart", (value) =>{//找到MainScript脚本,执行LoadMainScene方法Type type = value.GetType("MainScript");type.GetMethod("LoadMainScene").Invoke(null, null);});}/// <summary>/// 加载dll/// </summary>/// <param name="dllName">dll名称</param>/// <param name="callBack">回调</param>public void LoadDll(string dllName, UnityAction<Assembly> callBack){
#if !UNITY_EDITORStartCoroutine(OnLoadDll(dllName, callBack));
#elsevar assembly = AppDomain.CurrentDomain.GetAssemblies().First(assembly => assembly.GetName().Name == dllName);callBack?.Invoke(assembly);
#endif}/// <summary>/// 协程加载dll/// </summary>/// <param name="dllName"></param>/// <param name="callBack"></param>/// <returns></returns>private IEnumerator OnLoadDll(string dllName, UnityAction<Assembly> callBack){//判断ab包是否存在if (File.Exists($"{Application.streamingAssetsPath}/common")){//加载ab包var dllAB = BetterStreamingAssets.LoadAssetBundleAsync("common");yield return dllAB;if(dllAB.assetBundle != null){//加载dllTextAsset dllBytes = dllAB.assetBundle.LoadAsset<TextAsset>($"{dllName}.dll.bytes");var assembly = System.Reflection.Assembly.Load(dllBytes.bytes);//卸载ab包dllAB.assetBundle.Unload(false);//回调callBack?.Invoke(assembly);}}}
}

SceneLoadManager

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;/// <summary>
/// 加载场景的管理器
/// </summary>
public class SceneLoadManager : MonoBehaviour
{private static SceneLoadManager _instance;public static SceneLoadManager Instance{get{return _instance;}}private void Awake(){_instance = this;}private void Start(){Debug.Log("SceneLoadManager start");BetterStreamingAssets.Initialize();DontDestroyOnLoad(gameObject);}/// <summary>/// 加载场景/// </summary>/// <param name="sceneName"></param>/// <param name="callBack"></param>public void LoadScene(string sceneName, UnityAction callBack = null){
#if !UNITY_EDITORStartCoroutine(OnLoadScene(sceneName, callBack));
#elseStartCoroutine(OnLoadScene_Noab(sceneName, callBack));
#endif}/// <summary>/// 通过ab包加载场景/// </summary>/// <param name="sceneName"></param>/// <param name="callBack"></param>/// <returns></returns>private IEnumerator OnLoadScene(string sceneName, UnityAction callBack){//判断场景ab包是否存在if(File.Exists($"{Application.streamingAssetsPath}/scenes")){//加载ab包var dllAB = BetterStreamingAssets.LoadAssetBundleAsync("scenes");yield return dllAB;if(dllAB.assetBundle != null){//异步加载场景var sceneLoadRequest = SceneManager.LoadSceneAsync(sceneName);yield return sceneLoadRequest;if(sceneLoadRequest.isDone){//获取加载的场景Scene loadScene = SceneManager.GetSceneByName(sceneName);//跳转场景SceneManager.SetActiveScene(loadScene);//回调callBack?.Invoke();}//卸载AB包dllAB.assetBundle.Unload(false);}}}/// <summary>/// 加载场景--无需加载ab/// </summary>/// <param name="sceneName"></param>/// <param name="callBack"></param>/// <returns></returns>private IEnumerator OnLoadScene_Noab(string sceneName, UnityAction callBack){//异步加载场景var sceneLoadRequest = SceneManager.LoadSceneAsync(sceneName);yield return sceneLoadRequest;if (sceneLoadRequest.isDone){//获取加载的场景Scene loadScene = SceneManager.GetSceneByName(sceneName);//跳转场景SceneManager.SetActiveScene(loadScene);//回调callBack?.Invoke();}}
}

PrefabsLoadManager

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Events;/// <summary>
/// 加载预制体的管理器
/// </summary>
public class PrefabsLoadManager : MonoBehaviour
{private static PrefabsLoadManager _instance;public static PrefabsLoadManager Instance{get{return _instance;}}private void Awake(){_instance = this;}private void Start(){Debug.Log("PrefabsLoadManager start");BetterStreamingAssets.Initialize();DontDestroyOnLoad(gameObject);}/// <summary>/// 加载预制体/// </summary>/// <param name="prefabPath"></param>/// <param name="callBack"></param>public void LoadABPrefab(string prefabPath, UnityAction<GameObject> callBack){
#if !UNITY_EDITORstring[] paths = prefabPath.Split('/');string prefabName = paths[paths.Length - 1];StartCoroutine(OnLoadPrefab(prefabName, callBack));
#elseprefabPath += ".prefab";GameObject loadedPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);GameObject obj = GameObject.Instantiate(loadedPrefab);callBack?.Invoke(obj);
#endif}/// <summary>/// 通过AB包加载预制体/// </summary>/// <param name="prefabName"></param>/// <param name="callBack"></param>/// <returns></returns>private IEnumerator OnLoadPrefab(string prefabName, UnityAction<GameObject> callBack){//判断预制体的ab包是否存在if (File.Exists($"{Application.streamingAssetsPath}/prefabs")){//加载ab包var dllAB = BetterStreamingAssets.LoadAssetBundleAsync("prefabs");yield return dllAB;if(dllAB.assetBundle != null){//创建预制体GameObject loadedPrefab = GameObject.Instantiate(dllAB.assetBundle.LoadAsset<UnityEngine.GameObject>($"{prefabName}.prefab"));//卸载ab包dllAB.assetBundle.Unload(false);callBack?.Invoke(loadedPrefab);}}}
}

后续根据需求还会有图集的AB包,材质的AB包等,在此不做详细扩展。

至此一个主要的框架就好了,下面就要开始实现热更新的部分了。

2.实现UIPart热更新部分功能

创建UIPart文件夹(名称及内部脚本名称,方法名称可随意修改,但需要相应修改LoadDllManager对应名称字段),然后创建同名Assembly Definition文件。

创建MainScript脚本,实现如下

MainScript脚本为加载Main场景,创建Main场景,场景中添加一个Canvas,创建MainCanvas脚本,实现如下,创建MainView预制体

using UnityEngine;
using System;
using System.Linq;public class MainCanvas : MonoBehaviour
{public GameObject lay_1;public GameObject lay_2;public GameObject lay_3;public static AssetBundle dllAB;private System.Reflection.Assembly gameAss;void Start(){PrefabsLoadManager.Instance.LoadABPrefab("Assets/UIPart/Prefabs/UI/MainView", (mainView) =>{if (mainView != null)mainView.transform.SetParent(lay_1.transform, false);});}
}

然后创建MainView预制体及脚本,实现自己想实现的测试功能,在此就不具体实现了。注意上述预制体,脚本都需要放在UIPart文件夹下,可自行创建区分的文件夹。

这些都完成后,需要在HybridCLR中配置一下,如图

 之后就可以进行生成DLL,打包AB包等操作

3.生成Dll文件

如图,自行选择平台

 生成Dll文件所在路径为Assets同级目录\HybridCLRData\HotUpdateDlls下对应平台内。

4.复制Dll,方便打AB包

然后就是打包AB包,打包前先将生成的Dll及部分依赖的Dll先复制到Assets内,方便打包成AB包,此处提供一个我简单实现的复制工具(使用UIToolkit实现)

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
using System.Collections.Generic;
using System.IO;public class CopyDllEditor : EditorWindow
{public static readonly List<string> aotDlls = new List<string>(){"mscorlib.dll","System.dll","System.Core.dll",// 如果使用了Linq,需要这个// "Newtonsoft.Json.dll",// "protobuf-net.dll",// "Google.Protobuf.dll",// "MongoDB.Bson.dll",// "DOTween.Modules.dll",// "UniTask.dll",};/// <summary>/// 复制dll相关数据/// </summary>CopyDllData dllData = null;/// <summary>/// 用于初始化的json文件路径/// </summary>private string DllFileJsonPath = "";[MenuItem("CopyDllEditor/Settings")]public static void ShowExample(){CopyDllEditor wnd = GetWindow<CopyDllEditor>();wnd.titleContent = new GUIContent("CopyDllEditor");wnd.minSize = new Vector2(810, 540);wnd.maxSize = new Vector2(1910, 810);//wnd.position = new Rect(new Vector2(1920, 540), new Vector2(1600, 540));}public void CreateGUI(){DllFileJsonPath = $"{Application.dataPath}/Editor/CopyDll/DllFile.json";//初始化Init();if(dllData == null){dllData = new CopyDllData();dllData.Files = new List<string>();}if (!File.Exists(DllFileJsonPath)){File.Create(DllFileJsonPath);}VisualElement root = rootVisualElement;//添加平台选择EnumField toType = new EnumField("选择平台");toType.Init(BuildTarget.StandaloneWindows64);//初始化平台选择if (!string.IsNullOrEmpty(dllData.PingTaiType)){//toType.value = (BuildTarget)System.Enum.Parse(typeof(BuildTarget), dllData.PingTaiType);}else{dllData.PingTaiType = toType.value.ToString();}//平台改变监听toType.RegisterCallback<ChangeEvent<string>>((evt) =>{dllData.PingTaiType = evt.newValue;});root.Add(toType);//dll原始文件所在路径输入框TextField formPathInput = new TextField("dll原始文件路径(无需加平台文件夹名称,末尾加\\)");//初始化if(!string.IsNullOrEmpty(dllData.FromPath)){formPathInput.value = dllData.FromPath;}//监听原始文件路径改变formPathInput.RegisterCallback<ChangeEvent<string>>((evt) =>{dllData.FromPath = evt.newValue;});root.Add(formPathInput);//复制到目标目录路径输入框TextField toPathInput = new TextField("dll保存文件路径(无需加平台文件夹名称,最好为工程Assets内路径,末尾加\\)");//初始化if (!string.IsNullOrEmpty(dllData.ToPath)){toPathInput.value = dllData.ToPath;}//监听目标路径改变toPathInput.RegisterCallback<ChangeEvent<string>>((evt) =>{dllData.ToPath = evt.newValue;});root.Add(toPathInput);//设置dll文件数量的输入框IntegerField filescount = new IntegerField("dll文件数量");//初始化filescount.value = dllData.Files.Count;root.Add(filescount);//滑动界面ScrollView scrollView = new ScrollView();root.Add(scrollView);//所有文件名称输入框List<TextField> dllFileField = new List<TextField>();//初始化文件名称输入框foreach (var item in dllData.Files){TextField fileName = new TextField("dll文件名称(带后缀)");scrollView.Add(fileName);fileName.value = item;dllFileField.Add(fileName);}//监听文件数量变化filescount.RegisterCallback<ChangeEvent<int>>((evt) =>{//若资源数量增加if (evt.newValue > evt.previousValue){int count = evt.newValue - evt.previousValue;for (int i = 0; i < count; i++){TextField fileName = new TextField("dll文件名称(带后缀)");scrollView.Add(fileName);dllFileField.Add(fileName);}}else{int count = evt.previousValue - evt.newValue;int index = evt.previousValue - 1;//若减少,曾从后往前删除for (int i = 0; i < count; i++){scrollView.RemoveAt(index);dllFileField.RemoveAt(index);index--;}}});//复制dll文件按钮Button copyBtn = new Button(() =>{BuildTarget v = (BuildTarget)System.Enum.Parse(typeof(BuildTarget), toType.value.ToString());string yuanshiPath = GetHotFixDllsOutputDirByTarget(v);dllData.Files.Clear();foreach (var item in dllFileField){//去除未输入的和重复的if(!string.IsNullOrEmpty(item.value) && !dllData.Files.Contains(item.value)){//去除文件不存在的string filePath = $"{yuanshiPath}/{item.value}";if(File.Exists(filePath))dllData.Files.Add(item.value);}}//保存当前设置结果到json文件中,用于下次打开初始化string fileValue = JsonUtility.ToJson(dllData);File.WriteAllText(DllFileJsonPath, fileValue);//选择平台进行文件复制switch(v){case BuildTarget.StandaloneWindows:CopeByStandaloneWindows32();break;case BuildTarget.StandaloneWindows64:CopeByStandaloneWindows64();break;case BuildTarget.Android:CopeByAndroid();break;case BuildTarget.iOS:CopeByIOS();break;}});copyBtn.text = "复制dll文件";root.Add(copyBtn);}private void Init(){string value = File.ReadAllText(DllFileJsonPath);dllData = JsonUtility.FromJson<CopyDllData>(value);}private void CopeByStandaloneWindows32(){Copy(BuildTarget.StandaloneWindows);}private void CopeByStandaloneWindows64(){Copy(BuildTarget.StandaloneWindows64);}private void CopeByAndroid(){Copy(BuildTarget.Android);}private void CopeByIOS(){Copy(BuildTarget.iOS);}private void Copy(BuildTarget target){//复制的dll文件列表List<string> copyDlls = dllData.Files;//dll原始路径string outDir = GetHotFixDllsOutputDirByTarget(target);//目标路径string exportDir = GetDllToPath(target);if (!Directory.Exists(exportDir)){Directory.CreateDirectory(exportDir);}//复制foreach (var copyDll in copyDlls){File.Copy($"{outDir}/{copyDll}", $"{exportDir}/{copyDll}.bytes", true);}//复制固定需要的依赖dll文件,路径固定string AssembliesPostIl2CppStripDir = Application.dataPath.Remove(Application.dataPath.Length - 6, 6) + "HybridCLRData/AssembliesPostIl2CppStrip";string aotDllDir = $"{AssembliesPostIl2CppStripDir}/{target}";foreach (var dll in aotDlls){string dllPath = $"{aotDllDir}/{dll}";if (!File.Exists(dllPath)){Debug.LogError($"ab中添加AOT补充元数据dll:{dllPath} 时发生错误,文件不存在。需要构建一次主包后才能生成裁剪后的AOT dll");continue;}string dllBytesPath = $"{exportDir}/{dll}.bytes";File.Copy(dllPath, dllBytesPath, true);}AssetDatabase.Refresh();Debug.Log("热更Dll复制成功!");}/// <summary>/// 获取热更新时输出dll文件的路径/// </summary>/// <param name="target"></param>/// <returns></returns>public string GetHotFixDllsOutputDirByTarget(BuildTarget target){string path = dllData.FromPath;switch (target){case BuildTarget.StandaloneWindows:path += "StandaloneWindows";break;case BuildTarget.StandaloneWindows64:path += "StandaloneWindows64";break;case BuildTarget.Android:path += "Android";break;case BuildTarget.iOS:path += "iOS";break;}return path;}/// <summary>/// 获取复制文件目标路径/// </summary>/// <param name="target"></param>/// <returns></returns>public string GetDllToPath(BuildTarget target){string path = dllData.ToPath;switch (target){case BuildTarget.StandaloneWindows:path += "StandaloneWindows";break;case BuildTarget.StandaloneWindows64:path += "StandaloneWindows64";break;case BuildTarget.Android:path += "Android";break;case BuildTarget.iOS:path += "iOS";break;}return path;}
}[SerializeField]
public class CopyDllData
{public string FromPath;public string ToPath;public string PingTaiType;public List<string> Files;
}

放到Editor/CopyDll文件夹下即可,打开如下

先选择平台,然后设置原始Dll文件所在路径,再设置输出路径,填入dll文件数量并设置好dll文件名+后缀,最后点击复制即可完成复制。 

5.打AB包

此处同样提供一个我简单实现的打包工具(使用UIToolkit实现),也可使用其他打包的插件。

using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using System;
using Object = UnityEngine.Object;public class AssetBundle : EditorWindow
{private Dictionary<string, List<Object>> bundles = new Dictionary<string, List<Object>>();/// <summary>/// ab包设置部分的滑动界面/// </summary>ScrollView abScr = null;[MenuItem("AssetBundle/Setting")]public static void ShowExample(){AssetBundle wnd = GetWindow<AssetBundle>();wnd.titleContent = new GUIContent("AssetBundle");wnd.minSize = new Vector2(810, 540);wnd.maxSize = new Vector2(1910, 810);//wnd.position = new Rect(new Vector2(1920, 540), new Vector2(1600, 540));}public void CreateGUI(){VisualElement root = rootVisualElement;//创建打包按钮,用于打出AB包Button btn_Add = new Button(() =>{//ab包List<AssetBundleBuild> abs = new List<AssetBundleBuild>();//记录当前打包的ab包信息,用于下次打开时初始化ABSaveJsonData saveData = new ABSaveJsonData();saveData.ABSave = new List<ABSaveData>();//遍历设置的ab包数据foreach (var item in bundles){//单个ab包文件名与资源文件数据ABSaveData data = new ABSaveData();data.ABName = item.Key;data.ABFilePath = new List<string>();List<string> assets = new List<string>();foreach (var v in item.Value){if (v == null)continue;//获取资源路径,文件中存储路径信息string filePath = AssetDatabase.GetAssetPath(v);Debug.LogError(filePath);if (assets.Contains(filePath))continue;assets.Add(filePath);data.ABFilePath.Add(filePath);}AssetBundleBuild abFile = new AssetBundleBuild{//包名assetBundleName = item.Key,//资源assetNames = assets.ToArray(),};abs.Add(abFile);//添加每个ab包信息saveData.ABSave.Add(data);}//ab包保存位置string streamingAssetPathDst = $"{Application.streamingAssetsPath}";CreateDirIfNotExists(streamingAssetPathDst);BuildPipeline.BuildAssetBundles(streamingAssetPathDst, abs.ToArray(), BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);//ab包信息文件string bundleFilePath = $"{Application.dataPath}/Editor/AssetBundleEditor/ABFile.json";if (!File.Exists(bundleFilePath)){File.Create(bundleFilePath);}//序列化ab包信息string value = JsonUtility.ToJson(saveData);File.WriteAllText(bundleFilePath, value);});btn_Add.text = "打包";root.Add(btn_Add);CreatAddABBtn(root);}/// <summary>/// 创建添加ab包名的按钮/// </summary>/// <param name="root"></param>private void CreatAddABBtn(VisualElement root){abScr = new ScrollView();abScr.style.width = rootVisualElement.style.width;abScr.style.height = rootVisualElement.style.height;Button btn_Add = new Button(() =>{VisualElement abVi = CreataABNameField();abScr.Add(abVi);});btn_Add.text = "添加ab包名称";root.Add(btn_Add);root.Add(abScr);OnInitBundles(abScr);}/// <summary>/// 初始化上次设置的资源数据/// </summary>/// <param name="root"></param>private void OnInitBundles(VisualElement root){string bundleFilePath = $"{Application.dataPath}/Editor/AssetBundleEditor/ABFile.json";//反序列化文件数据string value = File.ReadAllText(bundleFilePath);ABSaveJsonData data = JsonUtility.FromJson<ABSaveJsonData>(value);foreach (var item in data.ABSave){//初始化bundlesif (!bundles.ContainsKey(item.ABName)){bundles.Add(item.ABName, new List<Object>());foreach (var path in item.ABFilePath){//通过资源路径获取到资源文件bundles[item.ABName].Add(AssetDatabase.LoadAssetAtPath(path, typeof(Object)));}}}foreach (var item in bundles){//初始化编辑器界面VisualElement abVi = CreataABNameField(item.Key, item.Value);root.Add(abVi);}}/// <summary>/// 创建ab包名称的输入框/// </summary>/// <param name="root"></param>/// <param name="defaultValue">初始包名</param>/// <param name="objects">初始资源</param>private VisualElement CreataABNameField(string defaultValue = "", List<Object> objects = null){VisualElement abVi = new VisualElement();TextField field = new TextField("输入ab包名称");field.style.width = 610;abVi.Add(field);//监听内容修改field.RegisterCallback<ChangeEvent<string>>((evt) =>{//修改bundlesif (bundles.ContainsKey(evt.previousValue)){bundles.Remove(evt.previousValue);}if(!bundles.ContainsKey(evt.newValue))bundles.Add(evt.newValue, new List<Object>());});//初始化包名if (string.IsNullOrEmpty(defaultValue))field.value = $"Default_{bundles.Count}";elsefield.value = defaultValue;CreateABCountField(abVi, field, objects);return abVi;}/// <summary>/// 创建ab包资源数量的输入框/// </summary>/// <param name="abVi"></param>/// <param name="field">用于设置bundles的key值</param>/// <param name="objects">初始资源对象</param>private void CreateABCountField(VisualElement abVi, TextField field, List<Object> objects = null){//资源数量输入框IntegerField field_Count = new IntegerField("输入ab资源数量");field_Count.style.width = 200;field.Add(field_Count);Button delBtn = new Button(() =>{if(bundles.ContainsKey(field.value)){bundles.Remove(field.value);}abScr.Remove(abVi);});delBtn.style.width = 60;delBtn.text = "删除ab包";field.Add(delBtn);VisualElement objVisE = new VisualElement();objVisE.style.width = rootVisualElement.style.width;//objVisE.style.maxHeight = 100;//初始化资源对象if (objects != null){//初始化数量field_Count.value = objects.Count;for (int i = 0; i < objects.Count; i++){VisualElement objField = CreataABFile(field, objects[i]);objVisE.Add(objField);}}//监听数量修改field_Count.RegisterCallback<ChangeEvent<int>>((evt) =>{//若资源数量增加if(evt.newValue > evt.previousValue){int count = evt.newValue - evt.previousValue;for (int i = 0; i < count; i++){VisualElement objField = CreataABFile(field);objVisE.Add(objField);}}else{int count = evt.previousValue - evt.newValue;int index = evt.previousValue - 1;//若减少,曾从后往前删除for (int i = 0; i < count; i++){objVisE.RemoveAt(index);if (bundles.ContainsKey(field.value) && bundles[field.value].Count > index){bundles[field.value].RemoveAt(index);}index--;}}});abVi.Add(objVisE);}/// <summary>/// 创建ab包资源的输入框/// </summary>/// <param name="root"></param>/// <param name="field">用于设置bundles的key值</param>/// <param name="obj">初始资源对象</param>/// <returns></returns>private VisualElement CreataABFile(TextField field, Object obj = null){//资源设置框ObjectField objField = new ObjectField();objField.objectType = typeof(Object);//初始化对象内容if(obj != null)objField.value = obj;//监听资源对象改变objField.RegisterCallback<ChangeEvent<Object>>((evt) =>{if (bundles.ContainsKey(field.value)){var objs = bundles[field.value];objs.Remove(evt.previousValue);objs.Add(evt.newValue);}});return objField;}//创建文件夹private static void CreateDirIfNotExists(string dirName){if (!Directory.Exists(dirName)){Directory.CreateDirectory(dirName);}}
}[Serializable]
public class ABSaveData
{[SerializeField]public string ABName;[SerializeField]public List<string> ABFilePath;
}[Serializable]
public class ABSaveJsonData
{[SerializeField]public List<ABSaveData> ABSave;
}

放到Editor/AssetBundleEditor文件夹下即可,界面如图

点击添加ab包名称即可添加一个ab包设置,输入ab包名称及资源数量,设置资源对象最后点击打包即可,ab包输出在StreamingAssets文件夹下。

至此一个简单的热更新就实现了,最后Build工程(Build时只需要Build Init场景即可,无需勾选Main场景等AB包中的场景,当然在编辑器中运行时,需要勾选上其他场景,否则无法跳转),然后修改UIPart中的部分代码,之后依次执行生成dll,复制dll,打ab包,最后将StreamingAssets下的ab包替换到Build的工程中运行,就会发现修改的代码生效了。

下面为我实现的演示工程,地址为:Huatuo热更新演示工程资源-CSDN文库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/70508.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Springboot + Ollama + IDEA + DeepSeek 搭建本地deepseek简单调用示例

1. 版本说明 springboot 版本 3.3.8 Java 版本 17 spring-ai 版本 1.0.0-M5 deepseek 模型 deepseek-r1:7b 需要注意一下Ollama的使用版本&#xff1a; 2. springboot项目搭建 可以集成在自己的项目里&#xff0c;也可以到 spring.io 生成一个项目 生成的话&#xff0c;如下…

如何在 macOS 上配置 MySQL 环境变量

如何在 macOS 上配置 MySQL 环境变量 步骤 1: 查找 MySQL 安装路径 打开终端&#xff0c;使用以下命令查找 mysql 的可执行文件路径&#xff1a; which mysql如果该命令没有返回结果&#xff0c;可以使用 find 命令&#xff1a; sudo find / -name "mysql" 2>/de…

Unity Excel导表工具转Lua文件

思路介绍 借助EPPlus读取Excel文件中的配置数据&#xff0c;根据指定的不同类型的数据配置规则来解析成对应的代码文本&#xff0c;将解析出的字符串内容写入到XXX.lua.txt文件中即可 EPPlus常用API //命名空间 using OfficeOpenXml;//Excel文件路径 var fileExcel new File…

【vue项目中如何实现一段文字跑马灯效果】

在Vue项目中实现一段文字跑马灯效果&#xff0c;可以通过多种方式实现&#xff0c;以下是几种常见的方法&#xff1a; 方法一&#xff1a;使用CSS动画和Vue数据绑定 这种方法通过CSS动画实现文字的滚动效果&#xff0c;并结合Vue的数据绑定动态更新文本内容。 步骤&#xff…

LeeCode题库第二十八题

28.找出字符串第一个匹配项的下标 项目场景&#xff1a; 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 …

深入解析BFS算法:C++实现无权图最短路径的高效解决方案

在无权图中&#xff0c;广度优先搜索&#xff08;BFS&#xff09;是解决最短路径问题的高效算法。接下来博主从专业角度深入探讨其实现细节&#xff0c;并给出C代码示例&#xff1a; 目录 一、核心原理 二、算法步骤 三、C实现关键点 1. 数据结构 2. 边界检查 3. 路径回溯…

Plant Simulation培训教程-双深堆垛机立库仿真模块

原创 知行 天理智能科技 2025年01月03日 17:02 浙江 又到年终盘点的时候了&#xff0c;在这里我把之前录制的Plant Simulation培训教程-双深堆垛机立库仿真模块分享出来&#xff0c;有需要的可以直接联系我。 双深堆垛机立库仿真模块基于单深模块开发&#xff0c;适用于双深堆…

文本和语音互转

目录 1. 下载依赖ddl 2. 引入Pom依赖 3. java代码 二. 语音转文本 1. 下载中文语音转文本的模型 2. 引入pom依赖 3. java代码 4. 运行效果 1. 下载依赖ddl 文字转语音文件需要使用jacob的dll文件放在jdk安装目录下的bin文件夹下 点击官网下载录或者通过csdn下载 2. …

Python大数据可视化:基于python大数据的电脑硬件推荐系统_flask+Hadoop+spider

开发语言&#xff1a;Python框架&#xff1a;flaskPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 价格区间界面 用户信息界面 品牌管理 笔记本管理 电脑主机…

阿里云虚机的远程桌面登录提示帐户被锁定了

提示由于安全原因&#xff0c;帐户被锁定。 阿里云虚机ECS的远程桌面登录提示帐户被锁定了&#xff0c;只能登录阿里云处理 阿里云-计算&#xff0c;为了无法计算的价值 需选择通过VNC连接 然后计算机管理&#xff0c;解除帐户锁定即可。

Servlet概述(Ⅰ)

目录 一、Servlet概述 演示 创建JavaWeb项目&#xff08;2017版本为例&#xff09; 1. 打开 IntelliJ IDEA 2. 选择项目类型 3. 配置框架 二、Servlet初识(熟练) 1.servlet说明 2.Servlet 接口方法 3.创建Servlet 4.JavaWeb请求响应流程 ​编辑 ​编辑 5.servlet…

Linux基础开发工具的使用(apt、vim、gcc、g++、gdb、make、makefile)

Linux软件包管理器–apt Linux安装软件的方式 在Linux下安装软件的方法有以下三种&#xff1a; 下载到程序的源代码&#xff0c;自己编译出可执行程序获取deb安装包、然后使用dpkg命令安装。&#xff08;不解决依赖关系&#xff09;通过apt进行安装软件。 小知识点&#xf…

C/C++ | 每日一练 (2)

&#x1f4a2;欢迎来到张胤尘的技术站 &#x1f4a5;技术如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 C/C | 每日一练 (2)题目参考答案封装继承多态虚函数底…

【前端框架】vue2和vue3的区别详细介绍

Vue 3 作为 Vue 2 的迭代版本&#xff0c;在性能、语法、架构设计等多个维度均有显著的变革与优化。以下详细剖析二者的区别&#xff1a; 响应式系统 Vue 2 实现原理&#xff1a;基于 Object.defineProperty() 方法实现响应式。当一个 Vue 实例创建时&#xff0c;Vue 会遍历…

基于Spring Boot的农事管理系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

拆解微软CEO纳德拉战略蓝图:AI、量子计算、游戏革命如何改写未来规则!

2025年2月19日 知名博主Dwarkesh Patel对话微软CEO萨蒂亚纳德拉 在最新访谈释放重磅信号&#xff1a;AI将掀起工业革命级增长&#xff0c;量子计算突破引爆材料科学革命&#xff0c;游戏引擎进化为世界模拟器。 整个视频梳理出几大核心观点&#xff0c;揭示科技巨头的未来十年…

HBase Shell

目录 1. HBase常用命令1.1 create命令1.2 list命令1.3 describe命令1.4 put命令1.5 get命令1.6 scan命令1.7 count命令1.8 exists命令1.9 修改表结构1.10 delete命令1.11 deleteall命令1.12 truncate命令1.13 disable、drop命令1.14 status命令1.15 version命令 2. HBase Shell…

MATLAB基础学习相关知识

MATLAB安装参考&#xff1a;抖音-记录美好生活 MATLAB基础知识学习参考&#xff1a;【1小时Matlab速成教程-哔哩哔哩】 https://b23.tv/CnvHtO3 第1部分&#xff1a;变量定义和基本运算 生成矩阵&#xff1a; % 生成矩阵% 直接法% ,表示行 ;表示列 a [1,2,3;4,5,6;7,8,9];%…

基于ffmpeg+openGL ES实现的视频编辑工具-opengl相关逻辑(五)

在我们的项目中,OpenGL ES 扮演着至关重要的角色,其主要功能是获取图像数据,经过一系列修饰后将处理结果展示到屏幕上,以此实现各种丰富多样的视觉效果。为了让大家更好地理解后续知识,本文将详细介绍 OpenGL 相关代码。需要注意的是,当前方案将对 OpenGL 的所有操作都集…