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"
      ]
    }
  ]
}

         unity2020.3.8出现以下报错,报错代码注释掉即可,貌似DownloadHandlerAssetBundle下还没有 autoLoadAssetBundle这个变量,而且支持的版本这个值默认false。

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 目录下的资源文件会被处理为原生资源包(比如dll.byte)。

4.资源构建

(1)Build Mode

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

(2)Encryption

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

    {
        ...
        var initParameters = new HostPlayModeParameters();//HostPlayModeParameters继承自    InitializeParameters
        initParameters.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继承自InitializeParameters
    string 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继承自InitializeParameters
    initParameters.DecryptionServices = new FileOffsetDecryption();//需要补充这个
    yield return package.InitializeAsync(initParameters);
}
else if (PlayMode == EPlayMode.HostPlayMode)
{
    // 注意:GameQueryServices.cs 太空战机的脚本类,详细见StreamingAssetsHelper.cs
    string defaultHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";
    string fallbackHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";
    var initParameters = new HostPlayModeParameters();//HostPlayModeParameters继承自InitializeParameters
    initParameters.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继承自InitializeParameters
    initParameters.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继承自InitializeParameters
            string 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继承自InitializeParameters
            initParameters.DecryptionServices = new FileOffsetDecryption();//需要补充这个
            yield return package.InitializeAsync(initParameters);
        }
        else if (PlayMode == EPlayMode.HostPlayMode)
        {
            // 注意:GameQueryServices.cs 太空战机的脚本类,详细见StreamingAssetsHelper.cs
            string defaultHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";
            string fallbackHostServer = "https://static0.xesimg.com/project-yooasset/0130Offset";
            var initParameters = new HostPlayModeParameters();//HostPlayModeParameters继承自InitializeParameters
            initParameters.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继承自InitializeParameters
            initParameters.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.获取资源版本:UpdatePackageVersionAsync
        Debug.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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity类银河恶魔城学习记录1-9 PlayerWallSilde源代码 P36

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Player.cs using System.Collections; using System.Collections.Generic; using Unity.VisualScripting; us…

【30秒看懂大数据】数据指标

公众号&#xff1a;知幽科技 PS:本文属专栏第24篇 简单说 数据指标是指对企业经营数据转化为可量化、可衡量、可对比、可预测的一个度量或者维度同称。 举例理解 你在小区门口开了一家馒头店。 开业第一天你算了下一共卖了50个馒头&#xff0c;一共收款100元&#xff0…

通过手写简易版RPC理解RPC原理

RPC是什么 所谓的RPC其实是为了不同主机的两个进程间通信而产生的&#xff0c;通常不同的主机之间的进程通信&#xff0c;程序编写需要考虑到网络通信的功能&#xff0c;这样程序的编写将会变得复杂。RPC就来解决这一问题的&#xff0c;一台主机上的进程对另外一台主机的进程发…

乐意购项目前端开发 #6

一、商品详情页面 代码模版 创建Detail文件夹, 然后创建index.vue文件 <script setup> import { getDetail } from "/api/goods/index"; import { ref, onMounted } from "vue"; import { useRoute } from "vue-router"; import { useCar…

2024/2/3学习记录

微信小程序 小程序中组件的分类 视图容器 view 普通视图区域&#xff0c;类似于 div 常用来实现页面的布局效果。 scroll-view 可滚动的视图区域&#xff0c;常用来实现滚动列表效果 swiper 和 swiper-item 常用 swiper 组件的常用属性 轮播图容器组件和轮播图item组件 基…

【CSS】常见样式总结

一. 溢出隐藏 1.1 单行文本溢出 .content{max-width:200px; /* 定义容器最大宽度 */overflow:hidden; /* 隐藏溢出的内容 */text-overflow:ellipsis; /* 溢出部分...表示 */white-space: nowrap; /* 确保文本在一行内显示 */ }问题&#xff1a;display:flex 和 ellipsis 冲…

Win7 Python入手教程(超简单)

前言 因为最近想学习AI&#xff0c;所以准备开始用python&#xff0c;所以为了照顾和我一样用win7且认为网上的教程难以操作的人&#xff0c;所以的算水写一篇博客。 正文 安装步骤&#xff1a; 打开python官网。&#xff08;会有一点慢&#xff0c;要耐心。&#xff09; …

回归预测 | Matlab实现CPO-CNN-LSTM-Attention冠豪猪优化卷积长短期记忆神经网络注意力机制多变量回归预测(SE注意力机制)

回归预测 | Matlab实现CPO-CNN-LSTM-Attention冠豪猪优化卷积长短期记忆神经网络注意力机制多变量回归预测&#xff08;SE注意力机制&#xff09; 目录 回归预测 | Matlab实现CPO-CNN-LSTM-Attention冠豪猪优化卷积长短期记忆神经网络注意力机制多变量回归预测&#xff08;SE注…

C#,哥伦布数(Golomb Number)的算法与源代码

1 哥伦布数&#xff08;Golomb Number&#xff09; 哥伦布数&#xff08;Golomb Number&#xff09;是一个自然数的非减量序列&#xff0c;使得n在序列中正好出现G&#xff08;n&#xff09;次。前几个15的G&#xff08;n&#xff09;值为&#xff1a;1 2 2 3 3 4 4 4 5 5 5 6…

线阵相机参数介绍之帧加行触发

要点&#xff1a; 了解行触发与帧触发的区别 了解线阵相机帧行触发的参数设置 目标&#xff1a; 能够独立进行帧行触发的配置 1.帧、行触发的区别 帧触发控制的是一张图像开始拍摄的位置 行触发控制的是图像中每一行开始拍摄的位置。

极速上手:使用Jmeter轻松实现N种参数化

参数化的方式&#xff1a; 一、使用用户自定义变量 一种方式&#xff1a;直接在测试计划中添加用户自定义变量 另外一种方式&#xff1a;配置元件——用户自定义变量 示例&#xff1a;用户自定义变量&#xff0c;登录手机号码 在接口请求的时候&#xff0c;进行引用 请求之后&…

【iOS ARKit】3D 人体姿态估计

与基于屏幕空间的 2D人体姿态估计不同&#xff0c;3D人体姿态估计是尝试还原人体在三维世界中的形状与姿态&#xff0c;包括深度信息。绝大多数的现有3D人体姿态估计方法依赖2D人体姿态估计&#xff0c;通过获取 2D人体姿态后再构建神经网络算法&#xff0c;实现从 2D 到 3D人体…

极速搭建幻兽帕鲁私服,叫上好友春节假期一起联机畅玩帕鲁

文章目录 前言幻兽帕鲁私服详细部署教程查看服务器开始游戏自定义游戏参数配置 前言 行业资讯 《幻兽帕鲁》的火爆对开发商 Pocketpair 来说&#xff0c;代价是巨大的。该游戏的成功让首席执行官沟部拓郎最近在推特上表示&#xff0c;他可能因服务器运营费用而面临破产。据他透…

Matplotlib绘制炫酷散点图:从二维到三维,再到散点图矩阵的完整指南与实战【第58篇—python:Matplotlib绘制炫酷散点图】

文章目录 Matplotlib绘制炫酷散点图&#xff1a;二维、三维和散点图矩阵的参数说明与实战引言二维散点图三维散点图散点图矩阵二维散点图进阶&#xff1a;辅助线、注释和子图三维散点图进阶&#xff1a;动画效果和交互性散点图矩阵进阶&#xff1a;调整样式和添加密度图总结与展…

Backtrader 文档学习-Indicators- TA-Lib

Backtrader 文档学习-Indicators- TA-Lib 1.概述 即使BT提供的内置指标数量已经很多&#xff0c;开发指标主要是定义输入、输出并以自然方式编写公式&#xff0c;还是希望使用TA-LIB。原因: 指标X在指标库中&#xff0c;而不在BT中TA-LIB众所周知的&#xff0c;人们信任口碑…

远程SSH连接树莓派, SSH反向隧道访问树莓派(使用阿里云服务器以及树莓派4b)

使用SSH反向隧道 由于其没有公网IP地址&#xff0c;那么不在同一个内网的其它电脑就无法直接连接到这台树莓派&#xff0c;这个时候内网穿透技术就可以帮助我们克服这个问题 这里使用ubuntu系统, 树莓派4b, 使用端口8999演示 参考 SSH 反向隧道搭建过程-云社区-华为云 (huawei…

架构篇33:传统的可扩展架构模式-分层架构和SOA

文章目录 分层架构SOA小结相比于高性能、高可用架构模式在最近几十年的迅猛发展来说,可扩展架构模式的发展可以说是步履蹒跚,最近几年火热的微服务模式算是可扩展模式发展历史中为数不多的亮点,但这也导致了现在谈可扩展的时候必谈微服务,甚至微服务架构都成了架构设计的银…

【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程

【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程 文章目录 前言一、引入依赖二、创建数据库连接简单链接连接选项开启日志调试 三、生成实体安装sea-orm-cli创建数据库表使用sea-orm-cli命令生成实体文件代码 四、增删改查实现新增数据主键查找条件查找查找用户名…

99例电气实物接线及52个自动化机械手动图

给大家分享一些流水线设计中常见的一些结构&#xff0c;这些动态图很直观&#xff0c;有助于大家了解其原理&#xff0c;非常好懂。 1.家庭总电箱接线图 2.经典双控灯接线 3.五孔一开接线 4.电动机点动控制接线&#xff08;不安全&#xff09; 5.电动机自锁接线图&#xff08;…

建筑工程答案在哪搜?九个免费好用的大学生搜题工具 #经验分享#知识分享

大学生必备&#xff0c;这条笔记大数据一定定要推给刚上大学的学弟学妹&#xff01;&#xff01; 1.七燕搜题 这是一个公众号 解题步骤详细解析&#xff0c;帮助你理解问题本质。其他考试领域也能找到答案。 下方附上一些测试的试题及答案 1、据《素问太阴阳明论》所论&…