服務器使用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。這個後面能夠試試

相關文章
相關標籤/搜索