Unity | 资源热更(YooAsset AB)

目录

一、AssetBundle 

1. 插件AssetBundle Browser 打AB包

(1)Unity(我用的版本是2020.3.8)导入AssetBundle Browser

(2)设置Prefab

(3)AssetBundleBrowser面板

2. 代码打AB包

二、YooAsset

1. 准备工作

(1)官方文档

(2) 通过Packages清单安装YooAsset

2.全局配置

3.资源配置

(1)Enable Addressable

(2)Pack Rule打包规则

4.资源构建

(1)Build Mode

(2)Encryption

 5.YooAsset运行时

(1)初始化

(2)资源系统的运行模式

(3)获取资源版本

(4)更新资源清单

(5)资源包下载

(6)资源加载

(7)完整TestLoad

6.YooAsset测试

三、参考资料


一、AssetBundle 

        AB包可以存储绝大部分Unity资源但无法直接存储C#脚本,所以代码的热更新需要使用Lua或者存储编译后的DLL文件。

        AB包不能重复进行加载,当AB包已经加载进内存后必须卸载后才能重新加载。

        多个资源分布在不同的AB包可能会出现一个预制体的贴图等部分资源不在同一个包下,直接加载会出现部分资源丢失的情况,即AB包之间是存在依赖关系的,在加载当前AB包时需要一并加载其所依赖的包。

        打包完成后,会自动生成一个主包(主包名称随平台不同而不同),主包的manifest下会存储有版本号、校验码(CRC)、所有其它包的相关信息(名称、依赖关系)。

1. 插件AssetBundle Browser 打AB包

(1)Unity(我用的版本是2020.3.8)导入AssetBundle Browser

        从github下载插件,将下载后的安装包解压到Unity工程的Packages文件夹下,如果报错,删除Tests即可。  

(2)设置Prefab

(3)AssetBundleBrowser面板

         正确获取到并安装完插件后,通过 Windows/AssetBundle Browser下打开AB包管理面板 一共有三个面板。利用AssetBundleBrowser打包时,我们用的是Build面板,设置好之后点击[build]即可。

  • Configure面板 :能查看当前AB包及其内部资源的基本情况(大小,资源,依赖情况等)。

  • Build面板:负责AssetBundle打包的相关设置。 

  • Inspect面板:用来查看已经打包后的AB包文件的一些详细情况(大小,资源路径等)。

2. 代码打AB包

        通过代码打AB包,不需要设置prefab的AssetBundle名称。

private static void BuildAssetBundles(string tempDir, string outputDir, BuildTarget target){Debug.Log("AB outputDir:"+ outputDir);Directory.CreateDirectory(tempDir);Directory.CreateDirectory(outputDir);List<AssetBundleBuild> abs = new List<AssetBundleBuild>();{var prefabAssets = new List<string>();string testPrefab = $"{Application.dataPath}/Prefabs/Cube.prefab";prefabAssets.Add(testPrefab);AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);abs.Add(new AssetBundleBuild{assetBundleName = "prefabs",assetNames = prefabAssets.Select(s => ToRelativeAssetPath(s)).ToArray(),});}BuildPipeline.BuildAssetBundles(outputDir, abs.ToArray(), BuildAssetBundleOptions.None, target);AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);}

二、YooAsset

        AssetBundle 的管理和使用相对复杂,需要处理依赖关系、版本控制、加载策略等问题。因此,很多开发者会选择使用如 YooAsset 这样的资源管理框架来简化 AssetBundle 的使用,自动处理这些复杂的任务。

1. 准备工作

(1)官方文档

  • Introduce | YooAsset  

(2) 通过Packages清单安装YooAsset

        直接修改Packages文件夹下的清单文件manifest.json:

{"dependencies": {"com.tuyoogame.yooasset": "2.1.0",......},"scopedRegistries": [{"name": "package.openupm.cn","url": "https://package.openupm.cn","scopes": ["com.tuyoogame.yooasset"]}]
}

         如果出现以下报错,说明Unity没有安装Windows Build Support(IL2CPP)模块,安装即可。

2.全局配置

        Project窗体内右键 -> Create -> YooAsset -> Create YooAsset Setting可创建YooAssetSettings全局配置文件,该文件需放在Resources文件夹下:

  • Manifest File Name : 清单文件名称
  • DefaultYooFolderName:Yoo文件夹名称

3.资源配置

        Project窗体内右键 -> Create -> YooAsset -> Create AssetBundle Collector Setting可创建该文件,需要注意的是一个工程只能有一个AssetBundleCollectorSetting。

重点关注Enable Addressable及Pack Rule打包规则:

(1)Enable Addressable

        启用可寻址资源定位系统。启用后加载资源时可以不写全路径,只根据资源名称即可加载:

 YooAssets.LoadSceneAsync("scene_home");

(2)Pack Rule打包规则

        规则可以自定义扩展。下面是内置规则:

  • PackSeparately 以文件路径作为资源包名,每个资源文件单独打包(比如场景文件)。
  • PackDirectory 以文件所在的文件夹路径作为资源包名,该文件夹下所有文件打进一个资源包。
  • PackTopDirectory 以收集器下顶级文件夹为资源包名,该文件夹下所有文件打进一个资源包。
  • PackCollector 以收集器路径作为资源包名,收集的所有文件打进一个资源包。
  • PackGroup 以分组名称作为资源包名,收集的所有文件打进一个资源包。
  • PackRawFile 目录下的资源文件会被处理为原生资源包。

4.资源构建

(1)Build Mode

  • ForceRebuild:强制构建模式,会删除指定构建平台下的所有构建记录,重新构建所有资源包。
  • IncrementalBuild:增量构建模式,以上一次构建结果为基础,对于发生变化的资源进行增量构建。

(2)Encryption

        加密类列表。Build时选的加密类型要和加载资源时的解密类型一致,否则加载资源时会报错。比如Build时选择的加密方式为FileOffsetEncryption,则文件解密服务接口也需要为FileOffset类型:

    {...var initParameters = new HostPlayModeParameters();//HostPlayModeParameters继承自    InitializeParametersinitParameters.BuildinQueryServices = new GameQueryServices();//内置资源查询服务接口initParameters.DecryptionServices = new FileOffsetDecryption();//如果资源包在构建的时候有加密,需要提供实现IDecryptionServices接口的实例类。...}/// <summary>/// 资源文件偏移加载解密类/// </summary>private class FileOffsetDecryption : IDecryptionServices{/// <summary>/// 同步方式获取解密的资源包对象/// 注意:加载流对象在资源包对象释放的时候会自动释放/// </summary>AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream){managedStream = null;return AssetBundle.LoadFromFile(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset());}/// <summary>/// 异步方式获取解密的资源包对象/// 注意:加载流对象在资源包对象释放的时候会自动释放/// </summary>AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream){managedStream = null;return AssetBundle.LoadFromFileAsync(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset());}private static ulong GetFileOffset(){return 32;}}

 5.YooAsset运行时

        以官方提供的Space Shooter这个Demo为例,重新编写YooAsset相关的流程。先确保Demo能正常运行:

  • Space Shooter在导入完成后,打开YooAsset->AssetBundle Collector窗口。
  • 点击修复按钮,然后点击Save按钮保存配置,最后关闭窗口。
  • 找到Boot.scene场景启动游戏。

        打开Boot.cs文件,注释掉 IEnumerator Start方法,在TestLoad方法中一步一步进行YooAsset资源的加载:

    private void Start(){StartCoroutine(TestLoad());}IEnumerator TestLoad(){...}

(1)初始化

    IEnumerator TestLoad(){// 初始化资源系统YooAssets.Initialize();// 创建默认的资源包var package = YooAssets.CreatePackage("DefaultPackage");// 设置该资源包为默认的资源包,可以使用YooAssets相关加载接口加载该资源包内容。YooAssets.SetDefaultPackage(package);...    }

(2)资源系统的运行模式

...
if (PlayMode == EPlayMode.EditorSimulateMode)
{var initParameters = new EditorSimulateModeParameters();//EditorSimulateModeParameters继承自InitializeParametersstring simulateManifestFilePath = EditorSimulateModeHelper.SimulateBuild(EDefaultBuildPipeline.BuiltinBuildPipeline.ToString(), "DefaultPackage");initParameters.SimulateManifestFilePath = simulateManifestFilePath;yield return package.InitializeAsync(initParameters);
}
else if (PlayMode == EPlayMode.OfflinePlayMode)
{var initParameters = new OfflinePlayModeParameters();//OfflinePlayModeParameters继承自InitializeParametersinitParameters.DecryptionServices = new FileOffsetDecryption();//需要补充这个yield return package.InitializeAsync(initParameters);
}
else if (PlayMode == EPlayMode.HostPlayMode)
{// 注意:GameQueryServices.cs 太空战机的脚本类,详细见StreamingAssetsHelper.csstring defaultHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";string fallbackHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";var initParameters = new HostPlayModeParameters();//HostPlayModeParameters继承自InitializeParametersinitParameters.BuildinQueryServices = new GameQueryServices();//内置资源查询服务接口initParameters.DecryptionServices = new FileOffsetDecryption();//如果资源包在构建的时候有加密,需要提供实现IDecryptionServices接口的实例类。initParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);//远端服务器查询服务接口var initOperation = package.InitializeAsync(initParameters);yield return initOperation;if (initOperation.Status == EOperationStatus.Succeed){Debug.Log("资源包初始化成功!");}else{Debug.LogError($"资源包初始化失败:{initOperation.Error}");}
}
else if (PlayMode == EPlayMode.WebPlayMode)
{string defaultHostServer = "http://127.0.0.1/CDN/WebGL/V1.0";string fallbackHostServer = "http://127.0.0.1/CDN/WebGL/V1.0";var initParameters = new WebPlayModeParameters();//WebPlayModeParameters继承自InitializeParametersinitParameters.BuildinQueryServices = new GameQueryServices();initParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);var initOperation = package.InitializeAsync(initParameters);yield return initOperation;if (initOperation.Status == EOperationStatus.Succeed){Debug.Log("资源包初始化成功!");}else{Debug.LogError($"资源包初始化失败:{initOperation.Error}");}
}

补充两个类:

    /// <summary>/// 资源文件偏移加载解密类/// </summary>private class FileOffsetDecryption : IDecryptionServices{/// <summary>/// 同步方式获取解密的资源包对象/// 注意:加载流对象在资源包对象释放的时候会自动释放/// </summary>AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream){managedStream = null;return AssetBundle.LoadFromFile(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset());}/// <summary>/// 异步方式获取解密的资源包对象/// 注意:加载流对象在资源包对象释放的时候会自动释放/// </summary>AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream){managedStream = null;return AssetBundle.LoadFromFileAsync(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset());}private static ulong GetFileOffset(){return 32;}}/// <summary>/// 远端资源地址查询服务类/// </summary>private class RemoteServices : IRemoteServices{private readonly string _defaultHostServer;private readonly string _fallbackHostServer;public RemoteServices(string defaultHostServer, string fallbackHostServer){_defaultHostServer = defaultHostServer;_fallbackHostServer = fallbackHostServer;}string IRemoteServices.GetRemoteMainURL(string fileName){return $"{_defaultHostServer}/{fileName}";}string IRemoteServices.GetRemoteFallbackURL(string fileName){return $"{_fallbackHostServer}/{fileName}";}}

(3)获取资源版本

    ...var package = YooAssets.GetPackage("DefaultPackage");var operation = package.UpdatePackageVersionAsync();yield return operation;if (operation.Status == EOperationStatus.Succeed){//更新成功string packageVersion = operation.PackageVersion;Debug.Log($"Updated package Version : {packageVersion}");}else{//更新失败Debug.LogError(operation.Error);}

(4)更新资源清单

        对于联机运行模式,在获取到资源版本号之后,就可以利用UpdatePackageManifestAsync更新资源清单了:

    ...    bool savePackageVersion = true;var package = YooAssets.GetPackage("DefaultPackage");var operation = package.UpdatePackageManifestAsync(packageVersion, savePackageVersion);yield return operation;if (operation.Status == EOperationStatus.Succeed){//更新成功}else{//更新失败Debug.LogError(operation.Error);}

(5)资源包下载

    IEnumerator Download(){int downloadingMaxNum = 10;int failedTryAgain = 3;var package = YooAssets.GetPackage("DefaultPackage");//创建资源下载器,下载所有资源var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain);//没有需要下载的资源if (downloader.TotalDownloadCount == 0){Debug.Log("TotalDownloadCount = 0");yield break;}//需要下载的文件总数和总大小int totalDownloadCount = downloader.TotalDownloadCount;long totalDownloadBytes = downloader.TotalDownloadBytes;//注册回调方法downloader.OnDownloadErrorCallback = OnDownloadErrorFunction;downloader.OnDownloadProgressCallback = OnDownloadProgressUpdateFunction;downloader.OnDownloadOverCallback = OnDownloadOverFunction;downloader.OnStartDownloadFileCallback = OnStartDownloadFileFunction;//开启下载downloader.BeginDownload();yield return downloader;//检测下载结果if (downloader.Status == EOperationStatus.Succeed){Debug.Log("Finish");}else{Debug.Log("DownLoad Failed");}}private void OnStartDownloadFileFunction(string fileName, long sizeBytes){Debug.Log("fileName:" + fileName + ",sizeBytes:" + sizeBytes);}private void OnDownloadOverFunction(bool isSucceed){Debug.Log("isSucceed");}private void OnDownloadProgressUpdateFunction(int totalDownloadCount, int currentDownloadCount, long totalDownloadBytes, long currentDownloadBytes){Debug.Log("totalDownloadCount:" + totalDownloadCount + ",currentDownloadCount" + currentDownloadCount + ",totalDownloadBytes:" + totalDownloadBytes + ",currentDownloadBytes" + currentDownloadBytes);}private void OnDownloadErrorFunction(string fileName, string error){Debug.Log("DownloadError:" + fileName + ",error:" + error);}

(6)资源加载

//加载场景,启用可寻址功能(Enable Addressable)后,不用写全路径,直接写资源名称即可
string location = "scene_home";
var sceneMode = UnityEngine.SceneManagement.LoadSceneMode.Single;
bool suspendLoad = false;
SceneHandle handle = package.LoadSceneAsync(location, sceneMode, suspendLoad);
yield return handle;
Debug.Log($"Scene name is {handle.SceneObject.name}");//加载预制体
AssetHandle handle0 = package.LoadAssetAsync<GameObject>("UIHome");//不用加后缀
yield return handle0;
GameObject go = handle0.InstantiateSync();
Debug.Log($"Prefab name is {go.name}");//加载音频
AssetHandle handle1 = package.LoadAssetAsync<AudioClip>("music_background");
yield return handle1;
AudioClip audioClip = handle1.AssetObject as AudioClip;
GameObject audio = new GameObject("AudioSource");
audio.AddComponent<AudioSource>().clip= audioClip;
audio.GetComponent<AudioSource>().Play();

(7)完整TestLoad

    IEnumerator TestLoad(){//0.别忘初始化项目中这两个事件相关的类// 游戏管理器 注册场景事件GameManager.Instance.Behaviour = this;// 初始化事件系统UniEvent.Initalize();//1.初始化Debug.Log("1. 初始化");// 初始化资源系统YooAssets.Initialize();// 创建默认的资源包var package = YooAssets.CreatePackage("DefaultPackage");// 设置该资源包为默认的资源包,可以使用YooAssets相关加载接口加载该资源包内容。YooAssets.SetDefaultPackage(package);//2.资源系统的运行模式Debug.Log("2. 资源系统的运行模式");if (PlayMode == EPlayMode.EditorSimulateMode){var initParameters = new EditorSimulateModeParameters();//EditorSimulateModeParameters继承自InitializeParametersstring simulateManifestFilePath = EditorSimulateModeHelper.SimulateBuild(EDefaultBuildPipeline.BuiltinBuildPipeline.ToString(), "DefaultPackage");initParameters.SimulateManifestFilePath = simulateManifestFilePath;yield return package.InitializeAsync(initParameters);}else if (PlayMode == EPlayMode.OfflinePlayMode){var initParameters = new OfflinePlayModeParameters();//OfflinePlayModeParameters继承自InitializeParametersinitParameters.DecryptionServices = new FileOffsetDecryption();//需要补充这个yield return package.InitializeAsync(initParameters);}else if (PlayMode == EPlayMode.HostPlayMode){// 注意:GameQueryServices.cs 太空战机的脚本类,详细见StreamingAssetsHelper.csstring defaultHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";string fallbackHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";var initParameters = new HostPlayModeParameters();//HostPlayModeParameters继承自InitializeParametersinitParameters.BuildinQueryServices = new GameQueryServices();//内置资源查询服务接口initParameters.DecryptionServices = new FileOffsetDecryption();//如果资源包在构建的时候有加密,需要提供实现IDecryptionServices接口的实例类。initParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);//远端服务器查询服务接口var initOperation = package.InitializeAsync(initParameters);yield return initOperation;if (initOperation.Status == EOperationStatus.Succeed){Debug.Log("资源包初始化成功!");}else{Debug.LogError($"资源包初始化失败:{initOperation.Error}");}}else if (PlayMode == EPlayMode.WebPlayMode){string defaultHostServer = "http://127.0.0.1/CDN/WebGL/V1.0";string fallbackHostServer = "http://127.0.0.1/CDN/WebGL/V1.0";var initParameters = new WebPlayModeParameters();//WebPlayModeParameters继承自InitializeParametersinitParameters.BuildinQueryServices = new GameQueryServices();initParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);var initOperation = package.InitializeAsync(initParameters);yield return initOperation;if (initOperation.Status == EOperationStatus.Succeed){Debug.Log("资源包初始化成功!");}else{Debug.LogError($"资源包初始化失败:{initOperation.Error}");}}//3.获取资源版本:UpdatePackageVersionAsyncDebug.Log("3. 获取资源版本");var operation = package.UpdatePackageVersionAsync();yield return operation;if (operation.Status == EOperationStatus.Succeed){string packageVersion = operation.PackageVersion;Debug.Log($"Updated package Version : {packageVersion}");//4.更新资源清单:对于联机运行模式,在获取到资源版本号之后,就可以更新资源清单了:UpdatePackageManifestAsync//联机运行模式//通过传入的清单版本,优先比对当前激活清单的版本,如果相同就直接返回成功。如果有差异就从缓存里去查找匹配的清单,如果缓存里不存在,就去远端下载并保存到沙盒里。最后加载沙盒内匹配的清单文件。Debug.Log("4. 更新资源清单");bool savePackageVersion = true;var operation2 = package.UpdatePackageManifestAsync(packageVersion, savePackageVersion);yield return operation2;if (operation2.Status == EOperationStatus.Succeed){//5.资源包下载Debug.Log("5. 资源包下载");yield return Download();//6.加载场景,启用可寻址功能(Enable Addressable)后,不用写全路径,直接写资源名称即可//YooAssets.LoadSceneAsync("scene_home");string location = "scene_home";var sceneMode = UnityEngine.SceneManagement.LoadSceneMode.Single;bool suspendLoad = false;SceneHandle handle = package.LoadSceneAsync(location, sceneMode, suspendLoad);yield return handle;Debug.Log($"Scene name is {handle.SceneObject.name}");//7.加载预制体AssetHandle handle0 = package.LoadAssetAsync<GameObject>("UIHome");//不用加后缀yield return handle0;GameObject go = handle0.InstantiateSync();Debug.Log($"Prefab name is {go.name}");//8.加载音频AssetHandle handle1 = package.LoadAssetAsync<AudioClip>("music_background");yield return handle1;AudioClip audioClip = handle1.AssetObject as AudioClip;GameObject audio = new GameObject("AudioSource");audio.AddComponent<AudioSource>().clip= audioClip;audio.GetComponent<AudioSource>().Play();//9.资源释放handle0.Release();package.UnloadUnusedAssets();}else{//更新失败Debug.LogError(operation.Error);}}else{//更新失败Debug.LogError(operation.Error);}}

6.YooAsset测试

         无论是通过增量构建还是强制构建,都会生成一个以Build Version命名的文件夹,我们把这个文件夹统称为补丁包。补丁包里包含了游戏运行需要的所有资源,我们可以无脑的将补丁包内容覆盖到CDN目录下。

        Host Play Mode下,YooAsset资源加载顺序是:先检查StreamingAsset目录,再检查同Library目录的的Yoo缓存目录(_data),最后才Host服务器下载。

        Offline Play Mode下:检查StreamingAsset目录。

三、参考资料

  • 【用Unity搭建自己的游戏框架】YooAsset资源管理-01.安装和配置_哔哩哔哩_bilibili

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

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

相关文章

nodejs历史版本下载

Node.js — Previous Releases下载地址 .msi 是安装包&#xff08;windows&#xff09;&#xff0c;下载安装包即可

关于最小系统板PCB设计后的一些反思

简介 趁着刚刚画完板子寄回来&#xff0c;在这里做一些记录。 板子状况 这里打烊了5块PCB&#xff0c;但是没有进行SMT贴片&#xff0c;后续如果有芯片可以焊接上去进行后续验证。 封装问题 这里可以看到&#xff0c;我这里两侧的排针都是焊盘&#xff0c;不是通孔&#…

Unity_Timeline使用说明

Unity_Timeline使用说明 首先要找到工具吧&#xff1f;Unity2023.1.19f1c1打开如下&#xff1a; &#xff08;团结引擎没找见哪儿打开&#xff0c;可能是引擎问题吧&#xff1f;有知道的同学可以告诉我在哪儿打开&#xff09; Timelime使用流程&#xff1a; 打开之后会提示您…

ClickHouse为什么这么快(二)SSE指令优化

上一篇 ClickHouse为什么这么快&#xff08;一&#xff09;减少数据扫描范围 我们说到了ClickHouse中使用列存储&#xff0c;每个列都单独存储为一个文件&#xff0c;每个文件都是由一个或多个数据块组成&#xff0c;也就是说&#xff1a;每个文件由一个或多个数组组成&#xf…

3分钟阅读100篇文献?GPT可以做到!

摘要和背景 PPMAN-AI 01 在开始深入阅读之前&#xff0c;了解文献的主题和背景是非常重要的。这可以帮助你快速判断该文献是否符合你的研究需求。 prompt&#xff1a; 请简述文献[文献标题]的摘要。 解释文献[文献标题]中提到的研究背景。 文献[文献标题]的主要研究目的是什…

WordPress块编辑器(Gutenberg古腾堡)中如何添加脚注?

WordPress默认自带的块编辑器​&#xff08;Gutenberg古腾堡编辑器&#xff09;本身就自带添加脚注功能&#xff0c;不过经典编辑器不行。如果想要在WordPress中添加更加专业的脚注&#xff0c;建议使用Modern Footnotes插件&#xff0c;具体介绍及使用请参考『WordPress站点如…

【计算机图形】几何(Geometry)和拓扑(Topology)

目录 参考文献三维实体建模内核CSG/BREPParasolid简介Parasolid接口函数Parasolid类的结构 Parasolid数据分类&#xff1a;几何(Geometry)和拓扑(Topology)拓扑(Topology)什么是“拓扑”呢&#xff1f;Principle Geometry- Topology - Construction Geometry案例&#xff1a;拓…

ElementUI Form:Switch 开关

ElementUI安装与使用指南 Switch 开关 点击下载learnelementuispringboot项目源码 效果图 el-switch.vue 页面效果图 项目里el-switch.vue代码 <script> export default {name: el_switch,data() {return {value: true,value1: true,value2: true,value3: 100,value…

MFC串行化的应用实例

之前写过一篇MFC串行化的博文;下面看一个具体例子; 新建一个单文档应用程序;在最后一步,把View类的基类改为CFormView; 然后在资源面板编辑自己的字段; 然后到doc类的头文件添加对应变量, public:CString name;int age;CString sex;CString dept;CString zhiwu;CStrin…

蓝桥杯2024/1/31----第十届省赛题笔记

题目要求&#xff1a; 1、 基本要求 1.1 使用大赛组委会提供的国信长天单片机竞赛实训平台&#xff0c;完成本试题的程序设计 与调试。 1.2 选手在程序设计与调试过程中&#xff0c;可参考组委会提供的“资源数据包”。 1.3 请注意&#xff1a; 程序编写、调试完成后选手…

JAVA Web 学习(二)ServLet

二、动态web 资源开发技术——Servlet Servlet&#xff08;小服务程序&#xff09;是一个与协议无关的、跨平台的Web组件&#xff0c;由Servlet容器所管理。运行在服务器端&#xff0c;可以动态地扩展服务器的功能&#xff0c;并采用“请求一响应”模式提供Web服务。 Servlet的…

将java对象转换为json字符串的几种常用方法

目录 1.关于json 2.实现方式 1.Gson 2.jackson 3.fastjson 3.与前端的联系 1.关于json JSON是一种轻量级的数据交换格式。它由Douglas Crockford在2001年创造。JSON的全称是JavaScript Object Notation&#xff0c;它是一种文本格式&#xff0c;可以轻松地在各种平台之间传…

C#学习笔记_类(Class)

类的定义 类的定义是以关键字 class 开始&#xff0c;后跟类的名称。类的主体&#xff0c;包含在一对花括号内。 语法格式如下&#xff1a; 访问标识符 class 类名 {//变量定义访问标识符 数据类型 变量名;访问标识符 数据类型 变量名;访问标识符 数据类型 变量名;......//方…

正则表达式与文本三剑客

目录 一、正则表达式 1. 定义 2. 字符匹配 3. 重复限定符 4. 位置锚点 5. 分组和引用 6. 扩展正则表达式 二、文本三剑客 1. grep 1.1 定义 1.2 语法 1.3 选项 1.4 示例 2. sed 2.1 定义 2.2 通式 2.3 选项 2.4 脚本格式&#xff08;脚本语法&#xff09; 2.…

【Unity3D小功能】Unity3D中Text使用超链接并绑定点击事件

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 在开发中遇到了要给Text加超链接的需求&#xff0c;研究了实现…

什么样的评论更容易得到别人的关注

要发表吸引人的评论&#xff0c;可以注意这些个方面&#xff1a; 合适的软件&#xff1a;用DT浏览器的笔记本写文本&#xff0c;保存为图片&#xff0c;用图片的方式评论更容易得到别人的关注。 特别的观点&#xff1a;发表与众不同的观点&#xff0c;或者从不同的角度看待问…

WiFi基础知识介绍(超详细)

1.WiFi专业名词概念 AP(Access Point):无线接入点&#xff1a;这个概念特别广&#xff0c;在这里&#xff0c;用大白话说&#xff0c;你可以把CC3200当做一个无线路由器&#xff0c;这个路由器的特点不能插入网线&#xff0c;没有接入Internet&#xff0c;只能等待其他设备的链…

canvas自定义扩展方法:文字自动换行

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

Websocket基本用法

1.Websocket介绍 WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c;并进行双向数据传输。 应用场景&#xff1a; 视频弹幕网页聊天体育实况更新股票基金…

基于OpenCV的高压电力检测项目案例

一、项目背景与目标 随着高压电力设施的日益增多&#xff0c;传统的巡检方式已无法满足现代电力系统的需求。为此&#xff0c;我们决定利用计算机视觉技术&#xff0c;特别是OpenCV库&#xff0c;开发一个高压电力检测系统。目标是实现自动化、高精度的电力设备检测&#xff0c…