当前位置: 首页 > news >正文

【recast-navigation/源码解析】findStraightPath详解以及寻路结果贴边优化

说在前面

  • recast-navigation版本:1.6.0

叉积cross product

  • 正常来讲,叉乘为:
    ∣ A ⃗ × B ⃗ ∣ = ∣ x A y A x B y B ∣ = x A ⋅ y B − x B ⋅ y A |\vec{A} \times \vec{B}|=\begin{vmatrix} x_A & y_A \\ x_B & y_B \end{vmatrix}=x_A \cdot y_B - x_B \cdot y_A A ×B = xAxByAyB =xAyBxByA
  • 例如:
    在这里插入图片描述
  • 但是在recast-navigation库中:
    /// Derives the signed xz-plane area of the triangle ABC, 
    /// or the relationship of line AB to point C.
    ///  @param[in]		a		Vertex A. [(x, y, z)]
    ///  @param[in]		b		Vertex B. [(x, y, z)]
    ///  @param[in]		c		Vertex C. [(x, y, z)]
    /// @return The signed xz-plane area of the triangle.
    inline float dtTriArea2D(const float* a, const float* b, const float* c)
    {const float abx = b[0] - a[0];const float abz = b[2] - a[2];const float acx = c[0] - a[0];const float acz = c[2] - a[2];return acx*abz - abx*acz;
    }
    
    这应该是因为x轴与正常坐标系相反

findStraightPath

  • 在使用findStraightPath前,我们通常会先使用findPath进行A*搜索,得到一个基于多边形的路径,假设findPath的结果如下图:
    在这里插入图片描述

  • findStraightPath的目的则是从这几个多边形中寻找一条路径出来,接下来我们一步步的分解这个过程

    1. 首先我们对一些计算过程中使用到的临时变量进行初始化, 主要为portalApexportalLeftportalRight

      float portalApex[3], portalLeft[3], portalRight[3];
      dtVcopy(portalApex, closestStartPos);
      dtVcopy(portalLeft, portalApex);
      dtVcopy(portalRight, portalApex);
      

      可以看到这三个变量初始化为start点的值
      在这里插入图片描述

    2. 我们从起始点(start)所在的多边形开始遍历,遍历次数(至少)为所有多边形的数量,在我们的例子中,数量为3;

      for (int i = 0; i < pathSize; ++i)
      {
      
    3. 在每次循环中,我们首先需要计算得到当前多边形与下一个多边形的公共边,得到公共边的两个点;假设当前多边形为绿色,下一个多边形为紫色,计算得到的两个点分别为leftright,如下图
      在这里插入图片描述

      if (i+1 < pathSize)
      {unsigned char fromType; // fromType is ignored.// Next portal.if (dtStatusFailed(getPortalPoints(path[i], path[i+1], left, right, fromType, toType))){
      
    4. 然后我们使用三角形的有向面积(signed area of triangle叉乘)对几个点的位置进行判定

      /// Derives the signed xz-plane area of the triangle ABC, or the relationship of line AB to point C.
      ///  @param[in]		a		Vertex A. [(x, y, z)]
      ///  @param[in]		b		Vertex B. [(x, y, z)]
      ///  @param[in]		c		Vertex C. [(x, y, z)]
      /// @return The signed xz-plane area of the triangle.
      inline float dtTriArea2D(const float* a, const float* b, const float* c)
      {const float abx = b[0] - a[0];const float abz = b[2] - a[2];const float acx = c[0] - a[0];const float acz = c[2] - a[2];return acx*abz - abx*acz;
      }
      

      首先是点portalApexportalRightright,由于此时portalApexportalRight相同,所以结果为0;
      在这里插入图片描述

      这个时候我们将点portalRight置为点right;同理,对于点portalApexportalLeftleft,将点portalLeft置为点left
      在这里插入图片描述

      // Right vertex.
      if (dtTriArea2D(portalApex, portalRight, right) <= 0.0f)
      {if (dtVequal(portalApex, portalRight) || dtTriArea2D(portalApex, portalLeft, right) > 0.0f){dtVcopy(portalRight, right);rightPolyRef = (i+1 < pathSize) ? path[i+1] : 0;rightPolyType = toType;rightIndex = i;}else{
      // ......
      // Left vertex.
      if (dtTriArea2D(portalApex, portalLeft, left) >= 0.0f)
      {if (dtVequal(portalApex, portalLeft) || dtTriArea2D(portalApex, portalRight, left) < 0.0f){dtVcopy(portalLeft, left);leftPolyRef = (i+1 < pathSize) ? path[i+1] : 0;leftPolyType = toType;leftIndex = i;}else{
      

      至此,第一个循环结束

    5. 第二个循环,我们挪动第二个多边形,即当前多边形为第二个,计算第二、第三多边形的公共边,得到新的leftright;如下图,当前多边形为绿色,下一个多边形为紫色,红色为公共边
      在这里插入图片描述

    6. 同样,先对点portalApexportalRightright以及点portalApexportalLeftleft进行计算,这一步实际上是在判定边(left-right)是否在夹角(portalLeft-portalApex-portalRight)之内
      在这里插入图片描述
      首先是点right,由于点right在向量portalApex-portalRight左侧,不再进行下一步判定;即以下条件不满足:

      // Right vertex.
      if (dtTriArea2D(portalApex, portalRight, right) <= 0.0f)
      {
      

      接着是点left,由于点left在向量portalApex-portalLeft左侧,满足条件,继续下一步判定;

      // Left vertex.
      if (dtTriArea2D(portalApex, portalLeft, left) >= 0.0f)
      {
      

      继续判定点left是否在向量portalApex-portalRight左侧,结果满足条件,所以点portalRight是拐点

      if (dtVequal(portalApex, portalLeft) || dtTriArea2D(portalApex, portalRight, left) < 0.0f)
      {//......
      }
      else
      {//....dtVcopy(portalApex, portalRight);
      

      得到拐点之后,将拐点置为新的portalApexportalRightportalLeft,同时,下一次循环将继续从绿色多边形开始
      在这里插入图片描述
      此时,我们就得到了部分路径(上图中绿色点)

    7. 继续下一次循环,同理可得到下述结果:
      在这里插入图片描述

    8. 下一次循环,由于此时已经没有下一个多边形了,我们将leftright置为点end
      在这里插入图片描述
      同样,先对点portalApexportalRightright以及点portalApexportalLeftleft进行计算,
      在这里插入图片描述
      最终得到拐点portalRight
      在这里插入图片描述

    9. 下一次循环,同样,由于此时已经没有下一个多边形了,我们将leftright置为点end
      在这里插入图片描述
      继续对点portalApexportalRightright以及点portalApexportalLeftleft进行计算,
      在这里插入图片描述
      此时循环结束,我们将点end加入路径

      }// Ignore status return value as we're just about to return anyway.
      appendVertex(closestEndPos, DT_STRAIGHTPATH_END, 0,straightPath, straightPathFlags, straightPathRefs,straightPathCount, maxStraightPath);
      
    10. 最终结果,如下图绿色路径
      在这里插入图片描述

寻路结果贴边优化的一种方式

  • 通过上面的解释,我们知道寻路结果上的拐点都是多边形的边上的顶点,也就是leftright点,所以想要让结果不那么贴边,可以将两点向内偏移,即:
    // Next portal.
    if (dtStatusFailed(getPortalPoints(path[i], path[i+1], left, right, fromType, toType)))
    {//......
    }if (!dtVequal(left, right)) {float _left[3], _right[3];dtVcopy(_left, left);dtVcopy(_right, right);dtVlerp(left, _left, _right, 0.1);dtVlerp(right, _right, _left, 0.1);
    }				
    
  • 优化后
    在这里插入图片描述
    在这里插入图片描述
http://www.lryc.cn/news/435338.html

相关文章:

  • ‌移动管家手机智能控制汽车系统
  • 828华为云征文|华为云Flexus X实例Redis性能加速评测及对比
  • 【OpenCV3】图像的翻转、图像的旋转、仿射变换之图像平移、仿射变换之获取变换矩阵、透视变换
  • 不要认为996是开玩笑
  • 精益工程师资格证书:2024年CLMP报名指南
  • 【Unity基础】如何选择脚本编译方式Mono和IL2CPP?
  • 写在OceanBase开源三周年
  • 【笔记】408刷题笔记
  • GitHub Star 数量前 13 的自托管项目清单
  • js实现生成随机数值的数组
  • 视频怎么转换成mp3格式?分享5种便捷的转换方法
  • Reflection 70B如何革新语言模型的准确性与推理能力
  • 覆盖索引是什么意思?
  • 最大间距问题
  • 【Hadoop|MapReduce篇】Hadoop序列化概述
  • 【Elasticsearch系列】Elasticsearch中的分页
  • NLTK:一个强大的自然语言处理处理Python库
  • NUUO网络视频录像机 css_parser.php 任意文件读取漏洞复现
  • 【支付】Stripe支付通道Java对接(产品 价格 支付 查询 退款 回调)
  • Unity3D 小案例 像素贪吃蛇 01 蛇的移动
  • 【STM32 MCU】stm32MCUs 32-bit Arm Cortex-M
  • html+css网页设计 旅游 雪花旅行社5个页面
  • vue3中的实例
  • 9.测试计划(包含笔试/面试题)
  • 这 7 款AI应用将让你全新的iPhone 16成为电影制作的强大工具
  • 自注意力机制(self-attention)
  • Nuxt3入门:过渡效果(第5节)
  • 【开发工具】IntelliJ IDEA插件推荐:Json Helper——让JSON处理更高效
  • Lua垃圾回收机制
  • Java学习路线:详细指引