华佗AB打包预制加载

该代码实现了一个Unity中的AssetBundle管理类ABManager,用于加载和管理AssetBundle资源,包括从内存加载、按需加载、引用计数以及资源释放。同时,提供了一个ABTool编辑器扩展,用于构建AssetBundle包和生成版本信息。ABTool支持从特定目录获取dll和预制体,打包成AB包,并生成版本信息文件供热更新使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
在这里插入图片描述

using Script;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

public class ABData
{
    public AssetBundle ab;
    public int num;
}
public class ABManager :Singleton<ABManager>
{
    public string SPath = Application.streamingAssetsPath + "/";
    public Dictionary<string, byte[]> assetDatas = new Dictionary<string, byte[]>();
    public Dictionary<string, List<string>> reflist = new Dictionary<string, List<string>>();
    public  Dictionary<string, ABData> abDic = new Dictionary<string,ABData>();
    public byte[] GetAssetData(string dllName)
    {
        return assetDatas[dllName];
    }
    public AssetBundle LoadAB(string dllName)
    {
        return AssetBundle.LoadFromMemory(GetAssetData(dllName));
    }
    public T Load<T>(string refname,string abName,string assetName) where T : UnityEngine.Object
    {
        AssetBundle ab;
        if (abDic.ContainsKey(abName))
        {
            ab = abDic[abName].ab;
            abDic[abName].num++;
        }
        else
        {
            ab = AssetBundle.LoadFromFile(SPath + abName);
            ABData data = new ABData();
            data.ab = ab;
            data.num = 1;
            abDic.Add(abName, data);
        }
        if (ab != null)
        {
            if (!reflist.ContainsKey(refname))
            {
                reflist.Add(refname, new List<string>());
            }
            reflist[refname].Add(abName);
        }
        return ab.LoadAsset<T>(assetName);
    }
    public GameObject Load(string abName,string assetName)
    {
        AssetBundle ab;
        if (abDic.ContainsKey(abName))
        {
            ab = abDic[abName].ab;
            abDic[abName].num++;
        }
        else
        {
            ab = AssetBundle.LoadFromFile(SPath + abName);
            ABData data = new ABData();
            data.ab = ab;
            data.num = 1;
            abDic.Add(abName, data);
        }
        if (ab != null)
        {
            if (!reflist.ContainsKey(assetName))
            {
                reflist.Add(assetName, new List<string>());
            }
            reflist[assetName].Add(abName);
        }
        return ab.LoadAsset<GameObject>(assetName);
    }
    public Type LoadScript(string abName,string assetName)
    {
        Assembly ass = Assembly.Load(GetAssetData(abName));
        return ass.GetType(assetName);
    }
    public void Release(string refname)
    {
        if (reflist.ContainsKey(refname))
        {
            foreach (var abName in reflist[refname])
            {
                if (abDic.ContainsKey(abName))
                {
                    abDic[abName].num--;
                    if (abDic[abName].num <= 0)
                    {
                        ScheduleOnce.Ins.AddSchedule(9, (obj) =>
                        {
                            object[] arr = obj as object[];
                            ABData data = arr[0] as ABData;
                            if (data.num <= 0)
                            {
                                data.ab.Unload(true);
                                abDic.Remove(abName);
                                Debug.Log("释放成功");
                            }
                        }, abDic[abName]);
                    }
                }
            }
            reflist.Remove(refname);
        }
    }
}

using System.Collections.Generic;
using System.IO;
using System.Linq;
using HybridCLR.Editor;
using UnityEditor;
using UnityEngine;
using System.Text;
using System.Security.Cryptography;
using Unity.Plastic.Newtonsoft.Json;

public class ABTool : Editor
{
    static string dllpath = Application.dataPath + "/../" + HybridCLRSettings.Instance.hotUpdateDllCompileOutputRootDir;

    //[MenuItem("ABTool/生成AB包/Android")]
    //public static void CreatAB_Android()
    //{
    //    var target = BuildTarget.Android;
    //    CreatAB(target);
    //}

    [MenuItem("ABTool/生成AB包(Windows64)")]
    public static void CreateAB()
    {
        BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
        //找到对应目录
        DirectoryInfo direction = new DirectoryInfo(dllpath + "/" + target);
        //获取目录下所有文件
        FileInfo[] files = direction.GetFiles("*", SearchOption.AllDirectories);
        //把所有dll文件拷贝到S目录下
        foreach (var item in files)
        {
            if (item.Name.EndsWith(".dll"))
            {
                string dllPath = $"{dllpath + "/" + target}/{item.Name}";
                string dllBytesPath = $"{Application.streamingAssetsPath}/{item.Name}.bytes";
                File.Copy(dllPath, dllBytesPath, true);
                Debug.Log($"hotfix dll {dllPath}->{dllBytesPath}");
            }
        }
        //预制体
        List<AssetBundleBuild> abs = new List<AssetBundleBuild>();
        //找到预制体目录下所有目录
        AddABPath(abs, $"{Application.dataPath}/Prefabs/");
        AddABPath(abs, $"{Application.dataPath}/Resources/");
        //构造AB包放到S目录下
        BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, abs.ToArray(), BuildAssetBundleOptions.None, target);
        AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
    }
    public static void AddABPath(List<AssetBundleBuild> abs, string path)
    {
        DirectoryInfo directoryInfo = new DirectoryInfo(path);
        DirectoryInfo[] directions = directoryInfo.GetDirectories();
        //每个目录打一个AB包
        for (int i = 0; i < directions.Length; i++)
        {
            List<string> prefabAbssets = new List<string>();
            Debug.Log("打包文件夹:" + directions[i].Name);
            FileInfo[] fileinfos = directions[i].GetFiles("*", SearchOption.AllDirectories);
            Debug.Log("目录下文件个数:" + fileinfos.Length);
            foreach (var item in fileinfos)
            {
                if (item.Name.EndsWith(".meta"))
                {
                    continue;
                }
                Debug.Log("打包文件:" + item.Name);
                prefabAbssets.Add(item.FullName.Replace("\\", "/"));
                AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
            }

            AssetBundleBuild PrefabsAb = new AssetBundleBuild
            {
                assetBundleName = directions[i].Name,
                assetNames = prefabAbssets.Select(s => ToReleateAssetPath(s)).ToArray(),
            };
            abs.Add(PrefabsAb);
        }
    }
    public static string ToReleateAssetPath(string s)
    {
        string path = s.Substring(s.IndexOf("Assets/"));
        Debug.Log(path);
        return path;
    }
    [MenuItem("ABTool/生产version")]
    public static void MakeVersion()
    {
        string json = AssetDatabase.LoadAssetAtPath<TextAsset>("Assets/Resources/Config/Config.json").text;
        Config config = JsonConvert.DeserializeObject<Config>(json);
        List<VersionData> versionDatas = new List<VersionData>();
        //第一条数据是假数据,用来判断是否需要热更新
        VersionData version = new VersionData();
        //判断使用测试版还是正式版热更新地址

        version.abName = config.isBate ? config.hotPath_Bate : config.hotPath;
        version.len = config.upid;//更新号
        version.md5 = config.version;//版本号
        versionDatas.Add(version);
        //找到S目录下所有文件
        string[] files = Directory.GetFiles(Application.streamingAssetsPath, ".", SearchOption.AllDirectories);
        foreach (var item in files)
        {
            //获取后缀名
            string extensionName = Path.GetExtension(item);
            //排除.meta和manifest文件
            if (extensionName.Contains("meta") || extensionName.Contains("manifest")) continue;
            Debug.Log(item);
            string abName = item.Replace(Application.dataPath, "Assets");
            abName = abName.Replace("\\", "/");
            string assetBundleNamee = "";
            assetBundleNamee = abName.Replace("Assets/StreamingAssets/", string.Empty);
            VersionData versionData = new VersionData();
            versionData.abName = assetBundleNamee;
            versionData.len = File.ReadAllBytes(item).Length;
            versionData.md5 = FileMD5(item);
            versionDatas.Add(versionData);
        }
        string versionInfo = JsonConvert.SerializeObject(versionDatas);
        File.WriteAllText(Application.streamingAssetsPath + "/version.text", versionInfo, Encoding.UTF8);
        AssetDatabase.Refresh();
        Debug.Log("version.text创建完成");
    }
    static string FileMD5(string filepath)
    {
        FileStream fileStream = new FileStream(filepath, FileMode.Open);
        MD5 mD5 = new MD5CryptoServiceProvider();
        byte[] bytes = mD5.ComputeHash(fileStream);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.Length; i++)
        {
            sb.Append(bytes[i].ToString("x2"));
        }
        return sb.ToString();
    }
}

在Resources下创建一个json文件

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Config : MonoBehaviour
{
    public bool isEditor;
    public bool isBate;
    public string version;
    public int upid;
    public string hotPath;
    public string hotPath_Bate;
    public string host;
    public int port;
    public string host_Bate;
    public int port_Bate;
}

在这里插入图片描述

### 华佗热更新概述 华佗热更新是一种能够动态替换应用程序中的代码的技术,适用于游戏或其他高性能需求的应用场景。它允许开发者在不重启应用的情况下更新部分功能模块或修复错误[^1]。 #### 热更新实现原理 华佗热更新的核心在于其支持动态加载和执行新版本的程序集(Assembly)。具体来说,这种技术依赖于C#的反射机制以及.NET平台上的动态链接库(DLL)加载能力来完成热更新操作。当新的DLL文件被上传到指定目录后,系统会卸载旧版DLL并加载新版DLL,在此过程中不会中断整个应用的运行状态[^2]。 #### 开发环境搭建与测试流程 为了成功实施基于华佗框架的热更新方案,需按照如下指南准备开发环境: 1. **配置项目结构** 需要将工程划分为基础架构层(不可热更的部分)、业务逻辑层(可热更的部分),其中后者会被编译成独立的DLL供后续热更新使用。 2. **编写热更新入口类** 创建类似于`HotUpdateEntry`这样的类作为热更新的主要启动点,该类应含至少一个公共静态方法用于初始化热更新后的逻辑处理。 ```csharp class HotUpdateEntry { public static void Main() { UnityEngine.Debug.Log("hello, HybridCLR"); } } ``` 3. **集成至现有引擎** 将上述组件整合进目标游戏或者软件产品之中,并确保能够在适当时候调用这些新增加的功能接口。 4. **验证效果** 完成部署之后,可通过实际模拟不同情况下的变更请求来进行全方位的压力测试,从而确认整体解决方案的有效性和稳定性。 ### 注意事项 尽管华佗提供了强大的灵活性,但在设计阶段仍需要注意一些潜在风险因素,比如内存泄漏问题、资源竞争条件等可能会影响用户体验甚至导致崩溃事故的发生;因此建议始终遵循最佳实践原则进行编码工作的同时也要加强监控力度以便及时发现异常状况加以解决。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值