这是第151篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。
UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)
本期目录:
- 如何规划UI图标图集
- 如何在Unity中不使用Native DLL实现进程间通讯
- 如何通过??运算符给物体添加组件
- 摄像机跟随人物被建筑物遮挡半透方案
- 如何通过脚本向Timeline增加关键帧
UI
Q:道具图标可能有几百上千个,如果打到同一图集里,大小都有2048P,实际游戏过程中有额外的内存浪费。
按类型拆分也不太好,新手各类道具各一个,所有类型的图集就都引用了一遍,跟打到一个里没太大区别。
如果按等级段来规划,可能会好一点,但是对于像商城或者某些特定UI上展示的图标,很可能就不是同一个等级段的,也会造成引用好多个图集的情况。如果按常用或者不常用来分图集,得要策划全程来跟进这块内容,感觉也很容易出问题。
或者直接对这大量的图标都不打到图集,都是散图,这样虽然不会有太多内存浪费,但渲染效率就低了。
请问下有没有比较好的方案来处理这个问题?
A:1、如果图量远超单屏显示量,那比较实际的是自己实现动态图集,也就是图标是散图,在运行过程中根据需求实时拼装成大图,可以用Render to Texture实现。
2、对于大多数游戏,如果不是常驻画面的界面,以现在机器的性能,忍受一下DrawCall也非不可。
感谢招文勇@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5c70038e2b3589332f1a4ade
Script
Q:如何在Unity中不使用Native DLL实现进程间通讯?在Unity中实现IPC遇到了一些问题,主要是.Net 3.5 没有System.IO.MemoryMappedFiles 这个类,而我在制作Unity Editor插件时要用 File mapping的方式实现 IPC,需要普遍适配各个项目,必须兼容.Net3.5,这样似乎除了Import C++编写的DLL之外就没有什么好的方法了,但插件又是纯Editor在用的,所以不想因为这个原因导致游戏无关的DLL可能被打到Build里,所以想请教一下有没有兼容.Net 3.5的共享内核内存实现IPC的方法。
A1:1、纯C#可以socket做IPC,很通用且跨平台。
2、用Native DLL也可以选择是否打进包里,至少现在版本Unity可以。
感谢招文勇@UWA问答社区提供了回答
A2:一些和底层比较近的功能还是自己写Native插件吧,可以通过DLL的Import Setting和宏定义避免你说的问题。
PS:Unity对一些非渲染功能的封装大多效率不高也不太可控,其他诸如Camera、Microphone等等。
感谢志鹏Zipper@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5c6fd6142b3589332f1a4add
Script
Q:为什么通过??运算符给物体添加组件添加不上?
public class PlayerMoveTest : MonoBehaviour
{
CharacterController controller;
private void Awake()
{
controller = transform.GetComponent<CharacterController>() ?? gameObject.AddComponent<CharacterController>();
}
}
public class PlayerMoveTest : MonoBehaviour
{
CharacterController controller;
private void Awake()
{
controller = transform.GetComponent<CharacterController>();
if (controller == null)
{
controller = gameObject.AddComponent<CharacterController>();
}
}
}
如上面的代码,第一段是不行的,controller 显示为null,而第二段是可以添加上组件的,请问这是为什么?
对于??运算符,难道不是??前面的为null,就会执行??后面的表达式吗?
A1:我试了下:
public class PlayerMoveTest : MonoBehaviour
{
CharacterController controller;
private void Awake()
{
controller = transform.GetComponent<CharacterController>();
if (controller == null)
{
controller = gameObject.AddComponent<CharacterController>();
}
}
}
把上面代码反编译:
if (GUI.Button(new Rect(100f, 100f, 200f, 80f), "Test") && (Object)base.transform.GetComponent<CharacterController>() == (Object)null)
{
base.gameObject.AddComponent<CharacterController>();
}
if (GUI.Button(new Rect(200f, 200f, 200f, 80f), "Test2") && (object)base.transform.GetComponent<CharacterController>() == null)
{
base.gameObject.AddComponent<CharacterController>();
}
可以看出2段代码是不一样的,第一个比较的是UnityEngine.Object类型,第二个比较的是System.object类型。
再写段代码测试下:
var c1 = (object)base.transform.GetComponent<CharacterController>();
if(c1 == null)
Debug.Log(1);
var c2 = (Object)base.transform.GetComponent<CharacterController>();
if(c2 ==(Object)null)
Debug.Log(2);
if(c2 == null)
Debug.Log(3);
输出结果是2和3。
断点调试下这几个值:
原因是UnityEngine.Object类型重载了==运算符,所以"null"和null认为是相等的,而用System.object类型比较的时候认为是不相等的。
感谢deviljz@UWA问答社区提供了回答
A2:看了一下,发现此"null"非彼null:
其实在contor = transform.GetComponent();执行这句的时候已经赋值了,只不过赋的值是"null",看一下打印的就知道两个null有什么区别了。
再加一句,当我打印contor的Type时,在one那里,会报对象的引用未设置为实例,而在two的时候会打印他的类型,也就是说 ?? 操作符左边必须是为未赋值的变量时才会执行右边的,例子看下面的代码b,最后打印的是v,c是我定义的一个未赋值的string c。
感谢Marco@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5c7101bc63826a332ad9966c
Script
Q:摄像机跟随角色的时候被建筑物遮挡,想让建筑物半透,这个时候需要射线检测,但是因为性能问题,场景物体不允许添加Collider组件, 用纯Shader写的时候效果总是有些问题,想请教大神们有没有其他方案。
A1:一种做法是,场景做的时候用Box Collider做物体形状的近似来进行物理碰撞的检测,不知道你们不允许使用添加Collider组件是不是也不允许用这种方式。
不知道你纯Shader编写是什么思路,我感觉单纯Shader里是没办法处理这个需求的。
不知道有没有更好的方案,在比较通用又不做很tricky的处理的情况下,感觉物理反而是比较直接、高效的方式。
PS:如果是2.5D的视角,相机不会俯仰的情况下,可以预计算一份2D的数据在Runtime做检索用,不过这里面有限制和问题,我没这么做过,这里只是提一句。
感谢贾伟昊@UWA问答社区提供了回答
A2:在Unity 3D里面确实没有想到比Box Collider来做成场景物体的近似形状来进行物理检测更直接的办法了。
贾伟昊提到可以预计算一份数据做Runtime检测所用,我想起来在Quake系列里面,场景的渲染和物理碰撞检测数据是预先烘焙进了BSP树里面的,Quake在Runtime阶段的碰撞检测是基于BSP的。
感谢张锐@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5c74f9e163826a332ad996e5
Timeline
Q:在使用新的Timeline时发现,没有手动添加关键帧的选项/按键,也没有发现相关的API,那应当手动如何添加关键帧呢?先谢过各位~
A:在编辑器中,可以通过鼠标拉动Clip的两边来在Clip的头部或者尾部添加关键帧,并且编辑器中会显示出当前操作中+/-了多少帧。
如果是想Runtime向built-in的轨道添加关键帧,目前Unity确实没有提供相关的接口。
可以通过脚本来实现自定义的Track/Clip,在脚本中控制Timeline的Playhead停留在固定的时间,或者是跳到指定的时间。具体需要自己实现TrackAsset、PlayableAsset和PlayableBehaviour。
其中:
- 实现TrackAsset用于提供给外部的Mono脚本来操作Track。
- 实现PlayableAsset用于提供自定义的Clip,用于为Track提供相关数据。
- 实现PlayableBehaviour,实现控制Timeline播放的控制逻辑。
感谢张锐@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5c6666a92b3589332f1a4a34
今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。
官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
官方技术QQ群:793972859(原群已满员)
(封面图来源来源于网络)