Unity 3D模型展示框架篇之Addressables+ILRuntime热更(完结篇)

Posted yxlalm

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity 3D模型展示框架篇之Addressables+ILRuntime热更(完结篇)相关的知识,希望对你有一定的参考价值。

系列文章目录

Unity 3D模型展示框架篇之项目整理
Unity 3D模型展示框架篇之框架运用
Unity 3D模型展示框架篇之自由观察(Cinemachine)
Unity 3D模型展示框架篇之资源打包、加载、热更(Addressable Asset System | 简称AA)
Unity 3D模型展示框架篇之资源打包、加载、热更(二)
Unity 3D模型展示框架篇之ILRuntime快速入门
Unity 3D模型展示框架篇之ILRuntime整合与应用


文章目录


前言

本项目将整合之前Unity程序基础小框架专栏在Unity3D模型展示项目基础上进行整合,并记录了集成过程中对原脚本的调整过程。增加了Asset Bundle+ILRuntime热更新技术流程。


本文章主要介绍如何在Unity工程中使用Addressable与ILRuntime项目进行整合的过程与使用实例。

一、热更新为什么要Addressables+ILRuntime?

ILRuntime项目为基于C#的平台(例如Unity)提供了一个纯C#实现,快速、方便且可靠的IL运行时,使得能够在不支持JIT的硬件环境(如ios)能够实现代码的热更新。
Addressables是官方推出的可寻址资产系统提供了一种通过"地址"加载资产的简单方法。它通过简化内容包的创建和部署来处理资产管理开销。可寻址资产系统使用异步加载来支持从任何位置加载任何依赖项集合。无论您使用直接引用、传统的资产包还是ResourceFolders进行资产管理,可寻址资产都提供了一种更简单的方法来使您的游戏更加动态。什么是资产?资产是您用来创建游戏或应用程序的内容。资产的常见例子包括预制、纹理、材料、音频剪辑和动画。什么是可寻址资产?将资产设置为“可寻址”允许您使用该资产的唯一地址在任何地方调用它。

简单来说ILRuntime+Addressables涵盖了热更新中的代码更新与资源热更新。

ILRuntime详见官方文档
Addressables官方文档

二、整合步骤

由于Addressables不支持DLL资源打包,因此需要将DLL文件转化为二进制文件,再通过Addressables进行打包进行资源热更后,加载时通过DLL文件流初始化ILRuntime的入口应用程序域AppDomain。完成对ILRuntime代码工程的更新。

1.ILRuntime工程打包DLL转化为二进制文件

  • 修改ILRuntime热更工程的DLL输出目录
    修改目录的原因是因为StreamingAssets在进行Addressables分组时报错。因此创建Assets/ILRunTimeAndAddressable/AssetsPackage/HotFixDllAssets/ILRunTimeAndAddressable/AssetsPackage/HotFixDllToBytes,HotFixDll为生成DLL文件目录,HotFixDllToBytes为DLL转化二进制文件的存放目录。

  • 创建DLL转二进制文件编辑器
    在Editor文件夹中创建DllToBytes.cs

DllToBytes.cs代码如下:


using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;


public class DllToBytes 

    public static string normalPath = Application.dataPath + "/ILRunTimeAndAddressable/AssetsPackage/HotFixDll";
    public static string normalPathToSave = Application.dataPath + "/ILRunTimeAndAddressable/AssetsPackage/HotFixDllToBytes";
    [MenuItem("MyMenu/ILRuntime/DllToByte")]
    public static void DllToByte()
    
        DllToByte(true);
    
    [MenuItem("MyMenu/ILRuntime/DllToByteChoose")]
    public static void DllToByteChoose()
    
        DllToByte(false);
    

    private static void DllToByte(bool autoChoosePath)
    
        string folderPath, savePath;
        if (autoChoosePath)
        
            folderPath = normalPath;
        
        else
        
            folderPath = EditorUtility.OpenFolderPanel("dll所在的文件夹", Application.dataPath + "/addressable/IlRuntime", string.Empty);
        

        if (string.IsNullOrEmpty(folderPath))
        
            return;
        
        DirectoryInfo directoryInfo = new DirectoryInfo(folderPath);
        FileInfo[] fileInfos = directoryInfo.GetFiles();
        List<FileInfo> listDll = new List<FileInfo>();
        List<FileInfo> listPdb = new List<FileInfo>();

        for (int i = 0; i < fileInfos.Length; i++)
        
            if (fileInfos[i].Extension == ".dll")
            
                listDll.Add(fileInfos[i]);
            

            else if (fileInfos[i].Extension == ".pdb")
            
                listPdb.Add(fileInfos[i]);
            
        

        if (listDll.Count + listPdb.Count <= 0)
        
            Debug.Log("文件夹下没有dll文件");
        
        else
        
            Debug.Log("路径为:" + folderPath);
        

        if (autoChoosePath)
        
            savePath = normalPathToSave;
        
        else
        
            savePath = EditorUtility.OpenFolderPanel("dll要保存的文件夹", Application.dataPath + "/addressable/IlRuntime", string.Empty);
        
        Debug.Log("-----开始转换dll文件------");
        string path = string.Empty;
        for (int i = 0; i < listDll.Count; i++)
        
            //$ 符号的作用:等同于string.Format(),不用写占位符了,直接拼起来就可以了

            path = $"savePath/Path.GetFileNameWithoutExtension(listDll[i].Name)_dll_res.bytes";
            Debug.Log(path);
            BytesToFile(path, FileToBytes(listDll[i]));
        
        Debug.Log("------dll文件转换结束---------");

        Debug.Log("-----开始转换pdb文件------");
        for (int i = 0; i < listPdb.Count; i++)
        
            //$ 符号的作用:等同于string.Format(),不用写占位符了,直接拼起来就可以了

            path = $"savePath/Path.GetFileNameWithoutExtension(listPdb[i].Name)_pdb_res.bytes";
            BytesToFile(path, FileToBytes(listPdb[i]));
        
        Debug.Log("------pdb文件转换结束---------");
        AssetDatabase.Refresh();

    
    /// <summary>
    /// file转byte
    /// </summary>
    /// <param name="fileInfo"></param>
    /// <returns></returns>
    private static byte[] FileToBytes(FileInfo fileInfo)
    
        return File.ReadAllBytes(fileInfo.FullName);
    
    /// <summary>
    /// byte转文件
    /// </summary>
    /// <param name="path"></param>
    /// <param name="bytes"></param>
    private static void BytesToFile(string path, byte[] bytes)
    
        Debug.Log($"路径为:path\\nlength:bytes.Length");
        File.WriteAllBytes(path, bytes);
    



2.Unity工程加载热更DLL二进制文件

HotFixMgr.cs 增加LoadHotFixAssembly通过二进制文件加载热更DLL。修改之前加载DLL的路径Application.streamingAssetsPath改为新改的路径。整体代码如下:

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

public class HotFixMgr : MonoBehaviour

    public  static HotFixMgr instance;
    public ILRuntime.Runtime.Enviorment.AppDomain appdomain;
    private System.IO.MemoryStream m_fs, m_p;
    public static HotFixMgr Instance
    
        get
        
            if (instance==null)
            
                instance=new GameObject("HotFixMgr").AddComponent<HotFixMgr>();
                instance.LoadHotFixAssembly();
            
            return instance;
        
    

    // Start is called before the first frame update
    void Start()
    
    
        public void LoadHotFixAssembly() 
        dllPath = Application.dataPath + "/ILRunTimeAndAddressable/AssetsPackage/HotFixDll";
        appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
#if UNITY_android
    WWW www = new WWW(Application.streamingAssetsPath + "/Hotfix.dll");
#else
        WWW www = new WWW("file:///" + dllPath + "/HotFix_Project.dll");
#endif
        while (!www.isDone)
            //yield return null;
            System.Threading.Thread.Sleep(100);
        if (!string.IsNullOrEmpty(www.error))
            Debug.LogError(www.error);
        byte[] dll = www.bytes;
        www.Dispose();
#if UNITY_ANDROID
    www = new WWW(Application.streamingAssetsPath + "/Hotfix.pdb");
#else
        www = new WWW("file:///" + dllPath + "/HotFix_Project.pdb");
#endif
        while (!www.isDone)
            //yield return null;
            System.Threading.Thread.Sleep(100);
        if (!string.IsNullOrEmpty(www.error))
            Debug.LogError(www.error);
        byte[] pdb = www.bytes;
        System.IO.MemoryStream fs = new MemoryStream(dll);
        System.IO.MemoryStream p = new MemoryStream(pdb);
        appdomain.LoadAssembly(fs, p, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider());

        OnILRuntimeInitialized();
    


    /// <summary>
    /// 加载dll,pdb
    /// </summary>
    /// <param name="dll"></param>
    /// <param name="pdb"></param>
    public void LoadHotFixAssembly(byte[] dll, byte[] pdb)
    
        m_fs = new MemoryStream(dll);
        m_p = new MemoryStream(pdb);
        try
        
            appdomain.LoadAssembly(m_fs, m_p, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider());
        
        catch
        
            Debug.LogError("加载热更DLL失败,请确保已经通过VS打开Assets/Samples/ILRuntime/1.6/Demo/HotFix_Project/HotFix_Project.sln编译过热更DLL");
            return;
        
        appdomain.DebugService.StartDebugService(56000);
        OnILRuntimeInitialized();
    

    void OnILRuntimeInitialized()
    
        //appdomain.Invoke("Hotfix.Game", "Initialize", null, null);

#if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE)
        //由于Unity的Profiler接口只允许在主线程使用,为了避免出异常,需要告诉ILRuntime主线程的线程ID才能正确将函数运行耗时报告给Profiler
        appdomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
#endif

        //下面再举一个这个Demo中没有用到,但是UGUI经常遇到的一个委托,例如UnityAction<float>
        appdomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction>((action) =>
        
            return new UnityEngine.Events.UnityAction(() =>
            
                ((System.Action)action)();
            );
        );
    
    // Update is called once per frame
    void Update()
    

    


修改ProLaunch.cs增加对热更DLL的加载入口

具体代码如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
/// <summary>
/// 加载方式
/// </summary>
public enum LoadMode

    ByLocalDll,
    ByLocalAddressable



public class ProLaunch : MonoBehaviour


    /// <summary>
    /// 显示下载状态和进度
    /// </summary>
    public Text UpdateText;
    public Text DownText;

    public Button btnCheckAndUpdate;
    public Button btnUpdate;
    public Button btnDown;
    public Button btnLogin;
    public Slider Slider;//滑动条组件

    private List<object> _updateKeys = new List<object>();

    //public Transform father;
    [Tooltip("dll文件的加载方式")]
    public LoadMode loadingMode = LoadMode.ByLocalAddressable;
    // Start is called before the first frame update
    void Start()
    
        //retryBtn.gameObject.SetActive(false);
        btnCheckAndUpdate.onClick.AddListener(() =>
        
            StartCoroutine(DoUpdateAddressadble());
        );
        btnUpdate.onClick.AddListener(() =>
        
            UpdateCatalog();
        );
        // 默认自动执行一次更新检测
        //StartCoroutine(DoUpdateAddressadble());
        StartHotFix();
        btnDown.onClick.AddListener(() =>
        
            DownLoad();
        );



        btnLogin.onClick.AddListener(() =>
        
            SceneManager.LoadScene(1);
            //StartCoroutine(LoadScene("Test2"));

        );
    

    // Update is called once per frame
    void Update()
    
        
    

    /// <summary>
    /// 加载dll
    /// </summary>
    /// <returns></returns>
    public async System.Threading.Tasks.Task StartHotFix()
    

        //去服务器上下载最新的aa包资源

        //下载热更代码
        //string m_url=null;
        byte[] dll = new byte[]  ;
        byte[] pdb = new byte[]  ;
        if (loadingMode == LoadMode.ByLocalDll)
        
            //StartCoroutine(CheckHotUpdate(dll,pdb));

           HotFixMgr.instance.LoadHotFixAssembly();
        
        else if (loadingMode == LoadMode.ByLocalAddressable)
        
            TextAsset assetDll = await Addressables.LoadAssetAsync<TextAsset>("HotFix_Project_dll_res").Task;
            dll = assetDll.bytes;
            TextAsset assetPdb = await Addressables.LoadAssetAsync<TextAsset>("HotFix_Project_pdb_res").Task;
            pdb = assetPdb.bytes;
            HotFixMgr.instance.LoadHotFixAssembly(dll, pdb);


        

    
    public async void UpdateCatalog()
    
        //初始化Addressable
        var init = Addressables.InitializeAsync();
        await init.Task;

        //开始连接服务器检查更新
        var handle = Addressables.CheckForCatalogUpdates(false);
        await handle.Task;
        Debug.Log("check catalog status " + handle.Status);
        if (handle.Status == AsyncOperationStatus.Succeeded)
        
            List<string
    参考技术A
            现在游戏行业属于低迷期,短期内还看不到市场反弹的迹象,如果你注意过这几年的新闻,会发现15年后国家对游戏管控是越来越严格了,18年开始国内游戏审查,游戏审批都断了一阵子,直接导致半年多没有新游戏面市(这也是吃鸡手游一直没收费,现在直接改和平精英的原因),市场低迷到了现在。 很多中小公司是关门了的,而Unity开发商主力正是中小型公司。
如果坚持想入行游戏开发的话,建议找微信小游戏、网页游戏方向的,这个未来几年还有不错的市场。 如果不限制游戏,可以看看Web开发,网页、网站制作、微信小程序开发。这几个在三四年内会比游戏更吃香。
参考技术B 现在用这种开发工具开发游戏的还是比较多的,现在游戏开发的公司也不少,找到一份工作应该不难。只是看你的游戏开发水平到达什么程度,有时遇到一个好公司是非常重要的 参考技术C .Unity3D是一款游戏引擎,你学习他找工作不难,难就难在看你准备付出多少,你现在大一,一直努力学习Unity3D,等你大三的时候,估计你应该能会不少,找个六千左右的工作应该没有问题,如果你在努力些八千到一万的都可以找到,但是你不要 ... 参考技术D Unity前景如何?现在uny tv还能找到工作吗?就肯定是可以找到工作,他还是比较好的 这个肯定可以发展的,现在还可以找到工作,是不是比较闲用的?现在是前景一片光明的,是比较好找工作的

以上是关于Unity 3D模型展示框架篇之Addressables+ILRuntime热更(完结篇)的主要内容,如果未能解决你的问题,请参考以下文章

Unity3D中的地形转成模型

虚拟仿真Unity3D中模型的渐隐渐现效果实现

虚拟仿真Unity3D中模型的渐隐渐现效果实现

winform展示Unity3D文件(支持动态改变文件路径)

winform展示Unity3D文件(支持动态改变文件路径)

unity3d怎么导入3d模型