Recast源码解析(一):射线实现原理

PIP多边形与点位置关系判断:射线法 算法:PIP 多边形与点位置关系纯手撸代码解决PIP问题面向对象射线法解决PIP问题结果 纯手撸代码解决PIP问题 编程语言为Python 面向对象 创建一个geometry类,其中包括点、线、多边形对象。 class Geometry: def __init__(self, name): self.__name = name def get_name(self): return self.__name class Point(Geometry): 阅读详情

最近公司的项目用到了recast做服务端寻路,自己在使用的过程中对其如何实现网格寻路很感兴趣,根据需要研读过部分实现代码,同时也发现网上关于源码分析方面的资料较少,因此这里打算写成一组系列做个总结。本文是针对recast中的一个射线方法(InputGeom::raycastMesh),结合代码探讨其实现原理,并在此过程中穿插相应数学解释。

recast简介

说到寻路,主流的地形建模方法有三种:grid(方格)、waypoint(路点)和navmesh(导航网格)(参见《游戏人工智能:寻找一个空间寻路表征》)。而recast就是使用navmesh作为模型的一个应用广泛、功能强大的开源项目(项目地址),它支持建网格、寻路、添加动态障碍、群体寻路等诸多特性,并在unity和unreal等著名引擎上都有应用。

核心问题:射线与网格求交点

recast主要分为两部分:recast(建网格)和detour(寻路)。虽然在过程中涉及到复杂的数据结构和数学原理,但是这里只针对其中一个特性:射线与navmesh求交点,探讨其实现原理。具体的应用场景有:

  1. 在recast官方的demo中,根据屏幕点击位置确定寻路的起点和终点坐标
    这里写图片描述
    强烈推荐一下这个demo,它的功能非常强大,以可视化的方法展示了recast提供的几乎所有功能。在寻路的过程中,通过鼠标点击即可设置起止点,这过程中就是用到射线来求取真实世界里的坐标。
  2. 在服务器寻路中,根据x和z坐标求取y坐标(地表高度)
    这是在项目中遇到的实际需求:recast的寻路函数需要传起止点的xyz坐标,x和z坐标可以在服务端保存,而y坐标我们不希望从客户端取(涉及到离线和反外挂的问题),而是通过服务端的地形数据实时算出。
    下面我们就应用场景1的需求,结合源码来做一下实现细节上的分析。

源码解析

首先,将射线的起点取成屏幕点击点,终点取成起点的深度(z坐标)加1,这样射线与navmesh的交点就认为是要设置的起点或终点。然后,在main.cpp中通过opengl的方法将鼠标点击的屏幕坐标转成世界坐标:

        // Get hit ray position and direction.
        GLdouble x, y, z;
        gluUnProject(mousePos[0], mousePos[1], 0.0f, modelviewMatrix, projectionMatrix, viewport, &x, &y, &z);
        rayStart[0] = (float)x;
        rayStart[1] = (float)y;
        rayStart[2] = (float)z;
        gluUnProject(mousePos[0], mousePos[1], 1.0f, modelviewMatrix, projectionMatrix, viewport, &x, &y, &z);
        rayEnd[0] = (float)x;
        rayEnd[1] = (float)y;
        rayEnd[2] = (float)z;

接下来将起点、终点坐标rayStart和rayEnd传入如下函数:

bool InputGeom::raycastMesh(float* src, float* dst, float& tmin)
{
    float dir[3];
    rcVsub(dir, dst, src);

    // Prune hit ray.
    float btmin, btmax;
    if (!isectSegAABB(src, dst, m_meshBMin, m_meshBMax, btmin, btmax))
        return false;
    float p[2], q[2];
    p[0] = src[0] + (dst[0]-src[0])*btmin;
    p[1] = src[2] + (dst[2]-src[2])*btmin;
    q[0] = src[0] + (dst[0]-src[0])*btmax;
    q[1] = src[2] + (dst[2]-src[2])*btmax;

    int cid[512];
    const int ncid = rcGetChunksOverlappingSegment(m_chunkyMesh, p, q, cid, 512);
    if (!ncid)
        return false;

    tmin = 1.0f;
    bool hit = false;
    const float* verts = m_mesh->getVerts();

    for (int i = 0; i < ncid; ++i)
    {
        const rcChunkyTriMeshNode& node = m_chunkyMesh->nodes[cid[i]];
        const int* tris = &m_chunkyMesh->tris[node.i*3];
        const int ntris = node.n;

        for (int j = 0; j < ntris*3; j += 3)
        {
            float t = 1;
            if (intersectSegmentTriangle(src, dst,
                                         &verts[tris[j]*3],
                                         &verts[tris[j+1]*3],
                                         &verts[tris[j+2]*3], t))
            {
                if (t < tmin)
                    tmin = t;
                hit = true;
            }
        }
    }

    return hit;
}

isectSegAABB函数的作用是修剪射线:它将整个navmesh看成是一个AABB包围盒,判断射线和包围盒是否有交集;若没有则直接return;否则将射线不在盒内的部分修剪掉。
将下来通过rcGetChunksOverlappingSegment函数求取二维平面下与射线有交集的所有trimesh node(三角网格节点)(只考虑x、z坐标)。这一步算是粗筛,因为不涉及点乘差乘等耗时运算,执行效率较高。相关代码如下:

int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm,
                                  float p[2], float q[2],
                                  int* ids, const int maxIds)
{
    // Traverse tree
    int i = 0;
    int n = 0;
    while (i < cm->nnodes)
    {
        const rcChunkyTriMeshNode* node = &cm->nodes[i];
        const bool overlap = checkOverlapSegment(p, q, node->bmin, node->bmax);
        const bool isLeafNode = node->i >= 0;

        if (isLeafNode && overlap)
        {
            if (n < maxIds)
            {
                ids[n] = i;
                n++;
            }
        }

        if (overlap || isLeafNode)
            i++;
        else
        {
            const int escapeIndex = -node->i;
            i += escapeIndex;
        }
    }

    return n;
}

检查方法checkOverlapSegment是将node看成AABB包围盒,通过比较射线起止点p、q与包围盒的x、z坐标的相对位置。若存在overlap,则还要判断node是否为叶子节点。这里recast为trimesh node建立的模型是一个树状结构,从根节点出发管理到大的区块,再到小的区块,直至一个基础node作为叶子节点。叶子节点是通过node的属性i来判断,若i小于0代表叶子节点,可以将这个node加入返回数组中;否则判断下一个。注意这里选取下一个的时候有个分支优化:若既没有overlap,又不是叶节点,则放弃当前节点下面的所有子孙节点,直接跳转到通过属性i计算出的下一个节点索引处。
通过上面这一步可以排除掉绝大多数节点。下面只需要对剩余的若干个trimesh node做精选,判断射线是否与它们存在交点。这实际是分两步:一是求射线与三角形所在平面的交点,二是判断交点是否在三角形内部。这是在如下函数中处理的:

static bool intersectSegmentTriangle(const float* sp, const float* sq,
                                     const float* a, const float* b, const float* c,
                                     float &t)
{
    float v, w;
    float ab[3], ac[3], qp[3], ap[3], norm[3], e[3];
    rcVsub(ab, b, a);
    rcVsub(ac, c, a);
    rcVsub(qp, sp, sq);

    // Compute triangle normal. Can be precalculated or cached if
    // intersecting multiple segments against the same triangle
    rcVcross(norm, ab, ac);

    // Compute denominator d. If d <= 0, segment is parallel to or points
    // away from triangle, so exit early
    float d = rcVdot(qp, norm);
    if (d <= 0.0f) return false;

    // Compute intersection t value of pq with plane of triangle. A ray
    // intersects iff 0 <= t. Segment intersects iff 0 <= t <= 1. Delay
    // dividing by d until intersection has been found to pierce triangle
    rcVsub(ap, sp, a);
    t = rcVdot(ap, norm);
    if (t < 0.0f) return false;
    if (t > d) return false; // For segment; exclude this code line for a ray test

    // Compute barycentric coordinate components and test if within bounds
    rcVcross(e, qp, ap);
    v = rcVdot(ac, e);
    if (v < 0.0f || v > d) return false;
    w = -rcVdot(ab, e);
    if (w < 0.0f || v + w > d) return false;

    // Segment/ray intersects triangle. Perform delayed division
    t /= d;

    return true;
}

这是一个纯粹的数学问题:设P、Q为射线的起止点,三角形的三个顶点分别为A、B、C,我们得到如下的几何模型:


程序先求三角形所在平面的法向量 norm−→−− n o r m → ,再用叉乘将 AP−→− A P → 、 QP−→− Q P → 分别映射到 norm−→−− n o r m → 所在方向,分别得到高度t和d,若t>d,则射线PQ肯定与平面没有交点,直接return。
接下来再判断交点是否在三角形内部。这里要用到的一个概念叫做质心坐标系(不明白的可以百度)。大概意思就是三角形ABC所在平面的点可以表示成:
M=(1−λ1−λ2)a→+λ1b→+λ2c→ M = ( 1 − λ 1 − λ 2 ) a → + λ 1 b → + λ 2 c →
而三角形内部的点必定满足: λ1 λ 1 和 λ2 λ 2 都在(0,1)范围内。
通过这个性质,再加一系列的方程计算和矩阵变换可以判断出交点是否在三角形内部(演算过程这里略过,具体可看《空间中直线段和三角形的相交算法》,说得很详细了)。若不在则直接return,否则将t/d作为返回值传出,后面会用来求取最终的交点坐标。
接下来重新回到InputGeom::raycastMesh函数中,可以看到若射线与多个trimesh node相交,会选择最先遇到的交点:


        for (int j = 0; j < ntris*3; j += 3)
        {
            float t = 1;
            if (intersectSegmentTriangle(src, dst,
                                         &verts[tris[j]*3],
                                         &verts[tris[j+1]*3],
                                         &verts[tris[j+2]*3], t))
            {
                if (t < tmin)
                    tmin = t;
                hit = true;
            }
        }

最后回到main.cpp中,根据上面return的t与d的比例关系,求取最终的交点坐标:

                    float pos[3];
                    pos[0] = rayStart[0] + (rayEnd[0] - rayStart[0]) * hitTime;
                    pos[1] = rayStart[1] + (rayEnd[1] - rayStart[1]) * hitTime;
                    pos[2] = rayStart[2] + (rayEnd[2] - rayStart[2]) * hitTime;

至此大功告成。

遇到的一些坑

本人因项目需要,使用的是recast的Java版本(项目地址)。它的大部分api和实现与原版(C++)一致,不过也存在少数细节差异,导致使用过程中遇到了一些坑。如Java版的SimpleInputGeomProvider.meshes()方法是调用时才根据地形数据实时生成所有的trimesh node,这一点非常耗时;而原版是加载地形数据时就生成了,后面直接用缓存。因此在项目中参照原版对这点做了优化。

小结

这里讨论的只是recast的一个非常小的功能:射线与mesh求交点;它本质上等价为一个数学问题:线段与三角形求交点,通过阅读源码和分析,我们看到不少空间几何数学知识的运用。同时为了性能考虑,recast通过很多独具匠心的小细节加速了求解过程。希望本文能给recast的使用者一些参考。

Unity游戏AI导航进阶:基于Recast/Detour实现动态寻路与避障 在游戏开发中,导航网格(NavMesh)是实现AI自主寻路的核心技术,它定义了虚拟角色在3D场景中的可行走区域。其原理是通过对场景几何体进行体素化处理,生成由凸多边形构成的网格,A*等寻路算法在此网格上运行以计算最优路径。这项技术的核心价值在于为AI提供了空间认知和路径规划能力,是实现RTS、MOBA、开放世界RPG等类型游戏中大规模单位调度的基础。面对Unity内置NavMesh在动态场景更新和**高性能寻路**方面的局限,开发者常转向更底层的解决方案。Recast负责从三角形网格生成导航网格,Detou 阅读详情

相关推荐

游戏AI导航实战:基于RecastNavigation的NavMesh生成与寻路系统构建

在游戏开发中,AI角色的智能移动是提升沉浸感与策略深度的核心技术。其核心原理在于将复杂的三维场景转化为计算机可理解的抽象空间表示,并在此之上进行高效的路径规划。导航网格(NavMesh)作为一种主流的空间表示技术,通过凸多边形精确覆盖可行走表面,相比传统的路点或网格,在空间利用率和路径自然度上具有显著优势。其技术价值在于将开发者从繁琐的底层几何处理(如体素化、区域划分)中解放出来,专注于高级AI行为逻辑。这一技术广泛应用于RTS、MOBA、MMO等需要大量单位智能移动的场景。本文以工业级解决方案Recast

weixin_33997389的博客 437

Recast-Detour看这里就够了!

一文详解recast-detour。游戏导航地图生成,寻路算法,多边形寻路算法,A星算法,选点,优化,路径平滑,漏斗算法,射线探测算法等,如有错误,望不吝赐教!

romantic_jie的博客 6322

Unity A* Pro 4.1.16 寻路系统实战:从动态避障到性能优化

寻路算法是游戏开发中实现智能体自主移动的核心技术,其原理是通过图搜索在节点网络中寻找最优路径。A*算法因其高效和灵活成为行业标准,它结合了启发式搜索与成本计算,能有效应对复杂地形与动态环境。在工程实践中,一个专业的寻路系统不仅能提升游戏体验,更是支撑大规模单位调度与实时策略玩法的技术基础。本文聚焦于Unity平台,深入解析如何利用A* Pathfinding Project Pro插件构建工业级寻路解决方案,涵盖动态障碍物处理、局部避障(RVO)与性能调优等关键场景,通过实战案例展示如何将算法能力转化为稳定

weixin_30457465的博客 425

Recast源码解析(二):NavMesh导航网格生成原理(上)

上文说到recast的寻路分为两部分:recast(建网格)和detour(寻路),这里就前面一步谈谈自己结合代码的理解。这里的navmesh指的是solo mesh,也就是静态网格,与能动态添加障碍的tile cache加以区别。后者另外单独撰文介绍。 这里推荐一个网站critterAI(网站地址),里面有两个很好的学习项目:CAINav是一个基于recast开发的可以在Unity运行的导航系...

长三月的游戏开发 2万+

recast4j:重铸和绕行导航网格工具集的Java Port

重铸4j 重铸和绕行导航网格工具集的Java端口。 重铸 重铸是用于游戏的最先进的导航网格构建工具集。 它是自动的,这意味着您可以向其投掷任何关卡几何图形,并且可以获得坚固的网格 快速,这意味着关卡设计师的周转时间很快 它是开源的,因此附带了完整的源代码,您可以根据自己的喜好对其进行自定义。 重铸过程首先从级别几何构造体素模具,然后在其上投射导航网格。 该过程包括三个步骤:构建体素模具,将模具划分为简单区域,将区域剥离为简单多边形。 通过将三角形栅格化为多层高度场,从输入三角形网格构建体素模具。 然后将一些简单的滤镜应用于模具,以修剪角色无法移动的位置。 模具描述的可行走区域分为简单的2D叠加区域。 所得区域仅具有一个不重叠的轮廓,从而极大地简化了过程的最后一步。 首先通过跟踪边界,然后对其进行简化,从而将导航多边形从区域中剥离。 最终将生成的多边形转换为凸多边形,这使它们非常

my-recast4j-study:recast4j学习

重铸和绕行导航网格工具集的Java端口。 重铸4j 原始语言学习,持续更新中! 链接: : 附加参考c ++版本: :

工业级导航网格系统在复杂游戏场景中的架构设计与实现方案

随着现代游戏开发向开放世界、动态环境和大规模AI群体方向演进,传统的基于路径点或网格的导航系统已难以满足复杂场景需求。游戏开发者面临的核心挑战在于如何让成百上千的AI角色在动态变化的环境中实现高效、自然的移动,同时保持实时性能和内存效率。Recast Navigation作为行业标准的导航网格工具集,通过其模块化架构和高效的体素化算法,为这一技术难题提供了系统性的解决方案。 ## 核心价值:从静

gitblog_00932的博客 623

STM32串口接收避坑指南:为什么你的USART_IT_IDLE中断不触发?附完整调试流程

本文深入解析STM32串口接收中断机制,特别是USART_IT_IDLE中断不触发的常见原因及解决方案。通过详细分析中断标志清除顺序、波特率配置、硬件连接等关键因素,并提供完整的调试流程和代码示例,帮助开发者快速定位并解决串口通信问题。

weixin_42519985的博客 347

RecastNavigation导航网格指南:5步掌握游戏AI路径规划技术

RecastNavigation是游戏开发领域的行业标准导航网格工具集,专门用于生成高质量导航网格和执行高效路径规划。无论你是独立开发者还是大型工作室成员,这个开源工具都能为你的游戏AI提供专业级的导航解决方案。本文将为你提供一个完整指南,帮助你快速掌握RecastNavigation的核心功能和使用方法。 ## 为什么游戏开发需要导航网格? 在3D游戏中,AI角色需要智能地在复杂环境中移动。

gitblog_00485的博客 384

别再乱拖了!Godot 4.x 场景编辑器的7个隐藏操作技巧(附快捷键清单)

本文揭示了Godot 4.x场景编辑器的7个隐藏操作技巧,包括智能对齐、轴心点应用、多节点批量操作等,帮助开发者提升游戏开发效率。特别介绍了坐标系切换和自定义快捷键等实用功能,适用于处理复杂场景和团队协作。

weixin_29534421的博客 361

无参考图像质量评估方法:BRISQUE、RankIQA、DIQA

常用的图像质量评估(IQA)分为无参考和有参考两种类型。常见的有参考图像质量评估方法有PSNR,SSIM以及VMAF。其中vmaf是针对视频来讲的,在它的评价方法里面添加了一些时域信息,如果应用于单张图片,需要做一些处理,如去除时域信息。有参考意味着图像质量是以参考图为基准。基于参考图像都是高清图像的假设,这些评价方法已经广泛应用于图像压缩以及图像传输、拼接之类的处理后图像的质量,假如这张参考图的质量本身就比较渣,那么该评价指标可信度就大大降低。

weixin_51732791的博客 3191

Godotdetour插件实战:解决复杂寻路与动态避障的常见问题

导航网格(NavMesh)是游戏开发中实现智能寻路的核心技术,它通过将3D场景的可行走区域转化为多边形网格,为AI角色提供可计算的移动空间。其原理基于空间体素化与区域划分,将复杂的几何环境转换为寻路算法(如A*)可高效处理的数据结构。这项技术的价值在于,它能显著提升游戏中NPC和单位的移动智能度与运行效率,是实现开放世界、RTS及多智能体模拟等复杂游戏类型的基石。在实际应用场景中,开发者常面临导航网格生成瑕疵、动态障碍物集成以及多单位协调等工程挑战。本文聚焦于Godot引擎中集成工业级Recast/Deto

weixin_33875564的博客 378

UFS 3.1协议分析(第五章) -- UFS协议栈

ContentsUFS顶层架构应用层UFS命令集UFS 设备管理器UFS任务管理器传输层互连层 UFS顶层架构     UFS基于SCSI SAM架构模型定义了一个完整的协议栈,从上到下依次为应用层、传输层、数据链路层、物理层。UFS使用MIPI联盟的UniPro作为数据链路层和MIPI的M-PHY作为物理层,两者合起来称之为互连层(UFS InterConnect Layer) 应用层     应用层包括UFS命令集、设备管

Frank_sample的专栏 7369

游戏AI导航实战:RecastNavigation核心原理与工程应用指南

在游戏开发中,寻路算法是构建智能角色移动的基础。A*算法作为经典的图搜索算法,通过评估函数寻找起点到终点的最优路径,但其在复杂3D环境中的直接应用面临效率与精度挑战。为了解决这一工程难题,导航网格技术应运而生,它将连续的3D世界离散化为由凸多边形构成的可行走表面图,极大提升了寻路查询的效率与质量。这一技术对于开发开放世界、RTS及MMO等需要大量智能体自主移动的游戏类型具有核心价值。本文聚焦于行业广泛采用的**RecastNavigation**解决方案,深入解析其通过**体素化**将3D几何体转换为规整体

weixin_33991418的博客 305

利用Python搭建用户画像系统

用户画像是当下很多企业都会提及的概念,多数情况下会和大数据以及营销挂钩。本文将对用户画像的相关知识进行进行简单的介绍,并利用Python去实现一个简单的用户画像系统。 1.什么是用户画像 用户画像可以理解成是海量数据的标签,根据用户的目标、行为和观点的差异,将他们区分为不同的类型,然后每种类型中抽取出典型特征,赋予名字、照片、一些人口统计学要素、场景等描述,形成一个人物原型 。 用户画像 =...

黎荣恒的博客 3万+

10. 重磅!Spring AI 1.0 正式发布,Java 开发者的 AI神器!

重磅!Spring AI 1.0 正式发布,Java 开发者的 AI神器! 全网最详细的Spring AI 文章,每章都会提供代码入口,让你更容易得入门和理解Spring AI 相关概念,直接节约你百分之六十的学习成本时间。

gorgor在码农 1066

游戏开发中拉绳算法实现平滑路径规划:告别“之”字形寻路

在游戏开发与机器人路径规划领域,寻路算法是核心基础技术之一。A*等算法虽然能找到可行路径,但常产生不自然的“之”字形折线,影响移动体验与效率。其原理在于算法输出的是多边形序列,而非连续坐标点。拉绳算法(Funnel Algorithm)通过构建动态漏斗模型,利用向量叉积进行几何判断,能高效地将多边形通道转化为平滑的最短路径,确保路径不穿墙且全局最优。该技术在游戏NPC导航、机器人运动规划等场景中具有重要价值,能显著提升移动自然度与性能。本文结合Java实战代码,深入解析拉绳算法的实现细节与常见问题,帮助开发

weixin_34216196的博客 337

python代码实现了一个基于图注意力网络(GAT)和长短期记忆网络(LSTM)的股票成交量预测模型

自定义数据集类:定义了类,继承自。该类用于生成股票数据的时间序列样本,每个样本包含一个长度为seq_length的时间序列和对应的目标成交量。数据加载器:使用DataLoader对数据集进行批量加载,设置批量大小为 64,并开启数据打乱功能。GAT - LSTM 模型:定义了GAT_LSTM类,继承自。该模型结合了 GAT 和 LSTM 两种神经网络:GAT 部分:使用GATConv处理全局图特征,捕获股票之间的相互关系。LSTM 部分:使用nn.LSTM处理时间序列数据,捕捉时间序列的动态变化。

huanghm88的专栏 425

空间中直线段和三角形的相交算法

原博客地址空间中直线段和三角形的相交算法 最近在看recast&detour源码的时候有遇到许多数学上的算法问题,特此记录,以便以后查看。 分析 问题: 已经线段pq (p起点 q终点) ,三角形abc(a b c逆时针存储),判断pq与abc有无交点。 第一步: 判断三角形所在的面和线段所在的直线 是否平行 或者 是否是从三角形面的背面进入。如果是就提前退出,否则进入第二步。 (...

惜双若雅的博客 2261

基于Simulink的电力电子系统电磁兼容性(EMC)仿真

在电力电子系统中,由于高频开关器件的存在,会产生大量的电磁干扰(EMI),这对系统的稳定性和可靠性提出了挑战。Simulink 提供了一个强大的仿真环境,可以用来设计、分析和验证电力电子系统的电磁兼容性。本教程将详细介绍如何使用 Simulink 进行电力电子系统的电磁兼容性仿真。通过本教程,我们详细介绍了如何使用 Simulink 进行电力电子系统的电磁兼容性仿真。为了模拟电力电子系统中的电磁干扰(EMI),我们需要添加噪声源模块。为了更好地分析系统的电磁兼容性特性,可以在模型中添加频谱分析工具。

amy_mhd的博客 1126

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

秋招笔记——汇总

今天把所有的秋招笔记汇总在这里,不然我的个人主页太杂乱了,这样也方便复习。

lastXuanGod的博客 304

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

java服务器Nav寻路

recast navmesh

siyunlong的专栏 4085

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

3D寻路系统NavMesh-服务端篇

上一节讲到的客户端使用Unity 自带的 NavMesh 来做寻路。然而,怪物的刷新、移动,和AI是由服务器负责的,怪物的寻路是由服务器控制的,或者像SLG,大地图寻路在玩家离线的情况下要继续寻路,这必须要服务器来主导寻路。...............

流子的专栏 3881

在几何图形中均匀随机取点算法总结及Delaunay三角剖分算法介绍

在工作中遇到一个需求,需要在圆形 矩形,三角形内随机,尽量均匀取点作为位置信息,但是random得到的信息有时候不是很满意。 这里讨论一下 第一种错误思路: 根据圆的解析式 (假设圆心在原点)我们可以先随机生成[-R, R]范围内横坐标x,然后生成 范围内的随机数y,(x,y)就是需要的点。 我们写程序模拟了该过程,从下图可以看出,我们可以看到当x靠近圆的边缘使,y的范围减小,因此两边边缘...

u014028063的博客 1万+

射线检测的方法解释

GUILayout GUILayout.Label("IsGet:" + isGet);//GUILayout自动布局 自动布局只考虑当前的OnGUI的函数中的布局,如果有其他的脚本中也存在OnGUI,但是不会考虑   射线 Physics中的静态函数,有许多的种类。 Physics.Raycast(transform.position, transform.forward); 这个...

qq_37896049的博客 2105
上一篇: 《Unity In Action》读书笔记
下一篇: NavMesh生成研究(一):体素化过程
长三月
博客等级 码龄9年 228粉丝 80原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值