Unity可以java 动态替换占位符资源吗

后使用快捷导航没有帐号?
 论坛入口:
  |   |    |   | 
深入Unity资源格式,实现动态依赖资源加载
  Unity的资源读取相信已经有很多分析的文章,本文将深入资源格式及引擎源码分析unity的资源读取,并尝试给assetbundle添加一个接口AssetBundle.LoadDependentResource(name)加载一个AssetBundle中依赖的但又没有显式路径的资源。
  对于特大规模的场景来说,我们往往希望能够释放掉一些不常用或远离的被依赖的资源来减少内存。假如我们有一个名为A的fbx角色模型依赖B、C两个Texture和D、E两个AnimChip,当这个A模型离镜头比较远时,并正在播放D这个AnimClip,我们可能希望1)暂时卸载掉Texture B和C中的minmip的精细几层,只保留粗糙的几层,2)卸载掉AnimClip E,并在A重新临近镜头时,3)重新加载纹理B和C中精细的几层minmip和4)重新加载AnimClip E,5)以及在一些特殊需求下直接需要加载资源包中的某个被依赖的资源。很多同学会想到调用Resouces.UnloadUnusedAssets来卸载不用的资源,但是并没有直接的方法能够卸载和重新加载正在被引用的资源中的一部分,如本文需求中的1)~5)。
  Unity引擎的AssetBundle已经为我们带有了Load(path)和LoadAll等接口,其中:
  Load (name : string, type : Type) : Object需要指定一个name/path,即处于包中的资源名字/相对路径,而这名字则是打包的时候往往通过
  BuildPipeline.BuildAssetBundle,
  BuildPipeline.BuildStreamedSceneAssetBundle
  BuildPipeline.BuildAssetBundleExplicitAssetNames
  等函数显示/非显示打包的资源名字,对于依赖的资源通常会通过BuildAssetBundleOptions.CollectDependencies等标记一并打包进AssetBundle,但是我们在加载的时候却无法直接通过Load指定名字来加载,除非把这个依赖资源也通过BuildPipeline.BuildAssetBundleExplicitAssetNames显式添加名字到assetNames集合。本文的主要目的就是让程序实现能够直接加载没有明确显式指定的却又因被依赖关系打包进AssetBundle的资源。
  AssetBundle格式
  AssetFile就是我们导出exe后bin/xxx_Data目录下的各个扩展名为.assets的文件,AssetBundle就是这些.assets文件的集合打包成一个可压缩的.unity3d扩展名的文件。因此我们对Unity源码进行修改,在原有AssetBundle.CreateFromFile基础上也添加AssetBundle.CreateFromFolder允许从磁盘目录上加载一堆.assets文件作为一个映射到AssetBundle进行统一管理。每个AssetBundle包括一个头BlockAssetBundleHeader,
public struct AssetBundleHeader
& & {
& && &&&pub
& && &&&public int streamV
& && &&&public UnityVersion unityV
& && &&&public UnityVersion unityR
& && &&&public uint minimumStreamedB
& && &&&public int headerS
& && &&&public int numberOfLevelsToD
& && &&&public int numberOfL
& && &&&public struct LevelInfo {
& && && & public uint PackS
& && && & public uint UncompressedS
& && &&&};
& && &&&public List&LevelInfo& levellist=new List&LevelInfo&();
& && &&&public uint completeFileS
& && &&&public uint dataHeaderS
& && &&&pu
& & };复制代码
  AssetBundleHeader由signature,streamVersion, unityVersion等字段组成。读完assetbundle的头信息之后后面就是lzma压缩数据包括可能的AssetFile集合,把数据解压缩到一个MemoryStream后,在这个MemoryStream里首先是每一个AssetFile在AssetBundle里的条目信息:
& & public struct AssetBundleEntryInfo
& & {
& && &&&
& && &&&
& && &&&
& & }
复制代码
  指出了每一个AssetFile在解压缩后的MemoryStream字节流中的偏移和长度。接下来就可以提取回每个AssetFile出来。
image003.gif (57.23 KB, 下载次数: 14)
09:36 上传
  AssetFile格式
  每个AssetFile由头信息Header、类型树TypeTree、资源对象信息ObjectInfo、外部结构externalsStruct、对象数据ObjectData等Block组成。头信息Header Block如下:
& &public struct AssetFileHeader
& & {
& && &&&public long metadataS// size of the structure data
& && &&&public long fileS// size of the whole asset file
& && &&&public long dataO// offset to the serialized data
& && &&&pu// byte order of the serialized data?
& && &&&public byte[] reserved = new byte[3];// unused
& & }复制代码
  包含了metadataSize,fileSize,versionInfo,dataOffset等字段以及表示编码顺序的endianness字段。紧随其后的TypeTree Block保存了各种序列化的类型节点,也包括了嵌入的类型信息节点。ObjectInfo包括了AssetFile里面每个Asset对象的元数据(条目信息,FileID指示了对象处于AssetFile的相对序号,offset指示字节偏移,length指示Asset对象长度,typeID指示类型类型,classID类型id,isDestroyed是否删掉)如下:
  &&public class ObjectInfo : UnityStruct
& & {
& && &&&public long localID;&&//LocalIdentifierInFileType
& && &&& // Object data offset
& && &&& // Object data size
& && &&&public int typeID; // Type ID, equal to classID if it's not a MonoBehaviour
& && &&&public int classID; // Class ID, probably something else in asset format &=5
& && &&&public short isD // set to 1 if destroyed object instances are stored?
& & }
复制代码
  跟随而来后面的ObjectData Block就是每个对象的具体字节流。每个对象的名字,序列化的变量,属性等,纹理的像素数据等都是存在对象的字节流里。对于打包进AssetBundle的AssetFile的第一个对象往往是一个xxx&AssetBundle&对象,而独立存在于目录上的AssetFile的第一个对象往往是一个xxx&ResourceManager&对象,分别用于反序列化这两种资源管理类。接下来就是个资源对象Asset了,比如xxx&Texture2D&, yyy&AnimClip&, zzz&Material&等。以纹理资源为例,xxx&Texture&这样一个名字为xxx的纹理资源,首先有一堆原来在磁盘上.meta文件里记录的字段,接下来就是像素的字节流。下面给出了一些常用的类型的字段例子:
image004.png (6.35 KB, 下载次数: 15)
09:36 上传
A3563f73&Texture2D&
image006.png (3 KB, 下载次数: 12)
09:36 上传
229bed14&Material&
image008.png (8.51 KB, 下载次数: 15)
09:36 上传
Box001&Mesh&
image010.png (2.68 KB, 下载次数: 16)
09:36 上传
AlphaTest-VertexList&Shader&
image012.png (4.02 KB, 下载次数: 15)
09:36 上传
unknown&AssetBundle&
image014.png (3.44 KB, 下载次数: 10)
09:36 上传
UVMesh&MonoScript&
常见的一些资源类型及其属性字段
  Unity在加载这些资源的时候就是先反序列化这些字段到对象实例对应的变量上。
  每个Asset对象的元数据只包含了Asset资源的引用对{FileID,localID},Asset对象自身的属性又没有相对路径,那么unity的Resouces.Load等究竟又是如何通过相对路径映射到这各个对象呢?这个相对路径到{FileID,localID}的映射表存在那?我们再次阅读unity源码,发现Unity初始化AssetBundle类实例会首先反序列化m_Container,m_MainAsset,m_PreloadTable等变量,而该类实例正是对应AssetFile中的第一个没名字的&AssetBundle&资源对象。对于目录上而非打包的AssetFile则是对应其中的第一个没名字的&ResourceManager& 资源对象。m_Container变量是一个key-value表保存了所有打包时显式资源从相对路径path到Asset{FileID,localID}的映射。我们想直接加载的依赖资源并没有在m_Container的映射表中,为了能直接加载非显式资源,我们另外建立一个映射表来实现从依赖的隐式对象名字name到引用对{FileID,localID}的映射表。
  AssetBundle导出给C#的接口定义在Runtime/Export/AssetBundleBindings.txt
具体实现在runtime/misc/AssetBundleUnity.cpp,AssetBundle.CreateFromFile 调用 ExtractAssetBundle加载AssetBundle到内存,具体加载过程是读入文件头后把AssetBundle压缩数据解开,得到多个AssetFile的头(一个AssetBundle包含多个AssetFile),名字可能包括”CAB”以识别,得到AssetFile头后按普通AssetFile调用持久化管理器PersistentManager.LoadExternalStream把AssetFile中的资源对象映射入内存。
  AssetBundle.Load加载对象具体在AssetBundleUtility:oadNamedObjectFromAssetBundle实现,首先调用ResourceManager::GetPathRange获得AssetBundleBindings.txt增加LoadDependent
// Loads object with /name/ of a given /type/ from the bundle.
& && &&&CSRAW
& && &&&[TypeInferenceRule(TypeInferenceRules.TypeReferencedBySecondArgument)]
& && &&&CONSTRUCTOR_SAFE
& && &&&CUSTOM Object LoadDependent (string name, Type type)
& && &&&{
& && && && && & Scripting::RaiseIfNull (type);
& && && && && & Object* o = LoadNonNamedObjectFromAssetBundle (*self, name, type);
& && && && && & if (o==0) return SCRIPTING_NULL;
& && && && && & return Scripting::ScriptingWrapperFor(o);
复制代码
  AssetBundleUnity.cpp中添加
Object* LoadNonNamedObjectFromAssetBundle (AssetBundle& bundle, const std::string& name, ScriptingObjectPtr type)
{
& && &&&LocalSerializedObjectIdentifier localID = bundle.GetLocalID(name);
& && &&&vector&Object*&
& && &&&ProcessAssetBundleEntries(bundle,localID,type,result,true);
& && &&&if (!result.empty())
& && && && && & return result[0];
& && &&&return NULL;
}
复制代码
  在bundle.GetLocalID 里从自定义的另外一个映射表根据名字查找资源引用对LocalSerializedObjectIdentifier {FileID,localID},得到后传入重载的另外一个以LocalSerializedObjectIdentifier为参数ProcessAssetBundleEntries逐个加载具体的Asset对象即可。
关注我们官方微信公众号
下载我们官方APP-游戏行
关注手游动态微信公众号
重拳!文化和旅游部集中检查网络表演平台等广深中小游戏公司生存境况4月16日—4月22日共有43款游戏开测|GameRe乔布斯做游戏的那些日子超大型个人在线角色扮演游戏的未来图景《尼尔》总监:动作游戏已经达到“真实度尽
微信扫一扫关注我们→【紧急求救】关于Unity3d动态加载资源的疑问
我用BuildPipeline.BuildAssetBundle实现了打包的资源BuildPipeline.BuildAssetBundle( && & & & & & & & & &null, && & & & & & & & & &obj_ArrAssetsList, & & & // 资源列表数组&& & & & & & & & & &bundlePath, && & & & & & & & & &BuildAssetBundleOptions.CompleteAssets );资源也能成功打包了但是我在用AssetBundle.CreateFromFile实现动态加载的时候却出现这样的错误This asset bundle was not created with UncompressedAssetBundle flag, expected id 'UnityRaw', got 'UnityWeb'UnityEngine.AssetBundle:CreateFromFile(String)求达人救济
要评论请先&或者&
AssetBundle.CreateFromFile 只能用于pc和mac standalone如果用于这种程序你的targetplatform要指定,不能用默认参数,默认模式是webplayer我记得是这样,这电脑上木有unity,暂时只能凭记忆
AssetBundle.CreateFromFile 只能用于pc和mac standalone如果用于这种程序你的targetplatform要指定,不能用默认参数,默认模式是webplayer我记得是这样,这电脑上木有unity,暂时只能凭记忆----------------------------------------------------------------我加了 BuildTarget.Android | BuildTarget.StandaloneWindows这两个还是不行
这个选项不能复合吧。。。不同target要重新build的
但是我成功build出来了。要是不能复合的话应该会出错吧
我单独试了BuildTarget.Android 和 BuildTarget.StandaloneWindows报错还是一样This asset bundle was not created with UncompressedAssetBundle flag, expected id 'UnityRaw', got 'UnityWeb'UnityEngine.AssetBundle:CreateFromFile(String)
最后一个buildoption还要加上UncompressedAssetBundlecreatefromfile很多限制的
谢谢 师兄了 算是解决了。我的项目是跨平台的这样做确实限制太多如果不用CreateFromFile的话 那就只能用 (url, 1);形式了加载了吗?程序写累了,就来玩玩酷跑小游戏吧,嘿嘿。
雨松MOMO送你一首歌曲,嘿嘿。
Unity3D研究院之动态修改烘培贴图的大小&脚本烘培场景(七十二)
Unity3D研究院之动态修改烘培贴图的大小&脚本烘培场景(七十二)
围观38077次
编辑日期: 字体:
Unity默认烘培场景以后每张烘培贴图的大小是1024。但是有可能你的场景比较简单,用1024会比较浪费。如下图所示,这是我的一个场景的烘培贴图,右上角一大部分完全是没有用到,但是它却占着空间。
有时候可能你想去修改烘培贴图的大小,如下图所示以前我试过在Inspector视图中修改烘培贴图的大小,图虽然是小了但是它是整体缩小,后来在手机上面发现这这样直接修改烘培贴图的大小确实有问题,接缝方面处理的有问题。而且每次烘培完场景以后都需要这样缩一下。。(千万不要这样缩图)
我们需要美术在烘培场景的时候去设置烘培贴图的大小,可是Unity的烘培菜单根本没有设置烘培贴图的大小。。后来我想到了一个巧妙的办法,用脚本来烘培场景,顺便设置烘培贴图的大小。。代码很简单。
Clear()就是删除当前场景的烘培贴图。
Bake()就是直接烘培当前场景,我还可以在烘培场景之前加一些代码。。。一切都是脚本化操作。嘿嘿。。
maxAtlasHeight和maxAtlasWidth 就是每张烘培贴图的宽和高,一般我们每个场景用一张512的就差不多了。烘培贴图必须要2的幂次方,并且要正方形的。
<div class="crayon-num" data-line="crayon-5ada2d<div class="crayon-num crayon-striped-num" data-line="crayon-5ada2d<div class="crayon-num" data-line="crayon-5ada2d<div class="crayon-num crayon-striped-num" data-line="crayon-5ada2d<div class="crayon-num" data-line="crayon-5ada2d<div class="crayon-num crayon-striped-num" data-line="crayon-5ada2d<div class="crayon-num" data-line="crayon-5ada2d<div class="crayon-num crayon-striped-num" data-line="crayon-5ada2d
[MenuItem("Test/Test")] static void Init() {
LightmapEditorSettings.maxAtlasHeight = 512;
LightmapEditorSettings.maxAtlasWidth = 512;
Lightmapping.Clear();
Lightmapping.Bake(); }
这样美术只用在Lightmapping窗口中设置好当前烘培场景的参数。。调用我们写的拓展菜单方法就可以了。这里我设置的烘培贴图的大小是512。
利用上面的方法我们把一张1024的贴图缩小到了512,效果没有什么变化但是空间缺缩小了4倍。。
IOS PVRTC压缩后512的贴图只有170.8KB,很给力吧。。
本文固定链接:
转载请注明:
雨松MOMO提醒您:亲,如果您觉得本文不错,快快将这篇文章分享出去吧 。另外请点击网站顶部彩色广告或者捐赠支持本站发展,谢谢!
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
如果您愿意花10块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。
您可能还会对这些文章感兴趣!Unity中资源动态加载的几种方式比较_百度知道
Unity中资源动态加载的几种方式比较
我有更好的答案
Unity3D 里有两种动态加载机制:一个是Resources.Load,另外一个通过AssetBundle,其实两者区别不大。 Resources.Load就是从一个缺省打进程序包里的AssetBundle里加载资源,而一般AssetBundle文件需要你自己创建,运行时动态加载,可以指定路径和来源的。
(1).assetBundle就是内部数据读取完后自动创建了一个assetBundle而已Create完以后,等于把硬盘或者网络的一个文件读到内存一个中,这时也就是个AssetBundle内存镜像数据块。释放方式是AssetBundle.Unload(false)
(2).用AssetBundle.Load(同Resources.Load) 会从AssetBundle的内存镜像里读取并创建一个Asset对象,使用Resources.UnloadUnusedAssets()释放全部和Resources.UnloadAsset(gameobject);释放单个;
(3).Instaniate一个Prefab,是一个对Assets进行Clone(复制)+引用结合的过程,使用GameObject.Destroy(gameobject);(注意)游戏对象可能不是动态加载时,但是可能他的材质、图集是动态加载的请把这些应用置为空。
采纳率:83%
为您推荐:
其他类似问题
unity的相关知识
&#xe675;换一换
回答问题,赢新手礼包&#xe6b9;
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。博主最新文章
博主热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 java 动态替换class 的文章

 

随机推荐