在服务器端如何用JNI实现 NavMesh寻路

简介: 在服务器端如何用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/服务端中

目录
相关文章
|
6月前
|
缓存 iOS开发
IOS网络编程:使用 URLSession 实现网络请求的步骤是什么?
IOS网络编程:使用 URLSession 实现网络请求的步骤是什么?
106 1
|
人工智能 算法 前端开发
【五子棋实战】第3章 算法包装成第三方接口
【五子棋实战】第3章 算法包装成第三方接口
|
算法
第2章 算法——程序的灵魂
第2章 算法——程序的灵魂
61 0
|
6月前
|
算法
写一段圆弧插补算法程序
写一段圆弧插补算法程序
92 0
|
6月前
|
缓存 Android开发 数据安全/隐私保护
【终极教程】Cocos2dx服务端重构(优化cocos2dx服务端)
Cocos2dx是一个非常流行的跨平台游戏引擎,开发者可以使用这个引擎来开发iOS、Android和Web游戏。同时,Cocos2dx还提供了一些服务器端工具,用于开发游戏的后端。然而,这些工具存在一些缺陷,需要进行优化和重构。本文将重点讨论如何优化和重构Cocos2dx服务器端的问题。
|
11月前
|
测试技术
【测试平台系列】第一章 手撸压力机(九)- 封装函数
将我们的一些代码封装到函数和方法中,这样我们看来代码可读性更好。如果发现bug,也可以更好的进行追踪。
|
算法
100个经典c算法 | 程序源码
100个经典c算法 | 程序源码
52 0
|
存储 Java
Java网络编程-服务端程序实现
Java网络编程-服务端程序实现
107 0
Java网络编程-服务端程序实现
|
网络协议 网络架构
网络通讯原理简介以及演示通讯过程
网络通讯原理简介以及演示通讯过程
179 0
网络通讯原理简介以及演示通讯过程
|
XML 前端开发 Java
从零开始实现放置游戏(十)——实现战斗挂机(1)hessian服务端搭建
 前面实现RMS系统时,我们让其直接访问底层数据库。后面我们在idlewow-game模块实现游戏逻辑时,将不再直接访问底层数据,而是通过hessian服务暴露接口给表现层。   本章,我们先把hessian服务搭好,并做一个简单的测试,这里以用户注册接口为例。   先简单介绍下,实现hessian接口,只需要在facade模块暴露接口,然后在core模块实现接口,最后在hessain模块配置好接口路由,将其启动即可。
从零开始实现放置游戏(十)——实现战斗挂机(1)hessian服务端搭建