如何获取Unity导航系统中某点的高度

如何获取Unity导航系统中某点的高度

本期聚集话题:如何在Unity自带导航系统中获取某一点的高度、GPU Instancing 采样不同纹理、优化Text.get_cachedTextGenerator的堆内存...


这是第115篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)


导航网格

Q:我们现在正在做基于Unity自带导航系统的寻路逻辑,但是不知道如何获得Unity自带导航模型在某一点的高度,就没法正确设置角色y值。目前想到的办法是自己生成一套Heightmap和一套导航模型,先对Heightmap进行高度查询,Heightmap查询失败再进行导航模型的射线检测。但是这么做会增加一些内存,不知道Unity有没有内置的方法去获得导航某点高度?

1)之前都是用的NavMesh.SamplePosition,从较高的点投射线,在射线上取点作为球心调用SamplePosition。
2)还一种方法是,让美术做Mesh Collider,专门用来接收测试高度的射线。
https://docs.unity3d.com/540/Documentation/ScriptReference/NavMesh.SamplePosition.html
感谢史云柯@UWA问答社区提供了回答

之前做过类似的东西,但是没有用NavMesh,所以不敢肯定效率是否比NavMesh.SamplePosition好。

方案:在Editor中做预处理,将场景中带碰撞的对象合并成一个Mesh(因为有些不是MeshCollider,而是Box、Sphere、Capsule,这种Mesh要自己生成),然后将问题转化为射线与三角面求交的问题(对于MeshCollider的射线检测,本质上就是这个问题)。
当然这是最基本的,更进一步:
1)相邻的面如果法线相近,就合并成一个多边形,于是又转化为射线与多边形求交的问题。
2)为多边形计算AABB,先用AABB进行检测。
3)对多边形进行排序,面积大的优先进行检测。
4)未实现的:将多边形投影到XZ平面上,y值只作存储,不参与求交运算。
5)未实现的:可以尝试使用四叉树进行空间分割。
感谢凯奥斯@UWA问答社区提供了回答

以前也遇到过这个问题,我尝试过各种方式,Unity并没有给出很好的解决方案,直接调用Navmesh.SamplePosition是很容易取到错误的位置,因为它是按照球体碰撞来获取最近的点,而且这个函数随着半径增加,性能急剧下降,测试过在1.5米半径下消耗是差不多的。所以获取高度,最好还是以物理碰撞未准,那么生成这个Mesh碰撞才是关键,体质可以使用Navmesh.CalculateTriangulation这个函数去获取已经刷好的Navmesh区域,生成Mesh。可以参考:
https://blog.csdn.net/huutu/article/details/52672505
感谢简简单单@UWA问答社区提供了回答

欢迎大家转至社区进行进一步交流:
https://answer.uwa4d.com/question/5b0e1fec09749726e4188d4f


UI

Q:在UWA Mono堆内存分析报告中发现我们逻辑代码中我们发现Text.get_cachedTextGenerator 这个函数居然占用了将近6MB的堆内存,请问这里可以怎么优化?
2501(0).png

每个Text组件都会执行一次构造函数,即TextGenerator…ctor。图片中该堆内存较高,说明题主项目中的Text组件太多了,建议对UI界面中类似的信息进行详细排查。

此问答由UWA提供,欢迎大家转至社区进行进一步交流:
https://answer.uwa4d.com/question/5b178c0d54646026e9ddd737


渲染

Q:GPU Instancing能够支持每个实例采样不同的纹理吗?我试了下类似这样的做法,但是编译不过:

UNITY_DEFINE_INSTANCED_PROP(sampler2d, _MainTex)

这是不被支持的,纹理不能被用来作为instanced的属性…建议题主了解下Texture2DArray,可以通过脚本创建一个Texture2DArray,然后把Array的Index作为instanced的属性传到Shader中,这样就可以通过不同的index来采样不同的纹理了。

感谢Bunnyisrunning@UWA问答社区提供了此回答,欢迎大家转至社区进行进一步交流:
https://answer.uwa4d.com/question/5b1e22f454646026e9ddd77d


编辑器

Q:有什么好办法可以在脚本的某个值在Inspector里被修改的时候触发一个函数吗?主要是希望在改数值的时候,做一些检测和提醒的功能。

题主可以用OnValidate这个事件:https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnValidate.html
感谢Bunnyisrunning@UWA问答社区提供了回答

题主可以了解下OnInspectorGUI
或者https://github.com/LMNRY/SetProperty
感谢凯奥斯@UWA问答社区提供了回答

重写Inspector视图,绑定脚本。

[CustomEditor(typeof(Test))]
public class TestEditor : Editor
{
public override void OnInspectorGUI()
{
Test test = (Test)target;
test.isSure= EditorGUILayout.Toggle(“是否确定”, test.isSure);
test.Name= EditorGUILayout.TextField(“名字”, test.Name);

    if (GUI.changed)
    {
        Debug.Log("change");
    }
}
}

感谢凌乱@UWA问答社区提供了回答

欢迎大家转至社区进行进一步交流:
https://answer.uwa4d.com/question/5b1e3e0e54646026e9ddd781


逻辑代码

Q:自己写的插件在IL2CPP下出现问题。先上代码:

byte[] bytes = System.Text.Encoding.Default.GetBytes(strMsg);
int ret = FTCode.ftcode(seed, 1, ref bytes, bytes.Length);

ftcode声明

[DllImport(LibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int ftcode(int _seed,int type, [MarshalAs(UnmanagedType.LPArray)] ref byte[] data,int length);

打了一个so放Plugins/Android/libs下。在Mono下调用ftcode能正常得到想要的结果,即传进去一个20长度的byte数组,经过加密后能正常得到20长度byte数组,但是在IL2CPP下调用这个bytes的长度就变成了1。 这个是转CPP后的Bug吗? 有没有人遇到过呀?

换了参数
int ftcode(int seed, int type, void* data, int length)

string strMsg = “{“userId”=111}”;
byte[] bytes = System.Text.Encoding.Default.GetBytes(strMsg);
GCHandle hObject = GCHandle.Alloc(bytes, GCHandleType.Pinned);
IntPtr pObject = hObject.AddrOfPinnedObject();
int _ret = Utility.FTCode.ftcode(seed, 1, pObject, bytes.Length);

    Marshal.Copy(pObject, bytes, 0,bytes.Length);
    if (hObject.IsAllocated)
        hObject.Free();

感谢题主Let_Go@UWA问答社区分享了经验,欢迎大家转至社区进行进一步交流:
https://answer.uwa4d.com/question/5b14fd1909749726e4188dd9

今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
官方技术QQ群:793972859(原群已满员)

封面图片来源:谷歌