游戏开发论坛

 找回密码
 立即注册
搜索
查看: 7158|回复: 0

Unity3D热更新LuaFramework入门实战(二):资源热更新

[复制链接]

4万

主题

4万

帖子

8万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
83752
发表于 2016-10-26 09:40:47 | 显示全部楼层 |阅读模式
  热更新涉及资源热更新和代码热更新(其实lua代码也是资源),那接下来看看如何动态加载一个模型,然后热更成其他素材。这一部分涉及资源打包、动态创建资源等内容。

  文/知乎 @罗培羽

  1、创建物体

  为了调试的方便,笔者先将框架配置为本地模式,待测试热更新时再改成更新模式。

image002.jpg
配置为本地模式

  先测试个简单的创建物体,新建一个名为go的物体,然后设置它的坐标为(1,1,1)。这段代码虽然不涉及资源加载,但能展示“把物体添加到场景中”的过程。Main.lua的代码如下:
  1. function Main()                                       
  2.         local go = UnityEngine.GameObject ('go')
  3.         go.transform.position = Vector3.one               
  4. end
复制代码

image004.jpg
动态创建一个名为go的空物体

  要热更新资源,便需要制作资源。这里制作一个名为tankPrefab的坦克模型预设,然后存到Assets/Tank目录下。接下来对它做打包,然后动态加载。

image006.jpg

  广告时间:这个坦克模型是来自笔者即将出版的一本书《Unity3D网络游戏实战》。该书通过一个完整的多人坦克对战实例,详细介绍网络游戏开发过程中涉及到的知识和技巧。书中还介绍了服务端框架、客户端网络模块、UI系统的架构等内容。相信透过本书,读者能够掌握Unity3D网络游戏开发的大部分知识,也能够从框架设计中了解商业游戏的设计思路,感谢大家支持。

  2、资源打包

  LuaFramework在打包方面并没有做太多的工作,我们需要手动打包。打开Assets/LuaFramework/Editor/Packager.cs,按照示例的写法,加上下面这一行:将Assets/Tank目录下的所有预设(.prefab)打包成名为tank的包。

image008.jpg
修改打包代码

  点击“Build Windows Resource”,即可在StreamingAssets中看到打包好的文件。

image010.jpg
坦克资源的包文件

  如下图所示,Unity3D资源包里面包含多个资源,就像一个压缩文件一样。在动态加载的时候,便需要有加载包文件、或取包中的资源两步操作(框架已经帮我们做好了这部分工作,直接调用API即可)。

image012.jpg
Unity3D的资源包

  3、动态加载模型

  编写如下lua代码(main.lua),使用框架提供的资源管理器(resMgr)加载tank包的TankPrefab文件,加载完成后回调OnLoadFinish方法。在OnLoadFinish中使用Instantiate实例化对象。
  1. --主入口函数。从这里开始lua逻辑
  2. function Main()                                       
  3.         LuaHelper = LuaFramework.LuaHelper;
  4.         resMgr = LuaHelper.GetResManager();
  5.         resMgr:LoadPrefab('tank', { 'TankPrefab' }, OnLoadFinish);
  6. end
  7. --加载完成后的回调--
  8. function OnLoadFinish(objs)
  9.         local go = UnityEngine.GameObject.Instantiate(objs[0]);
  10.         LuaFramework.Util.Log("Finish");       
  11. end
复制代码

  完成后运行游戏,即可看到动态加载出来的模型。

image014.jpg
动态加载出来的模型

  4、加载资源的过程

  只有理解了动态加载,即LoadPrefab的过程,才能算是真正的理解了热更新。LoadPrefab为ResourceManager中定义的方法,在Assets\LuaFramework\Scripts\Manager\ResourceManager.cs中实现,建议配合代码看下面的解释。

  LoadPrefab的流程如下所示,先是判定当前是否正在加载该资源包,如果没有则调用OnLoadAsset加载资源包、然后解包获取资源、调用回调函数。

image016.jpg
LoadPrefab的流程

  ResourceManager类定义了m_AssetBundleManifest、m_Dependencies、m_LoadedAssetBundles、m_LoadRequests这4个变量,只要理解了这几个变量的用途,也就能够理解了资源加载的全过程了。这4个变量的类型如下:

image018.jpg
ResourceManager定义的几个变量

  m_AssetBundleManifest

  理解m_AssetBundleManifest之前,需要先理解Unity3D的依赖打包。前面的tank.unity3D中,坦克的预设、坦克的贴图等资源都被打包到一起,没有依赖关系(只要打包时不给贴图单独打包,Unity3D会自动将预设相关的资源都打包进来)。如下图所示。

image020.jpg
前面加载坦克制作的资源包

  假如有两个坦克预设共用一套贴图,如果像上面那样打包,每个坦克预设各自包含一份贴图,资源会比较大。更好的办法是将公共贴图放到一个包里,每个坦克预设不再包含贴图(如下图)。这种打包方式下,加载TankPrefab前,需要先加载依赖包common.unity3D,坦克预设才能找到贴图。

image022.jpg
依赖打包

  打包后,Unity3D会产生一个名为AssetBundle.manifest的文件(框架会将该文件放在StreamingAssets中),该文件包含所有包的依赖信息。所以在加载资源前需要先加载这个文件,m_AssetBundleManifest便是指向这个包的变量。相关代码如下:

image024.jpg
m_AssetBundleManifest便是指向AssetBundle.manifest的变量

  加载这个包后,便可以使用下面的语句获取某个包所依赖的所有包名,然后加载它们。

  1. string[] dependencies = m_AssetBundleManifest.GetAllDependencies(包名);
复制代码

  m_LoadedAssetBundles

  字典类型的m_LoadedAssetBundles保存了所有已经加载资源包。如果某个包已经被加载过,那下次需要用到它时,直接从字典中取出即可,减少重复加载。简化后的代码如下:
  1. IEnumerator OnLoadAsset(XXX)
  2. {
  3.       AssetBundleInfo bundle = GetLoadedAssetBundle(XXX);  
  4.       if(!bundle)
  5.             bundle  =  OnLoadAssetBundle(名字);  
  6.       加载资源
  7.       回调函数处理
  8. }
复制代码

  其中GetLoadedAssetBundle方法会判断资源包是否存在于m_LoadedAssetBundles中,并返回资源包。OnLoadAssetBundle为重新加载资源包的方法。

  加载资源包后,只需通过 bundle.LoadAssetAsync(资源名,类型)便可加载所需的资源。

  m_Dependencies

  m_Dependencies记录了所有已加载资源的依赖包,以便在GetLoadedAssetBundle方法中判断资源包是否被完全加载(主体包和所有依赖包都被加载才算完成加载)。简化后的代码如下:
  1. IEnumerator OnLoadAssetBundle(包名)
  2. {
  3.       //获取依赖包
  4.       string[] dependencies = m_AssetBundleManifest.GetAllDependencies(abName);
  5.       m_Dependencies.Add(abName, dependencies); //更新依赖表
  6.       //加载依赖包
  7.       for (int i = 0; i < dependencies.Length; i++)
  8.             OnLoadAssetBundle(XXX)  
  9.   //然后加载主体资源
  10.        download = WWW.LoadFromCacheOrDownload(包名)
  11. //更新加载表       m_LoadedAssetBundles.Add(XXX)
  12. }
复制代码
  1. AssetBundleInfo GetLoadedAssetBundle(包名)
  2. {
  3.        //判断加载表
  4.        AssetBundleInfo bundle = m_LoadedAssetBundles[包名];
  5.        if (bundle == null) return null;
  6.        //判断依赖包
  7.        foreach (string 依赖包名 in m_Dependencies[包名])
  8.        {
  9.               if (m_LoadedAssetBundles[依赖包名]== null)
  10.                      return null;
  11.        }
  12.        return bundle;
  13. }
复制代码
m_LoadRequests

  m_LoadRequests是一个<string, List<LoadAssetRequest>>类型的字典,LoadAssetRequest的定义如下,它用于异步加载后的回调。

image026.jpg
LoadAssetRequest

  由于采用异步加载,加载资源的过程中,程序有可能发起同一个请求,多次加载似乎有些浪费,如下图所示。

image028.jpg
两次加载同一资源

  更好的办法是,在收到第2次请求时先做判断,如果该资源正在加载,那先记录请求2的回调函数,待资源加载完成,调用所有请求该资源的回调函数,如下图所示。m_LoadRequests便记录每种资源的请求,使程序可以判断该资源是否正在加载,并从m_LoadRequests获取各个资源请求的回调函数。

image030.jpg
记录请求2的回调函数

  简化后的代码如下:

  1. void LoadAsset(包名)
  2. {
  3.        If(m_LoadRequests[abName] == null)
  4.        {
  5.               m_LoadRequests[包名].Add(回调函数等);
  6.               OnLoadAsset();
  7.        }
  8.        else
  9.        {
  10.               m_LoadRequests[包名].Add(回调函数等);
  11.        }
  12. }

  13. IEnumerator OnLoadAsset(XXX)
  14. {
  15.        加载包
  16.        加载资源
  17.        foreach( request in  m_LoadRequests[包名] )
  18.        {
  19.               Request.回调函数();
  20.        }
  21. }
复制代码

  5、资源热更新

  “资源热更新”和上一篇的“代码热更新”完全相同,开启更新模式后,将新的资源文件复制到服务器上,框架即可自动下载更新的资源。这里不再复述。

  笔者也是刚刚接触LuaFramework不久,想看看是否适合于工作室接下来的项目,文章错误之处在所难免,请大家多加包涵。

  相关阅读:Unity3D热更新LuaFramework入门实战

  声明:游资网登载此文出于传递信息之目的,绝不意味着游资网赞同其观点或证实其描述。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2024-6-18 08:15

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表