服务器使用recast navigation

  在3D MMO或者其余类型的游戏中,一般须要进行寻路处理,地图针对寻路有多种方案,好比划分格子,凸多边形等,本篇介绍一种比较经常使用的navigation mesh的方式来进行寻路。不过整套navmesh的算法比较复杂,没有深刻的研究写不出来,咱们使用网上开源的解决方案,google的recast方案。咱们使用的开发环境是win7 + vs2013.git

  1、服务器端recast的安装和使用github

  一、先从github上面下载recast的源代码算法

  2. 源代码下载下来以后,须要咱们本身进行编译,recast使用预编译工具premake,须要premake.lua。先下载lua,而后单独下载premake5,不然premake5.lua不能使用。将下载好的premake.exe放到和premake5.lua的同一目录下,而后在控制台运行premake.exe vs2013(根据本身的IDE版本号),就会生成vs2013的解决方案sln文件。服务器

  2. recast的测试程序中,用到了SDL的相关库,这个比较简单,下载一个SDL-devel的库就能够了,而后看下RecastDemo里面的配置目录,copy进去就能够编译经过了。中间编译的时候,可能因为vs2013版本,会提示min,max这些函数没有定义,只要#include对应的STL库头文件<algorithm>就能够了函数

  3. 成功编译了recast相关的库,Detour,Recast等,咱们一开始对于如何使用recast可能一点概念都没有,不过recast已经考虑到了这一点,工程中有一个RecastDemo程序,是用来示例如何使用recast的。工具

  4. 使用recastDemo,咱们能够根据场景的.obj文件,来生成navmesh,和导入咱们本身生成的navmesh,不过这都是咱们经过recast开源工具生成的。实际的游戏中,咱们须要的是从客户端生成的navmesh,而后导入到服务器中进行解析。测试

 

  2、Unity3D客户端的navmesh生成google

  unity3D是自带navmesh agent的,能够本身生成navmesh,而且导出navmesh,不过它的导出navmesh,我查下来是须要本身写代码的,并且navmesh agent是修改过导出navmesh文件格式的,也就是说咱们recast C++代码是不能直接使用的。后来我又搜索了一下recast for Unity这个插件,这个插件是有源代码的,不过若是正常购买须要$50。lua

  对于这个插件当时研究了比较长的时间,一开始导出了一下它的navmesh格式,而后用C++recast导入试了一下,发现格式确定是不同的了。而后就仔细研究了一下两边生成navmesh时的格式差别,针对TileMesh存储的时候,两边是不一致的,原本想直接修改C#代码来达到两边一致,不过因为对C#的序列化不够熟悉,就放弃了。不过应该能够直接改为一致,这个后面有时间能够再深刻研究一下。spa

  后来使用了KBEngine修改过的CritterAI,它导出来的navmesh文件,在KBEngine中是能直接使用的。而后把咱们服务器中解析navmesh的格式,改为和KBEngine一致就能够了。代码仍是很是简单的,代码以下:

    bool NavMeshLoader::load_cai(const char* path) {
        FILE* fp = fopen(path, "rb");
        if (!fp) return false;

        // Read header.
        NavMeshSetHeader_CAI header;
        size_t readLen = fread(&header, sizeof(NavMeshSetHeader_CAI), 1, fp);
        if (readLen != 1)
        {
            fclose(fp);
            return false;
        }

        if (header.version != NAVMESHSET_VERSION)
        {
            fclose(fp);
            return false;
        }

        dtNavMesh* mesh = dtAllocNavMesh();
        if (!mesh)
        {
            fclose(fp);
            return false;
        }
        dtStatus status = mesh->init(&header.params);
        if (dtStatusFailed(status))
        {
            fclose(fp);
            return false;
        }

        // Read tiles.
        for (int i = 0; i < header.tileCount; ++i)
        {
            NavMeshTileHeader tileHeader;
            readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
            if (readLen != 1)
            {
                fclose(fp);
                return false;
            }

            if (!tileHeader.tileRef || !tileHeader.dataSize)
                break;

            unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
            if (!data) break;
            memset(data, 0, tileHeader.dataSize);
            readLen = fread(data, tileHeader.dataSize, 1, fp);
            if (readLen != 1)
            {
                fclose(fp);
                return false;
            }

            mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
        }

        fclose(fp);
        m_navMesh = mesh;
        m_navQuery = dtAllocNavMeshQuery();

        status = m_navQuery->init(m_navMesh, 2048);
        if (dtStatusFailed(status))
        {
            return false;
        }

        return true;
    }

  KBEngine中和RecastDemo中解析navmesh主要就是NavMeshSetHeader头结构不一致,其余的都同样,因此仍是比较简单的。

  能正常解析navmesh文件以后,就是对地图进行寻路了,在这里主要使用了RecastDemo中寻路的代码,进行了本地化的修改。

  

  3、 使用recast navigation遇到的问题

  一、其中有段时间,我对于客户端生成的navmesh,和服务器生成的navmesh在坐标系上不一致,感受很困惑,甚至想要不要本身用3DMax之类的工具从新导入地图模型,而后修改坐标系,这如今看来很好笑,主要仍是对于navmesh的生成原理不是很懂。其实客户端生成的navmesh是带有自身坐标系和相对坐标的。以前之因此有那种困惑,是由于服务器用的是recastDemo中生成的navmesh,客户端生成了本身的,根本就是两种世界,两种坐标,固然不一致了!!

  二、其实CritterAI的底层是C++写的dll,经过unity导入进工程,而后生成navmesh的,只要符合unity使用dll的规则,咱们其实也可使用recast的源码,而后改为unity可以使用的dll来导出navmesh。这个后面能够试试

相关文章
相关标签/搜索