前言
在使用HybridCLR(华佗)热更新方案配合Addressable资源管理系统时,很多开发者会遇到这样一个问题:场景文件没有放进Addressable组,但场景中存在静态物体挂载了热更脚本,打包之后运行就会报Script Missing错误。这个问题困扰了不少人,本文将从原理层面分析问题成因,并提供三种经过验证的解决方案。
现象描述:
场景文件未标记为Addressable,即保留在Build Settings中
场景中的某个GameObject挂载了热更新脚本,即HybridCLR热更模块中的脚本
打包后运行,该脚本显示为Script Missing,功能失效
为什么会出现这个问题?这个问题本质上涉及到Unity的资源打包机制与HybridCLR热更新机制之间的冲突。
原理分析
1. 非Addressable场景的打包机制
根据Unity官方文档,当场景文件没有标记为Addressable时,它会被打包进内置的Scene数据存档中:
“Assets you add directly to a Scene or to a component in a Scene, which the application loads automatically. Unity packages serialized scene data and the assets directly referenced by a scene into a single archive that it includes in your built player application.”
这意味着场景中所有直接引用的资源(包括挂载在GameObject上的脚本)都会被序列化到主程序包中。
2. HybridCLR热更脚本的特殊性
HybridCLR热更新脚本存在于热更新程序集中(如HotUpdate.dll),这些程序集是在运行时通过Assembly.Load动态加载的,不包含在主程序包中 。
3. 冲突点
当Unity打包非Addressable场景时,会记录场景中挂载的脚本元数据。此时热更脚本的元数据指向的是尚未加载的热更新程序集。运行时的加载顺序是:
主程序启动,加载非Addressable场景
Unity尝试解析场景中挂载的脚本
此时热更新程序集尚未加载,脚本无法解析 → Script Missing
而如果场景本身是Addressable的,情况则不同——Addressable系统会在加载场景时才解析脚本引用,此时热更新程序集已经加载完毕 。
解决方案
方案一:动态挂载热更脚本
核心思路:场景中不直接挂载热更脚本,而是在运行时通过代码动态添加。
using UnityEngine;
using System.Reflection;
public class DynamicScriptLoader : MonoBehaviour
{
void Start()
{
// 确保先加载热更新DLL
Assembly hotUpdateAssembly = Assembly.Load(File.ReadAllBytes(
$"{Application.streamingAssetsPath}/HotUpdate.dll.bytes"));
// 动态获取类型并添加组件
Type printType = hotUpdateAssembly.GetType("Print");
gameObject.AddComponent(printType);
}
}
方案二:将静态物体改为预制体动态生成
核心思路:将场景中需要挂载热更脚本的物体做成预制体,并标记为Addressable,在运行时动态实例化。
步骤:
将场景中的物体转换为预制体(Prefab)
将预制体标记为Addressable
在场景中移除该物体
运行时通过Addressables API加载并实例化:
// 加载并实例化预制体
AsyncOperationHandle<GameObject> handle = Addressables.InstantiateAsync("your_prefab_address");
await handle.Task;
GameObject instance = handle.Result;
关于动态预制体的使用,Unity官方示例中有详细说明:
“This sample leverages Addressables to load dynamic Prefabs.”
方案三:将场景文件标记为Addressable
核心思路:将场景本身加入Addressable组,使用Addressable API加载场景。
步骤:
在Build Settings中保留一个最小的初始化场景(非Addressable)
将其他所有场景标记为Addressable
在初始化场景中使用Addressables加载主场景:
// 加载Addressable场景
AsyncOperationHandle<SceneInstance> handle = Addressables.LoadSceneAsync("main_scene_address", LoadSceneMode.Single);
await handle.Task;
Unity官方文档明确指出这是“将项目集成Addressables最简单的方法”:
“The easiest way to integrate Addressables into a project is to move your Scenes out of the Build Settings list and make those scenes Addressable.”
Unity官方文档在Using Addressable assets in non-Addressable Scenes一节中明确指出:
“You cannot use Addressable assets for the fields of any UnityEngine components in a non-Addressable Scene.”
这意味着在非Addressable场景中,Unity组件字段不能使用Addressable资源——这里的“资源”同样适用于需要动态加载的脚本类型。
此外,关于HybridCLR的使用,官方文档强调:
“资源上挂载的热更新脚本可以正确实例化,这是其他所有热更新方案都不支持的”
但这需要正确配置资源的打包方式,确保脚本引用在运行时能够被正确解析。
总结
“场景未标记Addressable + 静态物体挂载热更脚本”导致Script Missing的根本原因,在于Unity打包时对脚本引用的静态解析与HybridCLR运行时动态加载之间的时序矛盾。
参考链接
Unity官方文档 - Upgrading to the Addressables system: https://docs.unity.cn/Packages/[email protected]/manual/AddressableAssetsMigrationGuide.html
Unity官方文档 - Addressable Assets: https://docs.unity3d.com/cn/2020.2/Manual/com.unity.addressables.html
Unity UOS文档 - CDN + HybridCLR: https://uos.unity.cn/docs/cdn/hybrid-clr.html
转载自CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/gbdsbgghj/article/details/159514638



