王在京同学 2017-11-10T03:51:15+00:00 spencer.wong@hotmail.com UGUI 'World Space' Canvas 不响应触摸 2017-11-10T00:00:00+00:00 王在京同学 http://peakcoder.com/unity3d/2017/11/10/ugui-raycast 最近做航母飞机的预瞄,用的World Space的Canvas绘制在3d空间,但是不响应触摸事件,Canvas的Sort Order设置无效,事件永远被Screen Space Camera的Canvas截获,之前的UGUI版本GraphicRaycaster脚本有显示的sort order设置,现在也没有了。

去bitbucket看了EventSystem源码发现比较顺序为
1.GraphicRaycaster不同
1) 都有event camera 先比较camera 的 depth
2) 比较GraphicRaycaster 的 sortOrderPriority
3) 比较GraphicRaycaster 的 renderOrderPriority

2.GraphicRaycaster相同
1) 比较Canvas 的 sortingLayer
2) 比较Canvas 的 sortingOrder
3) 比较控件的depth
4) 比较与摄像机的distance,overlay为0

private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs)
{
    if (lhs.module != rhs.module)
    {
        if (lhs.module.eventCamera != null && rhs.module.eventCamera != null && lhs.module.eventCamera.depth != rhs.module.eventCamera.depth)
        {
            // need to reverse the standard compareTo
            if (lhs.module.eventCamera.depth < rhs.module.eventCamera.depth)
                return 1;
            if (lhs.module.eventCamera.depth == rhs.module.eventCamera.depth)
                return 0;

            return -1;
        }

        if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
            return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);

        if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
            return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
    }

    if (lhs.sortingLayer != rhs.sortingLayer)
    {
        // Uses the layer value to properly compare the relative order of the layers.
        var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
        var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
        return rid.CompareTo(lid);
    }


    if (lhs.sortingOrder != rhs.sortingOrder)
        return rhs.sortingOrder.CompareTo(lhs.sortingOrder);

    if (lhs.depth != rhs.depth)
        return rhs.depth.CompareTo(lhs.depth);

    if (lhs.distance != rhs.distance)
        return lhs.distance.CompareTo(rhs.distance);

    return lhs.index.CompareTo(rhs.index);
}

World Space Canvas 的 eventCamera 是MainCamera,他的 depth一定是要小于UICamera的,基本没救了啊,只能把UI Canvas设置为Overlay来解决了,再做一个GraphicRaycaster子类设置sortOrderPriority来解决。

public class WorldRaycaster : GraphicRaycaster
{
    [SerializeField]
    private int SortOrder = 0;

    public override int sortOrderPriority
    {
        get
        {
            return SortOrder;
        }
    }
}

找个时间通读一遍UGUI的源码,画下类图。

–EOF–

]]>
Unity3d iOS crash 'Ran out of trampolines' 2017-07-09T00:00:00+00:00 王在京同学 http://peakcoder.com/unity3d/2017/07/09/ios-crash-randomly iOS平台不允许JIT,会通过Full AOT直接编译为ARM汇编代码。但在编译的时候仍有一些无法静态确定的事情:例如泛型接口可能需要不同的虚拟表(运行时存放执行方法的集合),这取决于接口被实例化时的类型。(对于这种情况,从技术上讲确定虚拟表的最大数量是可能的,但是这数量可能会是庞大的–即泛型接口数乘以应用中类型数).我们无法为这些虚拟表在运行时动态的分配内存,所以我们指定了一个合理的默认值并且允许用户在运行时出现问题的情况下增加默认值。这就是蹦床的基本理论(实际情况略微不同,这依赖于蹦床数的类型,但这并不重要)以上是Rolf Bjarne Kvinge的解释

Ran out of trampolines of type 0

如果你再运行时,输出这个错误,你可以在项目option–iphone build处添加额外的参数-aot “ntrampolines=2048” 这个参数默认值为1024,试着增加这个值直到满足你的应用的需求。

Ran out of trampolines of type 1

如果你使用了过多的泛型嵌套,如List中还有List成员,你可以同上通过添加额外的参数-aot "nrgctx-trampolines=2048" 来解决。 蹦床类型1的默认值为1024.

Ran out of trampolines of type 2

如果你界面操作频繁,你可以通过添加额外的参数-aot “nimt-trampolines=512” 来解决。 这里的默认值为128. 

以上参数在Build settings的 AOT Compilation Options 里添加

–EOF–

]]>
iOS Mono Full AOT 模式下的限制 2017-07-08T00:00:00+00:00 王在京同学 http://peakcoder.com/unity3d/2017/07/08/ios-crash-bugs IL
IL是.NET框架中中间语言(Intermediate Language)的缩写。使用.NET框架提供的编译器可以直接将源程序编译为.exe或.dll文件, 但此时编译出来的程序代码并不是CPU能直接执行的机器代码,而是一种中间语言IL(Intermediate Language)使用中间语言可以实现平台无关性,既与特定CPU无关;实现.NET框架中语言之间的交互操作,这就是为什么unity3D里面可以c#和js混编。

JIT
即时编译(英语:Just-in-time compilation),又译及时编译、实时编译,动态编译的一种形式,是一种提高程序运行效率的方法。通常,程序有两种运行方式:静态编译与动态解释。静态编译的程序在执行前全部被翻译为机器码,而解释执行的则是一句一句边运行边翻译。JIT狭义来说是当某段代码即将第一次被执行时进行编译,因而叫“即时编译”。JIT编译是动态编译的一种特例。

AOT
静态编译、提前编译(Ahead-of-time compilation)

Mono Full AOT 模式的限制
在iOS平台中,Mono是以Full AOT模式运行的,无法使用JIT引擎,Full AOT常见的限制:

1.不支持泛型虚方法。

因为对于泛型代码,Mono通过静态分析以确定要实例化的类型并生成代码,但静态分析无法确定运行时实际调用的方法(C++也因此不支持虚模版函数)。

2.不支持对泛型类的P/Invoke。

3.目前不能使用反射中的Property.SetInfo给非空类型赋值。

4.值类型作为Dictionary的Key时会有问题.

实际上实现了IEquatable的类型都会有此问题,因为Dictionary的默认构造函数会使用EqualityComparer.Default作为比较器,而对于实现了IEquatable的类型,EqualityComparer.Default要通过反射来实例化一个实现了IEqualityComparer的类(可以参考EqualityComparer的实现)。 解决方案是自己实现一个IEqualityComparer,然后使用Dictionary<TKey, TValue>(IEqualityComparer)构造器创建Dictionary实例。之前GC优化篇有提过。

5.由于不允许动态生成代码,不允许使用System.Reflection.Emit,不允许动态创建类型。

由于不允许使用System.Reflection.Emit,无法使用DLR及基于DLR的任何语言。 不要混淆了Reflection.Emit和反射,所有反射的API均可用

–EOF–

]]>
优化IL2CPP的build时间 2017-07-07T00:00:00+00:00 王在京同学 http://peakcoder.com/unity3d/2017/07/07/il2cpp 编译IL2CPP版本耗时太长,尤其是Android版本,公司项目出一个Android IL2CPP版本的build时间大概是70分钟。以下是Unity官方的优化方案。

使用 ‘incremental building’
如果使用 incremental building, C++ compiler 只会重新编译上次改变的文件. 使用 incremental building,只需编版本的路径和上次相同即可(之前的build的文件夹不能删除)

不要让反恶意软件工具扫描你的工程目录和build target目录
编版本之前关闭反恶意软件工具能提高build效率。(Unity Technologies 自己测试说在一台新装的Win 10 上关掉Windows Defender,build时间降低了50-60%)

工程目录和build target目录放到SSD上
SSD读写速度比HDD快的多,IL文件转到C++过程中需要大量的硬盘读写操作,用SSD会加快这个过程。

官方文档:https://docs.unity3d.com/Manual/IL2CPP-OptimizingBuildTimes.html
–EOF–

]]>
Xcode error 'MapFileParser.sh Permission denied' 2017-06-22T00:00:00+00:00 王在京同学 http://peakcoder.com/unity3d/2017/06/22/mac-compile-error 公司的项目很大,而我的macbook ssd 只有128G。为了调试公司Unity项目的iOS版本,我都是先在PC上编译好,然后拷贝到mac编译。 这样做没什么问题,但是会有一些脚本文件失去可执行权限,比如可能会报以下的错:

'MapFileParser.sh: Permission denied - not set as executable'

解决方法也比较简单,再将文件赋予可执行权限即可。

chmod +x /path/to/MapFileParser

–EOF–

]]>
Unity Reimport 'UnityEngine.UI.dll' 2017-03-14T00:00:00+00:00 王在京同学 http://peakcoder.com/unity3d/2017/03/14/reimport-dll 代码在切换分支后,Unity工程下Library文件夹(如果没有加入到版本控制中)可能会被污染,导致各种Unity的dll文件找不到,例如:

"Asset 'C:/Program Files (x86)/Unity/Editor/Data/UnityExtensions/Unity/GUISystem/4.6.0/UnityEngine.UI.dll'

Unity的dll文件不能单独reimport,只能remiport all,虽然能解决问题,但是太慢太慢了,尤其是Android平台Load贴图,非常浪费时间。
一番google之后,在Unity的坛子里,网友’j-robichaud’贡献了脚本,解决了问题,如救世主一般,感动。

using UnityEngine;
using System.Collections.Generic;
using UnityEditor;
using System.Text.RegularExpressions;
using System.IO;
using System.Text;
 
public class ReimportUnityEngineUI
{
    [MenuItem( "Assets/Reimport UI Assemblies", false, 100 )]
    public static void ReimportUI()
    {
#if UNITY_4_6
        var path = EditorApplication.applicationContentsPath + "/UnityExtensions/Unity/GUISystem/{0}/{1}";
        var version = Regex.Match( Application.unityVersion,@"^[0-9]+\.[0-9]+\.[0-9]+").Value;
#else
        var path = EditorApplication.applicationContentsPath + "/UnityExtensions/Unity/GUISystem/{1}";
        var version = string.Empty;
#endif
        string engineDll = string.Format( path, version, "UnityEngine.UI.dll");
        string editorDll = string.Format( path, version, "Editor/UnityEditor.UI.dll");
        ReimportDll( engineDll );
        ReimportDll( editorDll );
               
    }
    static void ReimportDll(string path )
    {
        if ( File.Exists( path ) )
            AssetDatabase.ImportAsset( path, ImportAssetOptions.ForceUpdate| ImportAssetOptions.DontDownloadFromCacheServer );
        else
            Debug.LogError( string.Format( "DLL not found {0}", path ) );
    }
}

–EOF–

]]>
Jenkins 'Waiting for next available executor' 2017-01-12T00:00:00+00:00 王在京同学 http://peakcoder.com/unity3d/2017/01/12/jenkins-error 用Jenkins部署Unity3d项目自动编译,新建一个Job,配置完成后,点击构建一直提示:

连接等待中 - Waiting for next available executor

一番google之后发现原因可能有几个:一个是节点offline了,一个是节点disk不够了,还有number of executor是0。 但是我去[系统管理->进入节点->节点管理]里看了下,都是好的。

没办法只能用重启大法了
jenkinsurl/safeRestart等待现有构建完成后重启。
jenkinsurl/restart直接重启。
重启之后就好了。

嗯,重启大法好。

–EOF–

]]>
Unity3d 'Fatal signal 11 (SIGSEGV), code 1' 2017-01-07T00:00:00+00:00 王在京同学 http://peakcoder.com/unity3d/2017/01/07/unity-crash 项目一切都是OK的,接入某渠道SDK后,打开app 就 crash。人啊,这一辈子注定要遇到几个 ‘SIGSEGV’,摊手。

01-05 18:48:12.124: A/libc(727): Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 776 (UnityMain)
01-05 18:48:12.124: E/libEGL(727): call to OpenGL ES API with no current context (logged once per thread)
01-05 18:48:12.234: A/DEBUG(533): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
01-05 18:48:12.234: A/DEBUG(533): Build fingerprint: 'samsung/c5pltezc/c5pltechn:6.0.1/MMB29M/C5000ZCU1APJ4:user/release-keys'
01-05 18:48:12.234: A/DEBUG(533): Revision: '10'
01-05 18:48:12.234: A/DEBUG(533): ABI: 'arm'
01-05 18:48:12.234: A/DEBUG(533): pid: 727, tid: 776, name: UnityMain  >>> com.kongzhong.c1.uc <<<
01-05 18:48:12.234: A/DEBUG(533): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
01-05 18:48:12.254: A/DEBUG(533):     r0 9ebc82a8  r1 00000008  r2 00000000  r3 00000000
01-05 18:48:12.254: A/DEBUG(533):     r4 a169ec36  r5 9ebc7010  r6 9eaadb10  r7 fffff0d4
01-05 18:48:12.254: A/DEBUG(533):     r8 00001298  r9 0000002b  sl 00000001  fp 00000002
01-05 18:48:12.254: A/DEBUG(533):     ip 0000002b  sp a082e378  lr ffffffff  pc a0d01c3c  cpsr 60070010
01-05 18:48:12.254: A/DEBUG(533): backtrace:
01-05 18:48:12.254: A/DEBUG(533):     #00 pc 004d2c3c  /data/app/com.kongzhong.c1.uc-1/lib/arm/libunity.so
01-05 18:48:12.254: A/DEBUG(533):     #01 pc 00333cb4  /data/app/com.kongzhong.c1.uc-1/lib/arm/libunity.so
01-05 18:48:12.254: A/DEBUG(533):     #02 pc 0030e0d8  /data/app/com.kongzhong.c1.uc-1/lib/arm/libunity.so
01-05 18:48:12.254: A/DEBUG(533):     #03 pc 0035e680  /data/app/com.kongzhong.c1.uc-1/lib/arm/libunity.so
01-05 18:48:12.254: A/DEBUG(533):     #04 pc 00509e58  /data/app/com.kongzhong.c1.uc-1/lib/arm/libunity.so
01-05 18:48:12.254: A/DEBUG(533):     #05 pc 0050cc00  /data/app/com.kongzhong.c1.uc-1/lib/arm/libunity.so
01-05 18:48:12.254: A/DEBUG(533):     #06 pc 003f3441  /data/app/com.kongzhong.c1.uc-1/oat/arm/base.odex (offset 0x21f000)

毫无头绪,看backtarce,挂在unity的C++代码里,鬼知道是什么,当然可以用ndk-stack tool来看具体的堆栈信息,如何使用 参考链接,但并没有任何有价值的信息。

SIGSEGV之前有可能还会遇到说GL找不到上下文的报错。

call to OpenGL ES API with no current context (logged once per thread)

一开始以为Unity开了Multithreaded Rendering,导致crash。 关掉了之后,确实不crash了,但是屏幕全粉了,心想要么材质掉了,要么shader跪了,开始报下面错:

-------- GLSL link failed, no info log provided.

一番google之后,在Unity 坛子里名叫haruki_tachihara的网友(貌似霓虹国友人)解决了我的问题,很简单
it was resolved by loading the game scene after the empty scene.
竟然添加一个空场景中转一下就好了,而且多线程打开也没问题了,就这样所有问题都好了,也不crash了。现在想想大概是渠道sdk在splash界面就初始化, 把UnityMain进程挂起导致的。 现在国内渠道为了让接入的游戏打开就先显示自己的logo,也是操碎了心。

–EOF–

]]>
错误的Import Settings会拖垮你的游戏[Part 2] 2016-12-29T00:00:00+00:00 王在京同学 http://peakcoder.com/unity3d/2016/12/29/importsettings2 [Part 1]里说到贴图的improt settings会影响性能,这次来说下Audio Clip的import settings.

Unity的默认设置大部分情况下是ok的,当然默认设置有些情景下不是最优的,很不幸Audio Clip Import Settings 默认设置就蛮糟糕。

与贴图的import settings 类似. Unity 支持各种音频格式,目前,Unity 5.2.1 支持 PCM, ADPCM, Vorbis/MP3 和 HEVAG. 并不是每个平台都支持所有的压缩格式,有些只支持一种(WebGL只能用AAC)

主要还是Memory matters吧

Default Import Settings:
Load Type: Decompress On Load
Compression Format: Vorbis

默认设置会占用非常大的内存!如果有很多音频文件移动设备可能顶不住。

Load Types: Compressed In Memory – Audio Clip 存储在RAM里,播放时解压. 播放时不需要额外的内存 Streaming – Audio Clip 存储在 persistent memory (hard drive, flash drive etc)里.存储和播放都不需要RAM(播放时需要极少). Decompress On Load – Audio Clip 无压缩存储在RAM里. 需要最多的RAM,但是不耗CPU 到底选哪个视情况而定.

Music and/or Ambient Sounds

音乐、BGM、环境声音一般比较大,存到RAM里很占内存,我们肯定不选 Decompress On Load, 至少要压缩过放到RAM里。

所以有两种选择

1.Load Type: Streaming Compression Format: Vorbis. 这个组合是最不占用内存的,但是耗CPU和磁盘I/O.

2.Load Type: Compressed In Memory Compression Format: Vorbis. 跟第一种的区别是把I/O占用换成一部份内存占用了。

你可以调整 Quality 来降低大小,100%最高,一般推荐70%.空间换时间,代价就是这个比较耗CPU,可以在profiler里看下。

Sound Effects

Sounds effects 一般为中短型. 播放频率要么很多,要么很少。

播放频率高而且很短的使用 Decompress On Load,Compression 格式 使用PCM 或者 ADPCM. 选择PCM就不用解压了,音频很短,也会load很快. ADPCM需要解压,但比Vorbis轻量。

播放频率高中型长度的使用 Compressed In Memory 和 ADPCM. ADPCM 差不多比PCM小3.5倍,解压比Vorbis快的多。

播放频率低而且很短的使用 Compressed In Memory 和 ADPCM.

播放频率低中型长度的使用 Compressed In Memory 和 Vorbis. 用ADPCM可能还是会太占用RAM,反正不经常用,不会很耗CPU.

总结

build 版本前check 下 Audio Clip Import Settings.

个人翻译.转载请注明出处.原文

–EOF–

]]>
错误的Import Settings会拖垮你的游戏[Part 1] 2016-12-28T00:00:00+00:00 王在京同学 http://peakcoder.com/unity3d/2016/12/28/importsettings1 游戏开发过程中需要注意的事情有很多。目标平台可能会因为你的模型面数太多而难以运行;你的算法可能会很耗CPU; 你可能用了太多的材质,Unity不能更高效的batching。这些都是棘手的问题,作为游戏开发者从始至终都要把握好视觉效果和性能之间的平衡。 然而,有些我们忽视了的小事儿,也对性能有显著的影响。

Texture Import Settings

当你导入一张贴图到你的工程里的时候,Unity会基于当前的 texure import settings 将其转成一种合适的格式,在大多数情况下这就足够了, 但是直接就这样发布给玩家可能不够好,这就是我们要说的地方。选中一张texture,在Inspector里就能看到她的import settings.

有三件事情需要注意

Texture Type

Texture Type 主要是用来告诉Unity这张贴图是用来做什么的,所有的选项除了「Advanced」都会调整贴图的内部设置而达到各自最佳的目的。 贴图的「Texture」是一种简单的diffuse类型, 用来给3d空间和3d游戏的。贴图的类型主要作用可以在Unity官方manual里找到。

The risk: 设置了错误的贴图类型,一开始可能没有察觉,就会导致性能损失。有些类型是必须设置的,因为shader需要这种类型才能工作(比如用作bump mapping的贴图必须设置为「Normal map」类型)。

Size

贴图的大小和压缩格式

这些设置会决定你的贴图怎样在ram里存储,有多大。这里是Max size 而不是 size,因为你可是在Quality Settings里设置所有贴图的分辨率。 即便设置max size 为2048也不能保证这张图是2048的,就算uality Settings 设置为 highest,分辨率也可能比2048小,因为你原始贴图没有这么大 (感觉在说废话啊)。其实你的贴图是可以设置的比预期大一点,反正可以直接设置所有的quality。

The Risk: 有些模型在场景里显示的很小,但是贴图用的是高分辨率的。你能通过scene -> Mipmaps 筛选出这些物件(变红的就是过大分辨率的贴图)。

Format

除了「Advanced」有四种格式

·Compressed – GPU 付出一点点换来贴图大瘦身,Compressed 会为你的目标平台选择一种最合适的压缩算法.

·16 bits – 无压缩 16 bit,适用于色彩少的。

·Truecolor – 无压缩 32 bit 真彩色. 看着爽, 但一张2048x2048 17M.

·Crunched – 用支持GPU压缩格式的方式压缩了一遍,然后再用CPU能够处理的格式再压缩一遍,下载Asset bundle的贴图比较适用。

建议就是除非有什么特殊目的直接用Compressed.

格式「advanced」还没说,设置为「advanced」能够设置更多的压缩格式, 但很多压缩格式有些平台是不支持的(比如Android好多设备支持不同的压缩格式,但应该都支持ETC).

The Risk: 压缩过的贴图有时候蛮丑的,16bits和Truecolor倒是好看,但占用更多的内存,你可以在build完之后查看editor.log,看图片到底占了多大空间。

个人翻译.转载请注明出处.原文

–EOF–

]]>