在服务器端如何用JNI实现 NavMesh寻路
Posted 流子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在服务器端如何用JNI实现 NavMesh寻路相关的知识,希望对你有一定的参考价值。
上一节我们已经讲到的在服务器端使用 Easy3dNav来进行 NavMesh 来做寻路 3D寻路系统NavMesh-服务端篇,但性能上能否更快一点呢?众所周知,C++ 版本的性能向来比JAVA版本要出色,所以我们尝试用JAVA调用JNI native 接口来提升寻路的性能。
如何使用JNI技术来实现,JAVA对C++的调用呢?具体可以参考JNI全流程实例使用总结 这篇文章,本篇文章只提供C++端的实现逻辑。
JAVA端
/**
* 加载地图
*
* @param navmeshId 寻路数据地图ID
* @param content 地图文件的路径例
* @param length 数据长度
* @return navmeshId, 为0或负数表示加载失败,为正数表示加载成功,后续寻路时传入此id为参数
*/
public native int load(int navmeshId, byte[] content, int length);
/**
* 寻路
*
* @param navmeshId 寻路数据地图ID
* @param start 起始点
* @param end 结束点
* @param extents 搜索范围 用于起点终点是阻挡时,可以找到范围内非阻挡点
* @return 返回路径点列表,注意,查找不到时,会返回空
*/
public native float[] find(int navmeshId, float[] start, float[] end, float[] extents);
/**
* 找到目标点最近的静态可达点
*
* @param navmeshId 寻路数据地图ID
* @param point 参考点
* @param extents 搜索范围 用于起点终点是阻挡时,可以找到范围内非阻挡点
* @return 如果目标点可达,返回目标点即可, 如果搜索范围内没有,返回空
*/
public native float[] findNearest(int navmeshId, float[] point, float[] extents);
/**
* 光线照射发,寻找可以支线通过的hit点,如果可通过则返回hit
*
* @param navmeshId 寻路数据地图ID
* @param start 起始点
* @param end 结束点
* @param extents 搜索范围 用于起点终点是阻挡时,可以找到范围内非阻挡点
* @return 返回射线射过去遇到的第一个阻挡点,如果到终点都没有阻挡,返回终点
*/
public native float[] raycast(int navmeshId, float[] start, float[] end, float[] extents);
C++ 端:
这里只展示寻路的API实现:
/**
* 寻路
*
* @param navmeshId 寻路数据地图ID
* @param start 起始点
* @param end 结束点
* @param extents 搜索范围 用于起点终点是阻挡时,可以找到范围内非阻挡点
* @return 返回路径点列表,注意,查找不到时,会返回空
*/
JNIEXPORT jfloatArray JNICALL Java_com_gamioo_ooxx_GamiooJNI_find(JNIEnv* env, jobject jobj, jint id, jfloatArray start, jfloatArray end, jfloatArray extent)
jfloatArray ret= env->NewFloatArray(0);
float straightPath[MAX_POLYS * 3];
jfloat* startPos = (jfloat*)env-> GetFloatArrayElements(start, 0);
jfloat* endPos = (jfloat*)env->GetFloatArrayElements(end, 0);
jfloat* extents = (jfloat*)env->GetFloatArrayElements(extent, 0);
if (startPos == nullptr)
return ret;
if (endPos == nullptr)
return ret;
if (extents == nullptr)
return ret;
dtPolyRef startRef = 0;
dtPolyRef endRef = 0;
float startNearestPt[3];
float endNearestPt[3];
dtQueryFilter filter;
filter.setIncludeFlags(0xffff);
filter.setExcludeFlags(0);
NavMeshContex* navMeshContex =RecastGet(id);
//cout << navMeshContex->toString()<<endl;
navMeshContex->navQuery->findNearestPoly(startPos, extents, &filter, &startRef, startNearestPt);
navMeshContex->navQuery->findNearestPoly(endPos, extents, &filter, &endRef, endNearestPt);
dtPolyRef polys[MAX_POLYS];
int npolys;
unsigned char straightPathFlags[MAX_POLYS];
dtPolyRef straightPathPolys[MAX_POLYS];
int nstraightPath = 0;
navMeshContex->navQuery->findPath(startRef, endRef, startNearestPt, endNearestPt, &filter, polys, &npolys, MAX_POLYS);
//printf("npolys: %d\\n", npolys);
if (npolys)
float epos1[3];
dtVcopy(epos1, endNearestPt);
if (polys[npolys - 1] != endRef)
navMeshContex->navQuery->closestPointOnPoly(polys[npolys - 1], endNearestPt, epos1, 0);
navMeshContex->navQuery-> findStraightPath(startNearestPt, endNearestPt, polys, npolys, straightPath, straightPathFlags, straightPathPolys, &nstraightPath, MAX_POLYS, 0);
ret= Util::ConvertFloatStarToJfloatArray(env, straightPath,nstraightPath * 3);
env->ReleaseFloatArrayElements(start, startPos, JNI_ABORT);
env->ReleaseFloatArrayElements(end, endPos, JNI_ABORT);
env->ReleaseFloatArrayElements(extent, extents, JNI_ABORT);
return ret;
接下去,我们用单测来做下数据对比:
经过数据轨迹对比(C++和JAVA,以及工具的导出轨迹数据),完全一致!说明API实现没问题。
接着,我们做下性能的对比:
系统配置,WIN 11,64G内存,8核数,16线程数
JAVA版本:
C++版本:
从压测数据对比得出结论,寻路性能提高了2倍多,其他的接口也有一定幅度的提升。
总结:
我们可以对寻路的耗时以及稳定性做监控,进行性能的对比。经过对比,通过对C++版本的导航接口调用,我们一定幅度提升了寻路的性能,并且在寻路的稳定性上也得到了提高。
参考文献:
基于NavMesh寻路、漏斗寻路、RVO动态避障自创的服务器大规模寻路+动态避障算法的实现
从零开始学习导航网格#2 编译RecastNavigation
构建recastnavigation并部署服务端到Linux
接入C++版本recastnavigation寻路库到Unity/服务端中
以上是关于在服务器端如何用JNI实现 NavMesh寻路的主要内容,如果未能解决你的问题,请参考以下文章