本文章会手把手教大家如何搭建HybridCLR+Adressable+Springboot热更。
创作不易,动动发财的小手点个赞。
安装华佗
首先我们按照官网的快速上手指南搭建一个简易的项目:
快速上手 | HybridCLR
注意在热更的代码里添加程序集。把用到的工具放到程序集里。
local程序集:这个程序集不热更,跟游戏一起打包:
注意:不能把热更的代码放到local程序集里,local程序集只能调用非热更代码。
安装Adressable:
然后开始配置Adressable:
系统配置,没什么需要强调的,根据需求点。
注意,我是用的是自己的config动态修改打包的位置,配置文件在下面:
自定义远端:
使用host(看后面): 这个也是根据需求点就行。
如果你没有自己的服务器,可以使用Addressable自带的host工具(注意修改配置文件里的信息):
Addressable和工具的config文件:
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场景里用代码启动游戏启动逻辑。
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 (静态服务器)