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 (静态服务器)

相关推荐
心前阳光2 小时前
Unity WebGL文本输入
unity·游戏引擎·webgl
天涯过客TYGK4 小时前
unity A星寻路
unity·游戏引擎
KhalilRuan4 小时前
Unity Demo——3D平台跳跃游戏笔记
笔记·游戏·unity·游戏引擎
ttod_qzstudio1 天前
Unity中使用EzySlice实现模型切割与UV控制完全指南
unity
南無忘码至尊1 天前
Unity 实现与 Ollama API 交互的实时流式响应处理
unity·游戏引擎·交互
平行云1 天前
如何实现UE程序大并发多集群的像素流部署
unity·ue5·图形渲染
向宇it2 天前
【unity小技巧】在 Unity 中将 2D 精灵添加到 3D 游戏中,并实现阴影投射效果,实现类《八分旅人》《饥荒》等等的2.5D游戏效果
游戏·3d·unity·编辑器·游戏引擎·材质
向宇it2 天前
Unity Universal Render Pipeline/Lit光照材质介绍
游戏·unity·c#·游戏引擎·材质
__water3 天前
RHA《Unity兼容AndroidStudio打Apk包》
android·unity·jdk·游戏引擎·sdk·打包·androidstudio
两水先木示3 天前
【Unity3D】微信小游戏适配安全区域或胶囊控件(圆圈按钮)水平高度一致方案
unity·微信小游戏·安全区域·ui适配·胶囊控件·safearea