当前位置: 首页 > article >正文

Unity3D 热更新之HybridCLR方案

HybridCLR 是其中一种在 Unity 中实现热更新的技术,它通过在运行时动态加载和卸载 DLL 文件来实现热更新。下面简单闭环是一个详细的实现方案,包括通过:版本配置表对比、从服务器加载热更新 DLL 文件、热更新的效果

1. 环境准备

1.1 安装 HybridCLR 插件

首先,确保你已经在 Unity 项目中安装了 HybridCLR 插件。你可以从 GitHub 或其他资源下载并导入到你的项目中。
GitHub: https://github.com/focus-creative-games/hybridclr.git
Gitee: https://gitee.com/focus-creative-games/hybridclr.git

1.2 配置 HybridCLR

根据 HybridCLR 的文档,配置你的项目以支持 HybridCLR。这通常包括设置编译选项、配置 IL2CPP 等。
官方文档以及教程:https://hybridclr.doc.code-philosophy.com/docs/beginner/quickstart
高手博主教程手把手教我们:https://blog.csdn.net/qq_33805569/article/details/131783857

2. 版本配置表

2.1 创建版本配置表

创建一个版本配置表,用于存储当前版本的 DLL 文件信息。这个配置表可以是一个 JSON 文件,存储在服务器上。

{
  "version": "1.0.0",
  "dlls": [
    { "name": "YourDLL1.dll", "hash": "abc123" },
    { "name": "YourDLL2.dll", "hash": "def456" }
  ]
}

2.2 本地版本配置表

在客户端,也需要一个本地版本配置表,用于记录当前已加载的 DLL 文件信息。

{
  "version": "1.0.0",
  "dlls": [
    { "name": "YourDLL1.dll", "hash": "abc123" },
    { "name": "YourDLL2.dll", "hash": "def456" }
  ]
}

3. 下载和加载 DLL 文件

3.1 创建 DLL 下载管理器

创建一个 DLLDownloader 类,用于从服务器下载最新的 DLL 文件。

// DLLDownloader.cs
using System;
using System.Collections;
using System.IO;
using System.Net;
using UnityEngine;

public class DLLDownloader : MonoBehaviour
{
    public string serverURL = "http://yourserver.com/";
    public string localPath = "Assets/DLLs/";

    private string versionConfigURL;
    private string localVersionConfigPath;

    void Start()
    {
        versionConfigURL = serverURL + "version.json";
        localVersionConfigPath = Path.Combine(Application.persistentDataPath, "local_version.json");//拼接Path路径

        StartCoroutine(CheckForUpdates());
    }

    IEnumerator CheckForUpdates()
    {
		 // 下载服务器版本配置表
		using (UnityWebRequest webRequest = UnityWebRequest.Get(versionConfigURL))
        {
            yield return webRequest.SendWebRequest();

            if (webRequest.result == UnityWebRequest.Result.ConnectionError 
            || webRequest.result == UnityWebRequest.Result.ProtocolError)
            {           
                 Debug.LogError("Failed to download version config: " + webRequest.error);
            }
            else
            {
            
               string serverVersionConfig =webRequest.downloadHandler.text;
	           VersionConfig serverConfig = JsonUtility.FromJson<VersionConfig>(serverVersionConfig);
	            // 读取本地版本配置表
	           string localVersionConfig = File.Exists(localVersionConfigPath) ? File.ReadAllText(localVersionConfigPath) : "{}";
	           VersionConfig localConfig = JsonUtility.FromJson<VersionConfig>(localVersionConfig);
	            // 对比版本
	            if (serverConfig.version != localConfig.version)
	            {
	                // 下载新的DLL文件
	                foreach (var dll in serverConfig.dlls)
	                {
	                    if (!IsDllUpToDate(dll, localConfig))
	                    {
	                        StartCoroutine(DownloadDLL(dll));
	                    }
	                }
	
	                // 更新本地版本配置表
	                File.WriteAllText(localVersionConfigPath, serverVersionConfig);
	            }
	                Debug.Log("Received data: " + data);
            }
        }
    }

    bool IsDllUpToDate(DLLInfo dll, VersionConfig localConfig)
    {
        foreach (var localDll in localConfig.dlls)
        {
            if (localDll.name == dll.name && localDll.hash == dll.hash)
            {
                return true;
            }
        }
        return false;
    }

    IEnumerator DownloadDLL(DLLInfo dll)
    {
      	string dllUrl = serverURL + dll.name;
        string dllPath = Path.Combine(localPath, dll.name);
 		using (UnityWebRequest webRequest = UnityWebRequest.Get(dllUrl))
        {
            yield return webRequest.SendWebRequest();

            if (webRequest.result == UnityWebRequest.Result.ConnectionError 
            || webRequest.result == UnityWebRequest.Result.ProtocolError)
            {
               Debug.LogError("Failed to download DLL: " + dll.name + " Error: " + webRequest.error);
            }
            else
            {
                byte[] bytes = webRequest.downloadHandler.data;
                File.WriteAllBytes(dllPath, bytes);
                Debug.Log("Download completed: " + savePath);
            }
        }
    }
}
//版本
[Serializable]
public class VersionConfig
{
    public string version;
    public DLLInfo[] dlls;
}
//DLL信息
[Serializable]
public class DLLInfo
{
    public string name;
    public string hash;
}

4. 动态加载 DLL 文件

4.1 扩展 DLLLoader 类

扩展 DLLLoader 类,使其能够动态加载从服务器下载的 DLL 文件。

// DLLLoader.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEngine;

public class DLLLoader : MonoBehaviour
{
    private Dictionary<string, Assembly> _assemblies = new Dictionary<string, Assembly>();
    private Dictionary<string, IModel> _models = new Dictionary<string, IModel>();

    public void LoadDLL(string dllPath)
    {
        if (!File.Exists(dllPath))
        {
            Debug.LogError("DLL not found at path: " + dllPath);
            return;
        }

        try
        {
            string dllName = Path.GetFileNameWithoutExtension(dllPath);
            Assembly assembly = Assembly.LoadFile(dllPath);
            _assemblies[dllName] = assembly;

            Type modelType = assembly.GetType(dllName + ".UserModel");
            if (modelType == null)
            {
                Debug.LogError("Type not found in DLL: " + dllName + ".UserModel");
                return;
            }

            IModel model = (IModel)Activator.CreateInstance(modelType);
            _models[dllName] = model;
        }
        catch (Exception e)
        {
            Debug.LogError("Error loading DLL: " + e.Message);
        }
    }

    public IModel GetModel(string dllName)
    {
        if (_models.ContainsKey(dllName))
        {
            return _models[dllName];
        }
        else
        {
            Debug.LogError("Model not found for DLL: " + dllName);
            return null;
        }
    }

    public void UnloadDLL(string dllName)
    {
        if (_assemblies.ContainsKey(dllName))
        {
            AppDomain.CurrentDomainUnload -= OnAppDomainUnload;
            AppDomain.CurrentDomainUnload += OnAppDomainUnload;

            _assemblies.Remove(dllName);
            _models.Remove(dllName);
        }
        else
        {
            Debug.LogError("DLL not found: " + dllName);
        }
    }

    private void OnAppDomainUnload(object sender, EventArgs e)
    {
        // Perform any cleanup if needed
    }
}

5. 在 Unity 中使用

5.1 创建 DLLLoader 实例

在 Unity 中创建一个空的 GameObject,并添加 DLLLoader 组件。

5.2 初始化控制器

在控制器中初始化模型和视图,并提供加载和卸载 DLL 的方法。

// MainController.cs
using UnityEngine;

public class MainController : MonoBehaviour
{
    public UserController userController;
    public DLLLoader dllLoader;
    public UserView userView;
    public DLLDownloader dllDownloader;

    void Start()
    {
        // 启动DLL下载管理器
        dllDownloader.Start();

        // 假设已经下载了最新的DLL文件
        LoadDLL("Assets/DLLs/YourDLL1.dll");
        LoadDLL("Assets/DLLs/YourDLL2.dll");

        IModel model1 = dllLoader.GetModel("YourDLL1");
        IModel model2 = dllLoader.GetModel("YourDLL2");

        userController.Initialize(model1, userView);

        // 模拟数据更新
        InvokeRepeating("UpdateData", 2f, 2f);
    }

    void UpdateData()
    {
        userController.SetData("New Data " + Time.time);
    }

    public void LoadDLL(string dllPath)
    {
        dllLoader.LoadDLL(dllPath);
    }

    public void UnloadDLL(string dllName)
    {
        dllLoader.UnloadDLL(dllName);
    }
}

6. 测试和验证

6.1 加载多个 DLL

在 Start 方法中,调用 LoadDLL 方法加载多个 DLL 文件。

void Start()
{
    // 启动DLL下载管理器
    dllDownloader.Start();

    // 假设已经下载了最新的DLL文件
    LoadDLL("Assets/DLLs/YourDLL1.dll");
    LoadDLL("Assets/DLLs/YourDLL2.dll");

    IModel model1 = dllLoader.GetModel("YourDLL1");
    IModel model2 = dllLoader.GetModel("YourDLL2");

    userController.Initialize(model1, userView);

    // 模拟数据更新
    InvokeRepeating("UpdateData", 2f, 2f);
}

6.2 动态卸载 DLL

在运行时,可以通过调用 UnloadDLL 方法卸载不再需要的 DLL 文件。

public void UnloadDLL(string dllName)
{
    dllLoader.UnloadDLL(dllName);
}

// 调用卸载方法
UnloadDLL("YourDLL1");

7. 注意事项(目前只是简单说闭环了一下)

  1. 路径问题:确保 DLL 文件的路径正确,特别是在不同平台(如 Windows 和 Android)上。(这个后续再继续聊聊)
  2. 异常处理:在加载和卸载 DLL 时,添加适当的异常处理机制,确保程序的稳定性。
  3. 资源释放:在卸载 DLL 时,确保释放所有相关的资源,避免内存泄漏。
  4. AppDomain 管理:使用 AppDomain 机制可以帮助更好地管理动态加载的 DLL 文件,但需要注意跨 AppDomain 的通信问题。(这个后续再继续聊聊)

8 . 扩展一下

  1. 多 DLL 支持:可以进一步扩展 DLLLoader 类,支持更复杂的多 DLL 管理,例如按需加载和卸载。
  2. 动态卸载:实现更精细的动态卸载策略,例如定时检查不再使用的 DLL 文件并自动卸载。
  3. 利用Unity官方最新HTTP请求UnityWebRequest (另外博客讲解Unity官方最新HTTP请求UnityWebRequest )

通过以上步骤,你可以在 Unity 中实现 HybridCLR 方案的热更新,并通过版本配置表对比从服务器加载热更新 DLL 文件。


http://www.kler.cn/a/428681.html

相关文章:

  • 编译chromium笔记
  • 【Day23 LeetCode】贪心算法题
  • 【云原生布道系列】第三篇:“软”饭“硬”吃的计算
  • apisix的authz-casbin
  • 解决用 rm 报bash: /usr/bin/rm: Argument list too long错
  • 项目开发实践——基于SpringBoot+Vue3实现的在线考试系统(七)
  • PT8M2102 触控型 8Bit MCU
  • SQL中的通配符:使用LIKE操作符进行模式匹配
  • 大数据治理:构建数据驱动决策的基石
  • ModelArts Standard的WebSocket在线服务全流程开发
  • [Java]项目入门
  • 梧桐数据库半结构化json数据入库及解析
  • 深度学习中注意力机制介绍及seq2seq案例
  • Matlab自学笔记四十四:使用dateshift函数生成日期时间型序列数据
  • 58 基于 单片机的温湿度、光照、电压、电流检测
  • 解决跨域问题方案
  • 高级java每日一道面试题-2024年12月05日-JVM篇-什么是空闲列表?
  • vue中this指针获取不到函数或数据
  • Vue 鼠标滚轮缩放图片的实现
  • Kubernetes 常用操作大全:全面掌握 K8s 基础与进阶命令
  • 基于 Spring Boot + Vue 的宠物领养系统设计与实现
  • Java 初学者的第一个 SpringBoot 登录系统
  • CT中的2D、MPR、VR渲染、高级临床功能
  • 鸿蒙技术分享:❓❓[鸿蒙应用开发]怎么更好的管理模块生命周期?
  • 论文研读|信息科技风险管理模型的主要内容、定位、目标企业、风险管理机制, 以及相应的风险评估流程和风险应对策略
  • Spring Boot中实现JPA多数据源配置指南