踩坑合集
- 按照我们的语言习惯,在表示小数时,用点号“.”表示小数点,用逗号“,”表示分位符,如1,111.1
但在越南和一些欧洲国家如法国德国等则是反过来的,用逗号“,”表示小数点,用点号“.”表示分位符, 如1.111,1
另外更特别的是波斯语中是用“/”表示小数点!
因此,你在保存数字时或解析字符串时则需要注意该语言差导,否则会有意相不到的bug等着你。
CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");或者.ToString(CultureInfo.InvariantCulture)简单处理可以这样统一语言环境。 - 2023.02.27 00:00:00这种时间格式字符串在印尼等国家语言环境下DateTime.Parse(str)解析失败,类似上一条需要固定区域:
DateTime.Parse(timeStr, System.Globalization.CultureInfo.InvariantCulture)
- TextMeshPro中文显示会遇到只能显示一部分的情况,报错如下:
UnassignedReferenceException: The variable m_AtlasTextures of TMP_FontAsset has not been assigned. You probably need to assign the m_AtlasTextures variable of the TMP_FontAsset script in the inspector.
文字消失就是当默认的第一张图集被塞满了后生成第二张图时m_AtlasTextures为空出错而生成失败导致的。
m_AtlasTexture首次运行时本身就为空,但如果ui上有用到该字体就会走到以上代码就会被初始化,所以多数人遇不到。
而我们项目主要使用英文,中文是通过设置fallback字体来实现的,所以没有直接用到该字体也就不会被初始化。
也好解决,在你第一个ui上增加引用就行,或者自己手动调用该属性来解决,或源代码中用m_AtlasTexture改为用属性atlasTexture也解决了,但改代码得从upm中移出来。
期待Unity官方修复这个bug(我在官方论坛回复了问题原因,但并没有得到回应)。 - C#调用Process StartInfo执行命令行时在Windows下需要在Arguments参数上命令前增加/c才能正确执行。
而Mac上面则分情况,bash命令如ls、svn等则需要在命令前增加-c指令且需要把命令用引号包起来:-c "ls -l",如果仍然执行失败则需要再添加-l指令(脚本调起的可能为非交互式非登录Shell下的环境变量只有最基础的几个)来指定为当前用户的shell:-l -c "svn info";
若是代码调用外部shell脚本失败,可考虑将脚本路径改为全路径,相对路径可能不行,仍有问题则可能也需要加上-l -c,如果仍然有问题,则.command脚本首行也需要通过i l指定shell:#!/bin/bash -ilex,对于e参数表示一旦出错,就退出当前的shell,x参数表示可以显示所执行的每一条命(x视情况是否需要)。 - C#调用Process StartInfo执行命令行时获取的结果输出会乱码:
指定输出编码格式可解决:
p.StartInfo.StandardOutputEncoding = Encoding.UTF8;
p.StartInfo.StandardErrorEncoding = Encoding.GetEncoding(CultureInfo.CurrentCulture.TextInfo.OEMCodePage);
网上大部分都说设置UTF8即可,但自测发现UTF8只有在MAC苹果电脑上可以解决乱码问题,在windows电脑上寻常编码格式全都试过了都会出错,最后通过以上下面那行加粗代码动态获取编码格式才得以解决:故需要用宏将不同平台设置为不同的编码,StandardOutputEncoding和StandardErrorEncoding最好都同时设置为同样的编码格式。
-
float Epsilon = 1e-10;该行shader代码在ipad 5和ipad mini 4机型上发现显示异常,有一些黑色块出现,而其他设备没有问题。应该是ipad部分机型硬件问题,不支持e这做写法。
解决办法即将1e-10改为0.0001,另外需要确保该变量定义需要在函数内部而不能在外部,如果需要在外部定义常量给多个函数共用则需要用#define宏定义。 -
粒子在Prewarm勾上时,编辑器运行与非运行状态下在同一样的时间时效果不一样,即预热的那段时间会带来显示上的差异,ParticleSystem.Simulate()函数指定时间时可能跟你在编辑器制作时停留时间预览不一样,如果需要运行时跟编辑时一致,建议取消Prewarm。
- 当有很多的2D UI图片要显示时可以考虑用UGUI的CavasRenderer手动传入SubMesh的方式来优化(必须同时挂上Canvas组件,否则会巨卡)。
但底层限制了数量最多只能有8个子Mesh,当有大量子Mesh要显示时就只能转为普通3D的MeshRenderer来显示了,性能会好很多。
当3D物体也需要被UGUI的Mask或者RectMask2D遮罩时会有问题,当MeshRenderer用UI相同的shader时:
* 配合Mask组件使用时,即便Mesh上的材质指定为同被遮罩的UI元素一样的stencil值遮罩不正常,整个Mesh都会不可见,原因为UGUI在Mask功能渲染完毕后又会把stencil的值清空,你的Mesh读到的值就是错误的。需要自己手动指定遮罩区域的sencil值,等于需要自己实现Mask组件的相应功能,包括嵌套Mask的情况。
* 配合RectMask2D组件使用时,需要手动传入Mesh上材质的_ClipRect裁剪区域坐标,如果在ScrollRect滑动列表中则还需要实时修改裁剪区域坐标,而UGUI传入的UI自身的相对坐标,将UI上能被正常裁剪的_ClipRect复制到Mesh上材质是有问题的,Mesh上需要传入的坐标为另一参考系(手动改值慢慢尝试发现类似世界坐标,明显比UI上的值小个数量级),未深入研究不清楚规律。 - xLua调用Unity的动画类Animation时如果不生成代码则只能反射调用,结果部分函数如animation.GetClip()、animation.Stop()会找不到函数而出错,必须配置LuaGenConfig生成代码后调用才能成功。
- Unity内播放视频文件最简单的方法就是使用VideoPlayer组件。
坑1:同为mp4文件查看属性时也有h.264的说明,但其编码格式可能android机上播放不了(ios上可以)(不同视频编辑软件可能功能不一样也许默认导出的mp4也能播放,此处测试为Mac机上某软件有MPEG4/H.264/H.265三种选择)。
解决办法:在导出视频时需要显式指定为h.264(推荐)或h.265(不推荐)(垃圾设备可能支持不好且视频宽高有限制不能超过2300多否则需要额外解码器支持)或者在Unity中将视频文件勾上Transcode进行转码(转码如果选择为高配置则文件大小可能会倍增)。
坑2:在Android 9以下的设备不支持直接播放AssetBundle中的视频文件,报错:W/Unity: AndroidVideoMedia::OpenExtractor could not translate archive:/CAB-de617f9f2b6b7a4679af2e89406b6bab/CAB-de617f9f2b6b7a4679af2e89406b6bab.resource to local file. Make sure file exists, is on disk (not in memory) and not compressed.
W/Unity: AndroidVideoMedia: Error opening extractor: -10004尝试过Unity中打AB包时选择Uncompress不压缩,且Android build.gradle文件中noCompress配置上所有.ab文件都不压缩,仍然播放不了。
将视频文件放到StreamingAssets目录通过VideoPlayer.url使用,在低端Andorid设备上无法播放(不确定是否为Android 9以下,测试机型为Android 7)
解决办法:视频文件直接放到Resources目录中,或者将视频文件从AB包中提取出来复制到其它缓存目录中(通过UnityWebRequset网络请求本地地址加载拷贝AB包或将视频文件后缀修改为.bytes直接加载AB包获得数据复制内容)。
坑3:高分辨率(宽高为1080*2400)在低端Android机上播放不了(不确定是否为Android 9以下,测试机型为Android 7)。
解决办法:修改原始视频文件高度(不清楚具体限制是多少),或者在Unity中将视频文件勾上Transcode进行转码,把Dimensions选择为3/4或更低(画质会有影响)。 - lua侧加载及读取Excel配置表数据有多种实现,最简单的方式为数据档直接保存为.bytes二进制文件,使用lua protobuf库直接加载;另外则是生成对应的lua文件直接require之。
当表数量多且部分表有几十M甚至更大时则需要考虑时间与空间的取舍:
生成的Lua文件有几十M时,使用lua语言去加载解析lua文件则效率相当低,此时只能牺牲空间换取时间,使用bytes二进制是更优的方案,pb库是C语言实现,加载和解析都更快,但数据量大则必定lua的table巨多最后lua侧占用很多内存。
其他大量的小配置文件则可以用时间换空间,当数据量少时用二进制文件读取的速度提升可能并不明显,而用lua文件保存数据则可以采用更优的生成算法优化lua table的生成:1 local defaults = { 2 activityId = 2, 3 aniType = 2, 4 num = 1, 5 } 6 7 local duplicates0 = { 8 t0 = {["type"] = 2,["id"] = 90053,["count"] = 1,}, 9 t1 = {["type"] = 2,["id"] = 90054,["count"] = 1,}, 10 t2 = {["type"] = 2,["id"] = 90095,["count"] = 1,}, 11 t3 = {["type"] = 2,["id"] = 90112,["count"] = 1,}, 12 } 13 14 local duplicates1 = { 15 t4 = {["eff"] = [=[]=],["width"] = 0.0,["height"] = 0.0,}, 16 t5 = {--[[1]]0.8,--[[2]]1.0,}, 17 t6 = {--[[1]]1.0,--[[2]]1.2,}, 18 t7 = {--[[1]]duplicates0.t0,}, 19 t8 = {--[[1]]0.8,--[[2]]1.2,}, 20 t9 = {--[[1]]duplicates0.t1,}, 21 t10 = {["eff"] = [=[Angel/eff_angel]=],["width"] = 456.922,["height"] = 553.171,}, 22 t11 = {--[[1]]0.5,--[[2]]0.8,}, 23 t12 = {--[[1]]1.0,--[[2]]1.0,}, 24 t13 = {--[[1]]duplicates0.t2,}, 25 t14 = {["eff"] = [=[Joker/eff_joker]=],["width"] = 456.922,["height"] = 553.171,}, 26 t15 = {--[[1]]duplicates0.t3,}, 27 } 28 29 local duplicates2 = { 30 t16 = {["refreshWeight"] = 1,["pic"] = duplicates1.t10,["rect"] = duplicates1.t4,["catchProb"] = 100.0,["num"] = 2,["size"] = duplicates1.t11,["transparent"] = duplicates1.t12,["reward"] = duplicates1.t13,}, 31 t17 = {["refreshWeight"] = 1,["pic"] = duplicates1.t10,["rect"] = duplicates1.t4,["catchProb"] = 80.0,["num"] = 3,["size"] = duplicates1.t11,["transparent"] = duplicates1.t12,["reward"] = duplicates1.t13,}, 32 t18 = {["refreshWeight"] = 1,["pic"] = duplicates1.t14,["rect"] = duplicates1.t4,["catchProb"] = 100.0,["num"] = 2,["size"] = duplicates1.t5,["transparent"] = duplicates1.t12,["reward"] = duplicates1.t15,}, 33 t19 = {["refreshWeight"] = 1,["pic"] = duplicates1.t14,["rect"] = duplicates1.t4,["catchProb"] = 80.0,["num"] = 3,["size"] = duplicates1.t5,["transparent"] = duplicates1.t12,["reward"] = duplicates1.t15,}, 34 } 35 36 local duplicates3 = { 37 t20 = {--[[1]]duplicates2.t16,--[[2]]duplicates2.t17,}, 38 t21 = {--[[1]]duplicates2.t18,--[[2]]duplicates2.t19,}, 39 } 40 41 42 local raw = 43 { 44 PbMT(defaults, { 45 ["id"] = 105, 46 ["activityId"] = 1, 47 ["type"] = { 48 { 49 ["refreshWeight"] = 1, 50 ["pic"] = { 51 ["eff"] = [=[ghost/eff_ghostH_5_ani]=], 52 ["width"] = 292.637, 53 ["height"] = 259.894, 54 }, 55 ["rect"] = duplicates1.t4, 56 ["catchProb"] = 70.0, 57 ["num"] = 3, 58 ["size"] = duplicates1.t8, 59 ["transparent"] = duplicates1.t5, 60 ["reward"] = duplicates1.t9, 61 }, 62 }, 63 ["aniType"] = 1, 64 }), 65 PbMT(defaults, { 66 ["id"] = 201, 67 ["type"] = duplicates3.t20, 68 }), 69 PbMT(defaults, { 70 ["id"] = 202, 71 ["type"] = duplicates3.t20, 72 }), 73 ...
通过将出现最多的值作为默认值、复用同样数据的table可以积少成多节省不少内存,也可加快解析的速度。
另外分享一下另一种优化,因为配置表中所有的字段是确定且不变的,可以改为不用key做键,直接使用proto里定义的数字,这样就少了大量的文本字段,数字连续时也变成了数组,大大降低的文本大小及运行时的内存大小,但是!每次读取都需要转换一次找到实际的key的id,当数据表量大时导致运行时的速度降低很多,只能放弃……示例如下: -
Unity的图片选项中Alpha Source有些误导人,给人感觉是只要我选了None它就应该自动给我去掉alpha通道节省至少1/4的内存:
然而实际该选项仅在ASTC格式下起作用:
这可能就是为什么ASTC的命名中A写在括号中,它是可选的,但也只是对表现有效,图片不再有半透效果,实际内存占用并不能减少!
其它格式则由格式本身带不带A通道决定了,与Alpha Source完全没有关系。
综上,Alpha Source大多数情况下都是可以不用管的,仅当需要使用ASTC格式且图片本身就有透明像素在显示时需要去掉透明像素的情况,或者使用平台自动决定格式时。 - UGUI上双指(三指及以上不触发点击)同时点击或者快速多次点击同一个按钮(或不同按钮)都会多次触发onClick事件!即便你在点击事件处理中立即打开了一个全屏UI阻挡点击也没有用(可能因为这多个点击事件是在同一帧触发,而UI表现层在当前帧设置后并不能立即生效),
要解决事件并发需要在事件回调处理中增加多点触控的判断如:if ((Input.touchCount <= 1) clickCallback(),
不能使用IPointerClickHandler点击回调参数给的PointerEventData中的clickCount来判断点击次数,其只在电脑上即鼠标操作时有效,触摸屏下永远是1,而Input.touchCount在鼠标下一直是0在手机屏幕下为手指个数或连点次数。 - 图片压缩格式经验总结:
ASTC压缩比在不同图片下不一样,时高时低,在多数情况下画质显示效果好,无图尺寸要求,在极少数android低端机型上不支持,会自动使用RGBA32原画质显示,此时内存占用会很大;
ETC2压缩比一般,显示效果较好,画质在少数情况优于ASTC,要求图的尺寸必须是4的倍数;
CrunchedETC2压缩比高,画质在小图时较好,大图有些比ASTC更好有时更差,要求图的尺寸必须是4的倍数;
内存占用多数情况下ASTC8会优于CrunchedETC2,ASTC4则会更高,不同情况不一样;
总的来说画质和内存表现上不会有特别明显的差距,跟图片上本身像素的情况有关,但综合看来ASTC是更优的压缩格式。
如果项目不需要支持少数低端机型,图片又不是超级多小图,建议无脑选择ASTC。
有大量小图时则优先选择CrunchedETC2,其他大图使用ASTC,使用工具批量将图全部转为4倍大小。
普通ETC2较少使用,除非嫌CrunchedETC2效果不好又需要支持全部低端机型。
- 在打完AssetBundle包,之后的第一次运行unity(重加载域时)或者触发代码编译的时候会在Reload Script Assemblies卡特别久:
第二次及以后就正常了不会再卡进度条。打AB包时使用的是upm里的库Scriptable Build Pipline,用原旧api打包则不会有这问题。
解决办法:删掉Library/BuildCache整个文件夹。看起来是SBP库有bug,当缓存过多时会导致这个问题。