HybridCLR+Adressable+Springboot热更

本文章会手把手教大家如何搭建HybridCLR+Adressable+Springboot热更。

创作不易,动动发财的小手点个赞。

安装华佗

首先我们按照官网的快速上手指南搭建一个简易的项目:

快速上手 | HybridCLR

注意在热更的代码里添加程序集。把用到的工具放到程序集里。

local程序集:这个程序集不热更,跟游戏一起打包:

注意:不能把热更的代码放到local程序集里,local程序集只能调用非热更代码。

安装Adressable:

然后开始配置Adressable:

系统配置,没什么需要强调的,根据需求点。

注意,我是用的是自己的config动态修改打包的位置,配置文件在下面:

自定义远端:

使用host(看后面): 这个也是根据需求点就行。

如果你没有自己的服务器,可以使用Addressable自带的host工具(注意修改配置文件里的信息):

Addressable和工具的config文件:

cs 复制代码
public  class FrameworkConfig
{
   public static string DownLoadPath = "D:/Desktop/local/test";//打包后,Adressable缓存地址(外部{}引用)
   public static string RemotePath = "http://47.xxx.43.98/files/";//Adressable的服务器地址(外部{}引用)
   public static string BaseUrl = "http://47.xxx.43.98/";
   public static string UploadPath = "http://47.xxx.43.98/upload";//打好的Addressable包的上传的地址
   public static string DeletePath = "http://47.xxx.43.98/files";//删除服务器远端仓库的请求地址
   public static string LoginPath = "http://47.xxx.43.98/login";//登录服务器远端仓库的请求地址
   public static string LogoutPath = "http://47.xxx.43.98/logout";//登出服务器远端仓库的请求地址
   public static string PackPath=@"D:\GameClient\game-client\client\ServerData\StandaloneWindows64";//打好的本地Addressable包的地址
   //   public static string RemoteBuildPath = "ServerData/[BuildTarget]";Build地址需要在Addressable里改
   public static string DLLName = "HotUpdate.dll.bytes";//热更dll在group中的索引
   public static string StartSceneName="Assets/HotUpdate/Scenes/StartScene.unity";//更新后启动场景的group中的索引
   public static string DLLPath = @"../HybridCLRData/HotUpdateDlls/StandaloneWindows64/HotUpdate.dll";//热更dll打包后迁移前的位置
   public static string NewDLLPath = "HotUpdate/Dlls";//热更dll打包后迁移后的位置
   public static string LevelJsonPosition = "D:\\Desktop\\local\\pos.json"; //地图编辑器生成的地图文件的地址
   
}

Q:为什么ip后面还有,A:因为Springboot服务器的http请求需要把写入删除拉取区分。

热更打包,注意把左上角的profile改成自己的(我用的是remote,默认是defaut),给每个包打上标签(更新使用)

热更逻辑:

我们的代码热更方式就是:用Hybrid打出一个热更的dll,然后把dll转存为比特文件,放到Addressable包里,热更到本地后加载新的dll。

启动逻辑:build一个场景,里面放CheckAssetsUpdate 脚本,在所有包体下载完成后,加载包中的StartScene场景。startScene场景里用代码启动游戏启动逻辑。

cs 复制代码
using HybridCLR;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
using static UnityEngine.Rendering.VirtualTexturing.Debugging;

public class CheckAssetsUpdate : MonoBehaviour
{
    private AsyncOperationHandle<long> downloadHandle;
    AsyncOperationHandle remote;
    private StaticLoadingPage loadPage;

    void Start()
    {

        LoadDefDLL();
        StartCoroutine(CheckUpdate());
        loadPage=GetComponent<StaticLoadingPage>();
    }
    private void LoadDefDLL()
    {
        //����dll
        Debug.Log("Starting to check and download assets with label: all");
        List<string> aotDllList = new List<string>
        {
            "System.Core.dll",
            "System.dll",
            "Unity.Addressables.dll",
            "Unity.ResourceManager.dll",
            "UnityEngine.CoreModule.dll",
            "mscorlib.dll",
        };
        foreach (var dllName in aotDllList)
        {
            byte[] dllBytes = File.ReadAllBytes($"{Application.streamingAssetsPath}/{dllName}");
            LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, HomologousImageMode.SuperSet);
            if (err != LoadImageErrorCode.OK)
            {
                Debug.LogError($"Failed to load AOT DLL: {dllName}, Error: {err}");
                // If any AOT DLL fails to load, stop the process
            }
            else
            {
                Debug.Log($"{dllName} 加载成功");
            }
        }
    }
    private IEnumerator CheckUpdate()
    {

        downloadHandle = Addressables.GetDownloadSizeAsync("all");
        //Debug.Log("加载"+ downloadHandle);
        yield return downloadHandle;
        Debug.Log("检查下载资源");
        if (downloadHandle.Status == AsyncOperationStatus.Succeeded)
        {
            if (downloadHandle.Result <= 0)
            {
                Debug.Log("没有更新");
                EnterGame();
            }
            else
            {
                Debug.Log("更新游戏");
                StartCoroutine(Download());
           
            }
        }
        yield return null;
    }

    IEnumerator Download()
    {
        remote = Addressables.DownloadDependenciesAsync("all", true);
        while (!remote.IsDone)
        {
            var bytes = remote.GetDownloadStatus().DownloadedBytes;
            var totalBytes = remote.GetDownloadStatus().TotalBytes;
            var status = remote.GetDownloadStatus();
            float progress = status.Percent;
            Debug.Log($"Download progress : {progress}");
            loadPage.Loading(progress);
            yield return null;
        }

        EnterGame();
    }
    void EnterGame()
    {
 
        Debug.Log("加载了:HotUpdate.dll"+ remote);
        var loadDllAsync = Addressables.LoadAssetAsync<TextAsset>(FrameworkConfig.DLLName);
        loadDllAsync.Completed += OnHotUpdateDllLoaded;
    }

    void OnHotUpdateDllLoaded(AsyncOperationHandle<TextAsset> handle)
    {
        if (handle.Status == AsyncOperationStatus.Succeeded)
        {
           
            Debug.Log("DLL 加载完毕");

            Assembly hotUpdate = null;
            try
            {
                hotUpdate = Assembly.Load(handle.Result.bytes);
                Debug.Log("加载游戏");

                //GameRoot.Instance.Init();
                AsyncOperationHandle<SceneInstance> lastHandle= Addressables.LoadSceneAsync(FrameworkConfig.StartSceneName, LoadSceneMode.Single);
                lastHandle.Completed += (o) =>
                {
                    loadPage.Loading(1);
                    Destroy(loadPage.loadingCanvas.gameObject,2);

                };
            }
            catch (Exception ex)
            {
                Debug.LogError("DLL加载错误: " + ex.Message);
                return;
            }
      
            
   
        }

   
    }
}

报错解决文档

专门记录一些坑,遇到报错问题可以来这里解决:

【有道云笔记】HybridCLR+Addressables热更

https://note.youdao.com/s/2QhPpppU

或者去官网。

源码:

larito/GameClient (客户端)

larito/StaticServer (静态服务器)

相关推荐
小贺儿开发11 小时前
Unity VideoPlayer 播放控制器
unity·编辑器·播放器·视频·工具·videoplayer·互动
め.18 小时前
UIFramework
ui·unity
℡枫叶℡1 天前
Unity - Import Activity Window 资源导入诊断信息窗口
unity·资源导入诊断
TO_ZRG1 天前
Unity 证书校验
unity·游戏引擎
mxwin1 天前
Unity Shader 切线空间数据是如何计算出来的
unity·游戏引擎·shader
mxwin1 天前
Unity Shader 法线贴图跟切线空间有什么关系
unity·游戏引擎·贴图·shader
mxwin1 天前
Unity Shader 贴图和采样的关系 如何保证贴图清晰
unity·游戏引擎·贴图·shader
心前阳光2 天前
Unity之使用火山引擎实现文字提问流式回复
unity·游戏引擎·火山引擎
mxwin2 天前
Unity Shader 什么是球谐光照 原理是什么
unity·游戏引擎·shader