一不注意,在Unity3D中DllImport 引起的Bug.
单要说这个Bug是很简单,但是得从头说起。
一些大型的网络游戏,或者加载比较多的一些场景时,如果要等待所有模型,贴图等各种资源文件加载完毕才能执行游戏,对用户将会是一个很头大的事情。所以就需要用到动态加载,即AssetBundles。
AssetBundle基本概念就如上面所讲,虽然我正开发的项目不是大型网络游戏,但是也涉及到在线更新,所以也使用到AssetBundle。准确说是动态加载。
项目最初没有使用动态加载,所有的资源都放在场景中。随着项目的不断发展,更新越发频繁,也发现了问题。
每次都是用Unity3D自带的Build(File->Build&Run),你会发现Unity很好心地将很多零散的资源整合在一起,分批打包到不同文件(sharedassetsX.assets)。
然而这些 *.assets 并没有什么规律,以至于有时候你只替换了一张小图,也可能导致整个项目需要重新更新。【尽管是PC,但是AndroidBuild 也存在同样的问题】
所以大概在半年前,在项目中实现了基本的动态加载资源功能。
但是使用AssetBundle来实现动态加载的话,那些资源如何才能可视化呢?例如一张图 需要加载进来后才能调大小、摆位置。从网上找的AssetBundle 有关的文章都没说到这个,也有可能是作为公司的机密没公布出来。
如何既方便可视化编辑,又方便动态加载呢?
大概的思路如下:
Launcher:
1.从服务器下载资源文件到指定文件夹(Dir)
Editor:
1.检测是否存在需要动态加载的资源(通常是Ngui)
2.对需要动态加载的资源AddComponent<Loader>
3.Loader.setPath = 当前资源的路径
4.打包当前资源( Prefab->AssetBundle->EncryptionZipDir-> UnloadToServer)
5.当前Ngui.资源(Texture) = null
6.Build
Loader:(Player 加载)
1.遍历Scene 中所有Loader
2.Ngui.Texture = GetResource(Launcher制定目录+Loader.Path)
然而在实现过程中 原先可以运行的游戏(初步实现时可以) 在一大轮魔改之后不行了,而且是只有在使用自己的方法Build就有问题,而使用Unity自带的Build就没问题。问题是某个DLL无法加载(DllNotFoundException),而更糟糕的问题是在某些机台上可以运行,而某些机器却抛出DllNotFoundException。
除此之外还由于加载未完善导致句柄泄露,资源未整理完毕(大部分资源存在透明空白,浪费空间和加载时间),还有项目的其他进度紧张,导致这部分被暂停了下来。
到了今天(170216)稍有时间,再仔细查了下这个问题,万万没想到查找这个问题却整整用了我一整天!
刚开始时,我以为是Editor中第五步 导致Unity出现怪异的Bug(所有问题首先别怀疑是自己的错误!逃),所以精简了Build场景后(又10多个减少至1个,方便发现问题),分辨使用我写的Build和Unity自带的Build生成了目标文件(假设分别为A和B)。
然后逐步使用B_Data中的文件逐步替换A_Data中的文件,看看是那些文件导致DllNotFound的发生。最终发现只要把"mainData"和"resources.assets"替换后就不会出现问题了!然后使用对比工具逐步对比两个项目中的这2个文件。。。然而我还是太天真了,对比了1个多小时,发现这应该不是主要原因。
然后只能转换思路,逐步减少自己写的Build步骤(二分注释法 - -),最终发现只要场景存在Loader脚本,则有可能发生DllNotFound的情况!原来元凶在这!
经过最终分析元凶 解密资源时需要用到某个DLL的算法进行解密,然而此时却并没对此Dll的路径增加到环境变量path,所以发生了DllNotFoundException。
然而事情并没这么简单,如这样正常的流程是可以的:
AddPluginPathToEnvironmentPath();
DecryptAsset();
但是这样却不行:
DecryptAsset(); //DllNotFoundExcepion
AddPluginPathToEnvironmentPath();
DecryptAsset(); //Still DllNotFoundExcepion
其原因在于DecryptAsset 函数是DllImport的,第一次调用时会查找和加载非托管DLL并查找函数在内存中的地址。而此时并没有在Path中找到该DLL,所以只能 DllNotFoundException。
但是按道理,在增加DLL路径到Path后,第二次调用揭秘应该成功才对。而DllImport 并没有重新查找一次Dll,因为第一次才会查找,以后调用都是调用缓存。只要没有找到这继续DllNotFoundException,不会重新查找。
所以只能是 调用 AddPluginPathToEnvironmentPath 后方能调用 DecryptAsset 方法。
而鉴于我项目的特殊性, 将 AddPluginPathToEnvironmentPath 和 DecryptAsset 写在不同脚本的 Start 方法中,由于Unity没有明确说明脚本调用顺序的话,是对脚本的Start顺序随机调用的,所以导致有些机上可以成功但另外的DllNotFoundException。
这里涉及到2个知识点
1.Unity 中的脚本乱序执行
2.DllImport 对调用方法做了缓存
一不注意,在Unity3D中DllImport 引起的Bug.的更多相关文章
- unity3d中dllimport方法的使用,以接入腾讯平台为例!!!
说到有关dllimport方法可能还有很多人比较陌生,其实我自己也说不太清楚,大概说说什么时候要用它. 事实上功能类似于调用android的第三包,我们想要使用苹果上特定的api或者第三方平台的一些东 ...
- UE4/Unity3D中同时捕获多高清摄像头的高效插件
本文主要讲实现过程的一些坑. 先说下要实现的目标,主要功能在UE4/Unity中都要用,能同时捕获多个摄像头,并且捕获的图片要达到1080p25桢上,并且需要经过复杂的图片处理后丢给UE4/Unity ...
- 【Unity3d游戏开发】Unity3D中的3D数学基础---向量
向量是2D.3D数学研究的标准工具,在3D游戏中向量是基础.因此掌握好向量的一些基本概念以及属性和常用运算方法就显得尤为重要.在本篇博客中,马三就来和大家一起回顾和学习一下Unity3D中那些常用的3 ...
- Unity3D中可中途释放的单例
Unity3D中可中途释放的单例 使用静态类,静态变量的坏处是从程序加载后就一直占用内存,想要释放比较麻烦,可是之前使用的单例,没有提供释放的方法,那是不是也同静态的一样直到程序结束菜释放?那单例的好 ...
- 图文详解Unity3D中Material的Tiling和Offset是怎么回事
图文详解Unity3D中Material的Tiling和Offset是怎么回事 Tiling和Offset概述 Tiling表示UV坐标的缩放倍数,Offset表示UV坐标的起始位置. 这样说当然是隔 ...
- unity3d中 刚体(Rigidbody) 碰撞体(Collider) 触发器(Is Trigger)
刚体(Rigidbody)的官方(摘自Unity3d的官方指导书<Unity4.x从入门到精通>)解释如下: Rigidbody(刚体)组件可使游戏对象在物理系统的控制下来运动,刚体可 ...
- Unity3D中Update()与FixedUpdate()的区别
Unity3D中Update()与FixedUpdate()的区别是什么呢?从字面上理解,它们都是在更新时会被调用,并且会循环的调用.但是Update会在每次渲染新的一帧时,被调用.而FixedUpd ...
- Unity3D中C#和js方法相互调用
通过查找资料,Unity3D中C#和js要相互调用彼此的方法,js文件必须放在"Standard Assets". "Pro Standard Assets" ...
- 在unity3d中使用opencv
1.首先下载opencv2.4.10,解压缩后放在合适的地方,然后根据自己的电脑(32位或64位)选择X86或X64,我的是32位,将“opencv存放路径\build\x86\vc12\bin”加入 ...
随机推荐
- Padavan老毛子固件:17CE插件集成
Padavan老毛子固件:17CE插件集成 1.老毛子路由设置:系统管理-服务-启动SSH服务器 以下链接下载 "winscp" http://down.orsoon.co ...
- 廖雪峰Java7处理日期和时间-3java.time的API-2ZonedDateTime
ZonedDatetime = LocalDateTime + ZoneId ZonedDateTime:带时区的日期和时间 ZoneId:新的API定义的时区对象(取代几句的java.util.Ti ...
- vue源码核心部分
1.模板编译 初始化时做的:template ==parse()==>ASTtree ==generate()==>render函数 ==> mount(调用dom方法) 每次 ...
- 【待考察】Appium使用技巧,助你快速入门移动端自动化!
Appium使用技巧,助你快速入门移动端自动化! 原创: 柠檬班superman 柠檬班软件测试 1月4日 关注并置顶[柠檬班]的小哥哥小姐姐 “猪”年行大运 说说最近研究移动端的自动化 移动端的自动 ...
- python-web自动化环境安装
web自动化环境安装 1.安装selenium 命令行使用以下命令安装selenium:pip install -U selenium 2.安装chrome浏览器 3.chromedriver的下载 ...
- svn Advanced
1.Eclipse last 1.Eclipse 安装subversive插件: 安装连接器: 打开视图"SVN Repositories": 创建“资源库位置”: 配置全局范围文 ...
- python中 函数名加括号与不加括号
加括号是返回函数的结果,不加括号相当于函数的调用.
- SpringBoot 之热部署
默认情况下, 我们修改 class 或者 修改模板文件(templates目录 下面的文件) 等动态资源, 都不会立即自动生效. 在IDEA中, 我通过Ctrl + F9 , 仍然是无效. 当然, 静 ...
- maven的单元测试中没有
原因:BaseTest没有找到单元测试造成的 增加一个空的单元测试 @Testpublic void testNothing(){} 异常现象:在maven项目执行mvn install 或mvn t ...
- MySql:SELECT 语句(二)排序 ORDER BY 的使用
1.按单列排序 语句: SELECT col FROM tablename ORDER BY col; ( 一般情况下, ORDER BY 子句中使用的列为选择的列,但也可以使用其他列) 2.按 ...