未验证 提交 7a38ea36 编写于 作者: N NKG丶MadLife 提交者: GitHub

fix&&feat:修复外网Actor发送错误,接入recastnavigation寻路库 (#219)

recastnavigation相关教程可参见:https://www.lfzxb.top/cpp-recastnavigation-to-unity-or-server/
上级 20354732
/////////////////////////////////////////////////
//
// 使用RecastNavigation库进行寻路的测试程序
// Written by: Liu Gang. July.11.2020.
//
/////////////////////////////////////////////////
#include <iostream>
#include <ostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include "tools.h"
#include "../RecastNavDll/RecastDll.h"
std::string _strLastError;
int DoRecast(std::string mapPathName, std::string posFrom, std::string posTo);
int main(int argc, char* argv[])
{
std::cout << "共有" << argc << "个参数" << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << argv[i] << std::endl;
}
if (argc == 2 && _stricmp(argv[1], "/?") == 0)
{
printf_s("Recast工具 参数说明: ");
printf_s(" Recast [寻路文件名, 含路径] [(起点坐标:) x,y] [(终点坐标:) x,y]");
printf_s(" 举例:");
printf_s(" Recast solo_navmesh.bin 33.07,13.46 1103.89,478.21");
return 0;
}
if (argc == 5) //第一个参数是可执行文件自身,第二个参数是Recast标志,第三个参数是文件名,第四个参数是起点,第五个参数是终点
{
int ret = DoRecast(argv[2], argv[3], argv[4]);
if (ret == 0)
{
return 0;
}
else
{
std::string strRet = std::to_string(ret);
std::string error = "Recast工具 错误(" + strRet + ") - " + _strLastError;
printf_s(error.c_str());
return ret;
}
}
printf_s("Recast工具 参数错误(-1)!请使用 /? 查看参数说明。");
return -1;
}
int Find2(int id, std::string mapPathName, const std::string posFrom, const std::string posTo);
int DoRecast(const std::string mapPathName, const std::string posFrom, const std::string posTo)
{
// 1,初始化
if (!recast_init())
{
_strLastError = "Recast 初始化失败!";
return -11;
}
// 2,加载地图101
int id1 = 101;
if (!recast_loadmap(id1, mapPathName.c_str()))
{
_strLastError = "地图创建失败!- " + mapPathName;
return -12;
}
// 2,加载地图102
int id2 = 102;
if (!recast_loadmap(id2, mapPathName.c_str()))
{
_strLastError = "地图创建失败!- " + mapPathName;
return -12;
}
// 3,地图101开始寻路
printf_s("寻路开始...\n");
int ret = Find2(id1, mapPathName, posFrom, posTo);
if (ret < 0)
{
printf_s(_strLastError.c_str());
}
printf_s("...寻路结束 - ret:%d\n\n", ret);
// 3,地图102开始寻路
printf_s("寻路开始...\n");
ret = Find2(id2, mapPathName, posFrom, posTo);
if (ret < 0)
{
printf_s(_strLastError.c_str());
}
printf_s("...寻路结束 - ret:%d\n\n", ret);
// 4,释放地图
recast_freemap(id1);
recast_freemap(id2);
// 5,释放Recast引擎
recast_fini();
printf_s("Recast 寻路成功!");
return 0;
}
int Find2(int id, std::string mapPathName, const std::string posFrom, const std::string posTo)
{
Tools::dtStatus status;
std::vector<std::string> strsFrom = Tools::split(posFrom, ",");
std::vector<std::string> strsTo = Tools::split(posTo, ",");
//注意在Unity中因为坐标系原因,x需要为相反数
float spos[3] = {stof(strsFrom[0]), stof(strsFrom[1]), stof(strsFrom[2])};
float epos[3] = {stof(strsTo[0]), stof(strsTo[1]), stof(strsTo[2])};
// 1,寻路
status = recast_findpath(id, spos, epos);
if (Tools::dtStatusFailed(status))
{
int statusDetail = status & Tools::DT_STATUS_DETAIL_MASK;
std::string strDetail = std::to_string(statusDetail);
_strLastError = "寻路失败!错误码<" + strDetail + ">";
if (statusDetail == Tools::DT_COORD_INVALID)
{
char szFrom[256], szTo[256];
sprintf_s(szFrom, sizeof(szFrom), "%f, %f, %f", spos[0], spos[1], spos[2]);
sprintf_s(szTo, sizeof(szTo), "%f, %f, %f", epos[0], epos[1], epos[2]);
std::string strFrom = szFrom, strTo = szTo;
_strLastError += " - 坐标非法!From<" + strFrom + "> To<" + strTo + ">";
}
return -13;
}
else if (Tools::dtStatusInProgress(status))
{
return -14;
}
float* fixPos = new float[3];
memcpy(fixPos, recast_getfixposition(id, spos), sizeof(float) * 3);
float* fixPos2 = new float[3];
memcpy(fixPos2, recast_getfixposition(id, epos), sizeof(float) * 3);
// 2,得到实际(平滑)路径
recast_smooth(id, 2, 0.5);
// 寻路成功!
// 3,得到凸多边形id序列
std::string format = " 起点<%f,%f,%f> 终点<%f,%f,%f>\n";
printf_s(format.c_str(), spos[0], spos[1], spos[2], epos[0], epos[1], epos[2]);
int m_npolys = recast_getcountpoly(id);
unsigned int* m_polys = recast_getpathpoly(id);
std::string outputPoly = " 路线地块序号 (" + std::to_string(m_npolys) + "): \n ";
for (int i = 0; i < m_npolys; ++i)
{
std::string strPoly = std::to_string((m_polys[i]));
outputPoly += strPoly + ", ";
}
outputPoly += "\n";
printf_s(outputPoly.c_str());
// 4,得到寻路路径的坐标序列
int m_nsmoothPath = recast_getcountsmooth(id);
float* m_smoothPath = recast_getpathsmooth(id);
std::string outputSmooth = " 路线 (" + std::to_string(m_nsmoothPath) + "): \n";
for (int i = 0; i < m_nsmoothPath; ++i)
{
std::string strSmooth = std::to_string(m_smoothPath[i * 3]) + "," + std::to_string(m_smoothPath[i * 3 + 1]) +
"," + std::to_string(m_smoothPath[i * 3 + 2]);
outputSmooth += " <" + strSmooth + "> \n";
}
printf_s(outputSmooth.c_str());
return 1;
}
#include <iostream>
#include <string>
#include "tools.h"
namespace Tools
{
// #ifdef _UNICODE
// // 来源:https://www.cnblogs.com/happykoukou/p/5427268.html
// //字符串分割函数
// //因为只用到了坐标分割,所以这个版本的split不是很必要
// std::vector<std::wstring> Tools::split(std::wstring str, std::wstring pattern)
// {
// std::wstring::size_type pos;
// std::vector<std::wstring> result;
// str += pattern;//扩展字符串以方便操作
// size_t size = str.size();
// for (size_t i = 0; i < size; i++)
// {
// pos = str.find(pattern, i);
// if (pos < size)
// {
// std::wstring s = str.substr(i, pos - i);
// result.push_back(s);
// i = pos + pattern.size() - 1;
// }
// }
// return result;
// }
//
// #else
// 字符串分割函数
std::vector<std::string> split(std::string str, std::string pattern)
{
std::string::size_type pos;
std::vector<std::string> result;
str += pattern;//扩展字符串以方便操作
size_t size = str.size();
for (size_t i = 0; i < size; i++)
{
pos = str.find(pattern, i);
if (pos < size)
{
std::string s = str.substr(i, pos - i);
result.push_back(s);
i = pos + pattern.size() - 1;
}
}
return result;
}
FN_Proc GetFunc(_moduleType module, const char* funcName) {
_moduleType mod = reinterpret_cast<_moduleType>(module);
FN_Proc func;
func = (FN_Proc)(::GetProcAddress(mod, funcName));
return func;
}
// #endif
}
\ No newline at end of file
#pragma once
#include <string>
#include <vector>
#include <windows.h>
typedef HMODULE _moduleType;
typedef void (*FN_Proc)(void);
namespace Tools
{
// #ifdef _UNICODE
// std::vector<std::wstring> split(std::wstring str, std::wstring pattern);
// #else
std::vector<std::string> split(std::string str, std::string pattern);
// #endif
static const unsigned int DT_FAILURE = 1u << 31; // Operation failed.
static const unsigned int DT_SUCCESS = 1u << 30; // Operation succeed.
static const unsigned int DT_IN_PROGRESS = 1u << 29; // Operation still in progress.
// Detail information for status.
static const unsigned int DT_STATUS_DETAIL_MASK = 0x0ffffff;
static const unsigned int DT_WRONG_MAGIC = 1 << 0; // Input data is not recognized.
static const unsigned int DT_WRONG_VERSION = 1 << 1; // Input data is in wrong version.
static const unsigned int DT_OUT_OF_MEMORY = 1 << 2; // Operation ran out of memory.
static const unsigned int DT_INVALID_PARAM = 1 << 3; // An input parameter was invalid.
static const unsigned int DT_BUFFER_TOO_SMALL = 1 << 4; // Result buffer for the query was too small to store all results.
static const unsigned int DT_OUT_OF_NODES = 1 << 5; // Query ran out of nodes during search.
static const unsigned int DT_PARTIAL_RESULT = 1 << 6; // Query did not reach the end location, returning best guess.
static const unsigned int DT_ALREADY_OCCUPIED = 1 << 7; // A tile has already been assigned to the given x,y coordinate
static const unsigned int DT_COORD_INVALID = 1 << 13; // 新增错误码:传入坐标错误
typedef unsigned int dtStatus;
// Returns true of status is success.
inline bool dtStatusSucceed(dtStatus status)
{
return (status & DT_SUCCESS) != 0;
}
// Returns true of status is failure.
inline bool dtStatusFailed(dtStatus status)
{
return (status & DT_FAILURE) != 0;
}
// Returns true of status is in progress.
inline bool dtStatusInProgress(dtStatus status)
{
return (status & DT_IN_PROGRESS) != 0;
}
// Returns true if specific detail is set.
inline bool dtStatusDetail(dtStatus status, unsigned int detail)
{
return (status & detail) != 0;
}
FN_Proc GetFunc(_moduleType module, const char* funcName);
}
#include "CRecastHelper.h"
#include <iostream>
#include <string>
#include "../../../../Detour/Include/DetourCommon.h"
#include "../../../../Detour/Include/DetourNavMesh.h"
#include "../../../../Detour/Include/DetourNavMeshQuery.h"
#include "../../../../Recast/Include/Recast.h"
using namespace std;
/////////////////////////////////////////////////
// local functions
/////////////////////////////////////////////////
enum SamplePolyFlags
{
SAMPLE_POLYFLAGS_WALK = 0x01, // Ability to walk (ground, grass, road)
SAMPLE_POLYFLAGS_SWIM = 0x02, // Ability to swim (water).
SAMPLE_POLYFLAGS_DOOR = 0x04, // Ability to move through doors.
SAMPLE_POLYFLAGS_JUMP = 0x08, // Ability to jump.
SAMPLE_POLYFLAGS_DISABLED = 0x10, // Disabled polygon
SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
};
static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET';
static const int NAVMESHSET_VERSION = 1;
struct NavMeshSetHeader
{
int magic;
int version;
int numTiles;
dtNavMeshParams params;
};
struct NavMeshTileHeader
{
dtTileRef tileRef;
int dataSize;
};
dtNavMesh* loadMap(const char* path)
{
FILE* fp = NULL;
errno_t error = fopen_s(&fp, path, "rb");
if (!fp) return 0;
// Read header.
NavMeshSetHeader header;
size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp);
if (readLen != 1)
{
fclose(fp);
return 0;
}
if (header.magic != NAVMESHSET_MAGIC)
{
fclose(fp);
return 0;
}
if (header.version != NAVMESHSET_VERSION)
{
fclose(fp);
return 0;
}
dtNavMesh* mesh = dtAllocNavMesh();
if (!mesh)
{
fclose(fp);
return 0;
}
dtStatus status = mesh->init(&header.params);
if (dtStatusFailed(status))
{
fclose(fp);
return 0;
}
// Read tiles.
for (int i = 0; i < header.numTiles; ++i)
{
NavMeshTileHeader tileHeader;
readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
if (readLen != 1)
{
fclose(fp);
return 0;
}
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)
{
dtFree(data);
fclose(fp);
return 0;
}
mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
}
fclose(fp);
return mesh;
}
inline bool inRange(const float* v1, const float* v2, const float r, const float h)
{
const float dx = v2[0] - v1[0];
const float dy = v2[1] - v1[1];
const float dz = v2[2] - v1[2];
return (dx * dx + dz * dz) < r * r && fabsf(dy) < h;
}
static int fixupCorridor(dtPolyRef* path, const int npath, const int maxPath,
const dtPolyRef* visited, const int nvisited)
{
int furthestPath = -1;
int furthestVisited = -1;
// Find furthest common polygon.
for (int i = npath - 1; i >= 0; --i)
{
bool found = false;
for (int j = nvisited - 1; j >= 0; --j)
{
if (path[i] == visited[j])
{
furthestPath = i;
furthestVisited = j;
found = true;
}
}
if (found)
break;
}
// If no intersection found just return current path.
if (furthestPath == -1 || furthestVisited == -1)
return npath;
// Concatenate paths.
// Adjust beginning of the buffer to include the visited.
const int req = nvisited - furthestVisited;
const int orig = rcMin(furthestPath + 1, npath);
int size = rcMax(0, npath - orig);
if (req + size > maxPath)
size = maxPath - req;
if (size)
memmove(path + req, path + orig, size * sizeof(dtPolyRef));
// Store visited
for (int i = 0; i < req; ++i)
path[i] = visited[(nvisited - 1) - i];
return req + size;
}
// This function checks if the path has a small U-turn, that is,
// a polygon further in the path is adjacent to the first polygon
// in the path. If that happens, a shortcut is taken.
// This can happen if the target (T) location is at tile boundary,
// and we're (S) approaching it parallel to the tile edge.
// The choice at the vertex can be arbitrary,
// +---+---+
// |:::|:::|
// +-S-+-T-+
// |:::| | <-- the step can end up in here, resulting U-turn path.
// +---+---+
static int fixupShortcuts(dtPolyRef* path, int npath, dtNavMeshQuery* navQuery)
{
if (npath < 3)
return npath;
// Get connected polygons
static const int maxNeis = 16;
dtPolyRef neis[maxNeis];
int nneis = 0;
const dtMeshTile* tile = 0;
const dtPoly* poly = 0;
if (dtStatusFailed(navQuery->getAttachedNavMesh()->getTileAndPolyByRef(path[0], &tile, &poly)))
return npath;
for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
{
const dtLink* link = &tile->links[k];
if (link->ref != 0)
{
if (nneis < maxNeis)
neis[nneis++] = link->ref;
}
}
// If any of the neighbour polygons is within the next few polygons
// in the path, short cut to that polygon directly.
static const int maxLookAhead = 6;
int cut = 0;
for (int i = dtMin(maxLookAhead, npath) - 1; i > 1 && cut == 0; i--) {
for (int j = 0; j < nneis; j++)
{
if (path[i] == neis[j]) {
cut = i;
break;
}
}
}
if (cut > 1)
{
int offset = cut - 1;
npath -= offset;
for (int i = 1; i < npath; i++)
path[i] = path[i + offset];
}
return npath;
}
static bool getSteerTarget(dtNavMeshQuery* navQuery, const float* startPos, const float* endPos,
const float minTargetDist,
const dtPolyRef* path, const int pathSize,
float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef,
float* outPoints = 0, int* outPointCount = 0)
{
// Find steer target.
static const int MAX_STEER_POINTS = 3;
float steerPath[MAX_STEER_POINTS * 3];
unsigned char steerPathFlags[MAX_STEER_POINTS];
dtPolyRef steerPathPolys[MAX_STEER_POINTS];
int nsteerPath = 0;
navQuery->findStraightPath(startPos, endPos, path, pathSize,
steerPath, steerPathFlags, steerPathPolys, &nsteerPath, MAX_STEER_POINTS);
if (!nsteerPath)
return false;
if (outPoints && outPointCount)
{
*outPointCount = nsteerPath;
for (int i = 0; i < nsteerPath; ++i)
dtVcopy(&outPoints[i * 3], &steerPath[i * 3]);
}
// Find vertex far enough to steer to.
int ns = 0;
while (ns < nsteerPath)
{
// Stop at Off-Mesh link or when point is further than slop away.
if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
!inRange(&steerPath[ns * 3], startPos, minTargetDist, 1000.0f))
break;
ns++;
}
// Failed to find good point to steer to.
if (ns >= nsteerPath)
return false;
dtVcopy(steerPos, &steerPath[ns * 3]);
steerPos[1] = startPos[1];
steerPosFlag = steerPathFlags[ns];
steerPosRef = steerPathPolys[ns];
return true;
}
/////////////////////////////////////////////////
// class CRecast
/////////////////////////////////////////////////
CRecast::CRecast()
{
m_navMesh = NULL;
m_navQuery = NULL;
}
bool CRecast::LoadMap(const char* path)
{
m_navMesh = loadMap(path);
m_navQuery = dtAllocNavMeshQuery();
if (!m_navMesh)
return false;
m_navQuery->init(m_navMesh, 2048);
return true;
}
void CRecast::FreeMap()
{
dtFreeNavMeshQuery(m_navQuery);
}
unsigned int DT_COORD_INVALID = 1 << 13; // 新增错误码:传入坐标错误
/// <summary>
/// 寻路
/// </summary>
/// <param name="spos">float[3] 起点三维坐标</param>
/// <param name="epos">float[3] 终点三维坐标</param>
/// <returns>寻路的结果</returns>
dtStatus CRecast::FindPath(const float* spos, const float* epos)
{
dtVcopy(m_spos, spos);
dtVcopy(m_epos, epos);
m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL ^ SAMPLE_POLYFLAGS_DISABLED);
m_filter.setExcludeFlags(0);
m_navQuery->findNearestPoly(spos, m_polyPickExt, &m_filter, &m_startRef, 0);
m_navQuery->findNearestPoly(epos, m_polyPickExt, &m_filter, &m_endRef, 0);
if (!m_startRef || !m_endRef)
{
return DT_FAILURE| DT_COORD_INVALID;
}
// 开始寻路
dtStatus status = m_navQuery->findPath(m_startRef, m_endRef, spos, epos, &m_filter, m_polys, &m_npolys, MAX_POLYS);
return status;
}
dtStatus CRecast::Raycast(const float* spos, const float* epos)
{
dtVcopy(m_spos, spos);
dtVcopy(m_epos, epos);
m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL ^ SAMPLE_POLYFLAGS_DISABLED);
m_filter.setExcludeFlags(0);
m_navQuery->findNearestPoly(spos, m_polyPickExt, &m_filter, &m_startRef, 0);
m_navQuery->findNearestPoly(epos, m_polyPickExt, &m_filter, &m_endRef, 0);
if (!m_startRef || !m_endRef)
{
return DT_FAILURE | DT_COORD_INVALID;
}
float hit_param = FLT_MAX;
float hit_normal[3] = {0.0f};
memset(&m_npolys, 0, sizeof(dtPolyRef) * MAX_POLYS);
m_npolys = 0;
// float target[3] = {m_epos[0], m_epos[1], m_epos[2]};
dtPolyRef target_ref;
float nearest[3] = {0};
dtStatus status = m_navQuery->findNearestPoly(
m_epos, m_polyPickExt, &m_filter, &target_ref, nearest);
if (dtStatusFailed(status)) return false;
if (!m_navQuery->isValidPolyRef(target_ref, &m_filter)) return false;
// fix 目标点高度
m_epos[1] = nearest[1];
// 开始射线检测
status = m_navQuery->raycast(m_startRef, m_spos, m_epos, &m_filter, &hit_param, hit_normal, m_polys, &m_npolys, MAX_POLYS);
if (dtStatusFailed(status)) return status;
//计算碰撞点逻辑
if (0.0f < hit_param && hit_param < 1.0f)
{
float delta[3];
for (int i = 0; i < 3; ++i)
{
delta[i] = m_epos[i] - m_spos[i];
m_hitPos[i] = m_spos[i] + delta[i] * hit_param;
}
}
else if (0.000001f > hit_param)
{
memcpy(m_hitPos, m_spos, sizeof(m_hitPos));
}
else
{
memcpy(m_hitPos, m_epos, sizeof(m_hitPos));
}
return status;
}
void CRecast::Smooth(float STEP_SIZE, float SLOP)
{
m_nsmoothPath = 0;
if (m_npolys)
{
// Iterate over the path to find smooth path on the detail mesh surface.
dtPolyRef polys[MAX_POLYS];
memcpy(polys, m_polys, sizeof(dtPolyRef) * m_npolys);
int npolys = m_npolys;
float iterPos[3], targetPos[3];
m_navQuery->closestPointOnPoly(m_startRef, m_spos, iterPos, 0);
m_navQuery->closestPointOnPoly(polys[npolys - 1], m_epos, targetPos, 0);
if(STEP_SIZE == 0) STEP_SIZE = 0.5f;
if(SLOP == 0) SLOP = 0.01f;
m_nsmoothPath = 0;
dtVcopy(&m_smoothPath[m_nsmoothPath * 3], iterPos);
m_nsmoothPath++;
// Move towards target a small advancement at a time until target reached or
// when ran out of memory to store the path.
while (npolys && m_nsmoothPath < MAX_SMOOTH)
{
// Find location to steer towards.
float steerPos[3];
unsigned char steerPosFlag;
dtPolyRef steerPosRef;
if (!getSteerTarget(m_navQuery, iterPos, targetPos, SLOP,
polys, npolys, steerPos, steerPosFlag, steerPosRef))
break;
bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END) ? true : false;
bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ? true : false;
// Find movement delta.
float delta[3], len;
dtVsub(delta, steerPos, iterPos);
len = dtMathSqrtf(dtVdot(delta, delta));
// If the steer target is end of path or off-mesh link, do not move past the location.
if ((endOfPath || offMeshConnection) && len < STEP_SIZE)
len = 1;
else
len = STEP_SIZE / len;
float moveTgt[3];
dtVmad(moveTgt, iterPos, delta, len);
// Move
float result[3];
dtPolyRef visited[16];
int nvisited = 0;
m_navQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &m_filter,
result, visited, &nvisited, 16);
npolys = fixupCorridor(polys, npolys, MAX_POLYS, visited, nvisited);
npolys = fixupShortcuts(polys, npolys, m_navQuery);
// BUG FIX:原来是h=0, 这里改为目的地的高度,否则如果getPolyHeight()没有得到数值(这是有可能的),则高度为0,就错了。Aug.2.2020. Liu Gang.
// 原因:如果这里的getPolyHeight()获取失败,则h就为零了,而下一个循环,就会在上面的getSteerTarget()中结束,这里就是最后一个节点,高度为零
float h = result[1];
m_navQuery->getPolyHeight(polys[0], result, &h);
result[1] = h;
dtVcopy(iterPos, result);
// Handle end of path and off-mesh links when close enough.
if (endOfPath && inRange(iterPos, steerPos, SLOP, 1.0f))
{
// Reached end of path.
dtVcopy(iterPos, targetPos);
if (m_nsmoothPath < MAX_SMOOTH)
{
dtVcopy(&m_smoothPath[m_nsmoothPath * 3], iterPos);
m_nsmoothPath++;
}
break;
}
else if (offMeshConnection && inRange(iterPos, steerPos, SLOP, 1.0f))
{
// Reached off-mesh connection.
float startPos[3], endPos[3];
// Advance the path up to and over the off-mesh connection.
dtPolyRef prevRef = 0, polyRef = polys[0];
int npos = 0;
while (npos < npolys && polyRef != steerPosRef)
{
prevRef = polyRef;
polyRef = polys[npos];
npos++;
}
for (int i = npos; i < npolys; ++i)
polys[i - npos] = polys[i];
npolys -= npos;
// Handle the connection.
dtStatus status = m_navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos);
if (dtStatusSucceed(status))
{
if (m_nsmoothPath < MAX_SMOOTH)
{
dtVcopy(&m_smoothPath[m_nsmoothPath * 3], startPos);
m_nsmoothPath++;
// Hack to make the dotted path not visible during off-mesh connection.
if (m_nsmoothPath & 1)
{
dtVcopy(&m_smoothPath[m_nsmoothPath * 3], startPos);
m_nsmoothPath++;
}
}
// Move position at the other side of the off-mesh link.
dtVcopy(iterPos, endPos);
float eh = 0.0f;
m_navQuery->getPolyHeight(polys[0], iterPos, &eh);
iterPos[1] = eh;
}
}
// Store results.
if (m_nsmoothPath < MAX_SMOOTH)
{
dtVcopy(&m_smoothPath[m_nsmoothPath * 3], iterPos);
m_nsmoothPath++;
}
}
}
}
float* CRecast::fixPosition(const float* pos) {
dtPolyRef posRef;
m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL ^ SAMPLE_POLYFLAGS_DISABLED);
m_filter.setExcludeFlags(0);
float nearest[3] = {0};
dtStatus status = m_navQuery->findNearestPoly(pos, m_polyPickExt, &m_filter,
&posRef, nearest);
if (dtStatusFailed(status)) return nullptr;
if (!posRef) return nullptr; // 有时候返回成功,但是也没有找到对应的多边形,实际上还是出错了
memcpy(m_fixPos, nearest, sizeof(m_fixPos));
return m_fixPos;
//float h = 0;
//status = m_navQuery->getPolyHeight(posRef, nearest, &h);
//if (!dtStatusFailed(status))
//{
// m_fixPos[1] = h;
//}
//return m_fixPos;
}
/////////////////////////////////////////////////
// class CRecastHelper
/////////////////////////////////////////////////
CRecastHelper::~CRecastHelper()
{
for (map<int, CRecast*>::iterator iter = m_mapRecast.begin(); iter != m_mapRecast.end(); iter++)
{
delete iter->second;
}
m_mapRecast.clear();
}
bool CRecastHelper::LoadMap(int id, const char* path)
{
CRecast* recast = new CRecast();
bool ret = recast->LoadMap(path);
if (!ret)
return false;
m_mapRecast[id] = recast;
return true;
}
void CRecastHelper::FreeMap(int id)
{
m_mapRecast[id]->FreeMap();
delete m_mapRecast[id];
m_mapRecast.erase(id);
}
#pragma once
#include <map>
#include "../../../../Detour/Include/DetourNavMesh.h"
#include "../../../../Detour/Include/DetourNavMeshQuery.h"
static const int MAX_POLYS = 256;
static const int MAX_SMOOTH = 2048;
class CRecast
{
dtNavMesh* m_navMesh;
dtNavMeshQuery* m_navQuery;
float m_spos[3];
float m_epos[3];
dtPolyRef m_startRef, m_endRef;
float m_polyPickExt[3] = { 2,40,2 };
dtQueryFilter m_filter;
dtPolyRef m_polys[MAX_POLYS];
int m_npolys = 0;
float m_smoothPath[MAX_SMOOTH * 3];
int m_nsmoothPath;
float m_fixPos[3];
float m_hitPos[3];
public:
CRecast();
bool LoadMap(const char* path);
void FreeMap();
dtStatus FindPath(const float* spos, const float* epos);
dtStatus Raycast(const float* spos, const float* epos);
// stepSize : 如果给0,会自动变成0.5f
// slop : 如果给0,会自动变成0.01f
void Smooth(float STEP_SIZE, float SLOP);
int GetCountPoly() { return m_npolys; }
int GetCountSmooth() { return m_nsmoothPath; }
dtPolyRef* GetPathPoly(int* polyCount) { *polyCount = m_npolys; return m_polys; }
float* GetPathSmooth(int* smoothCount) { *smoothCount = m_nsmoothPath; return m_smoothPath; }
float* fixPosition(const float* pos);
float* GetHitPosition() { return m_hitPos; }
};
class CRecastHelper
{
std::map<int, CRecast*> m_mapRecast;
public:
CRecastHelper() {}
~CRecastHelper();
CRecast* Get(int id) { return m_mapRecast[id]; }
bool LoadMap(int id, const char* path);
void FreeMap(int id);
};
\ No newline at end of file
#include "RecastDll.h"
#include "CRecastHelper.h"
extern "C"
{
CRecastHelper* _helper = NULL;
DllExport bool recast_init()
{
_helper = new CRecastHelper();
return true;
}
void recast_fini()
{
if (_helper)
delete _helper;
_helper = NULL;
}
bool recast_loadmap(int id, const char* path)
{
if (!_helper) return false;
if (!_helper->LoadMap(id, path))
return false;
return true;
}
bool recast_freemap(int id)
{
if (!_helper || !_helper->Get(id)) return false;
_helper->FreeMap(id);
return true;
}
int recast_findpath(int id, const float* spos, const float* epos)
{
if (!_helper || !_helper->Get(id)) return 0;
dtStatus status = _helper->Get(id)->FindPath(spos, epos);
return status;
}
bool recast_smooth(int id, float step_size, float slop)
{
if (!_helper || !_helper->Get(id)) return false;
_helper->Get(id)->Smooth(step_size, slop);
return true;
}
int recast_raycast(int id, const float* spos, const float* epos)
{
if (!_helper || !_helper->Get(id)) return 0;
dtStatus status = _helper->Get(id)->Raycast(spos, epos);
return status;
}
float* recast_gethitposition(int id)
{
if (!_helper || !_helper->Get(id)) return NULL;
return _helper->Get(id)->GetHitPosition();
}
int recast_getcountpoly(int id)
{
if (!_helper || !_helper->Get(id)) return 0;
return _helper->Get(id)->GetCountPoly();
}
int recast_getcountsmooth(int id)
{
if (!_helper || !_helper->Get(id)) return 0;
return _helper->Get(id)->GetCountSmooth();
}
unsigned int* recast_getpathpoly(int id)
{
if (!_helper || !_helper->Get(id)) return NULL;
int count = 0;
unsigned int* polys = _helper->Get(id)->GetPathPoly(&count);
if (count == 0)
return NULL;
return polys;
}
float* recast_getpathsmooth(int id)
{
if (!_helper || !_helper->Get(id)) return NULL;
int count = 0;
float* polys = _helper->Get(id)->GetPathSmooth(&count);
if (count == 0)
return NULL;
return polys;
}
float* recast_getfixposition(int id, const float* pos)
{
if (!_helper || !_helper->Get(id)) return NULL;
float* fixPos = _helper->Get(id)->fixPosition(pos);
return fixPos;
}
}
#pragma once
#define DllExport __declspec(dllexport)
extern "C"
{
/// <summary>
/// 初始化Recast引擎
/// </summary>
/// <returns></returns>
DllExport bool recast_init();
/// <summary>
/// 结束化Recast引擎
/// </summary>
/// <returns></returns>
DllExport void recast_fini();
/// <summary>
/// 加载地图数据,支持同时打开多张地图,用id来查找
/// </summary>
/// <param name="id">地图Id</param>
/// <param name="path">地图文件名(含路径)</param>
/// <returns></returns>
DllExport bool recast_loadmap(int id, const char* path);
/// <summary>
/// 释放地图数据
/// </summary>
/// <param name="id">地图Id</param>
/// <returns></returns>
DllExport bool recast_freemap(int id);
/// <summary>
/// 寻路,寻路的结果其实只是返回从起点到终点之间所有经过的凸多边形的序号
/// </summary>
/// <param name="id">地图Id</param>
/// <param name="spos">起点坐标,float[3]</param>
/// <param name="epos">终点坐标,float[3]</param>
/// <returns>返回寻路结果:1u << 30 - 成功;1u << 31 - 失败;1u << 29 - 寻路未完成,详细内容见:DetourStatus.h</returns>
DllExport int recast_findpath(int id, const float* spos, const float* epos);
/// <summary>
/// 计算平滑路径,其实是根据findpath得到的【从起点到终点所经过的凸多边形的序号】,得到真正的路径(三维坐标),所以这一步是不可缺少的
/// </summary>
/// <param name="id">地图Id</param>
/// <param name="step_size">平滑参数,步长,越小越平滑,如果给0,则自动变为0.5</param>
/// <param name="slop">用途不明(但肯定不影响平滑),如果给0,则自动变为0.01</param>
/// <returns></returns>
DllExport bool recast_smooth(int id, float step_size, float slop);
/// <summary>
/// 射线检测,从起始位置向终点位置发射一个射线,中间遇到阻挡停止,并返回阻挡Poly
/// </summary>
/// <param name="id">地图Id</param>
/// <param name="spos">起点坐标,float[3]</param>
/// <param name="epos">终点坐标,float[3]</param>
/// <returns>返回寻路结果:1u << 30 - 成功;1u << 31 - 失败;1u << 29 -
/// 寻路未完成,详细内容见:DetourStatus.h</returns>
DllExport int recast_raycast(int id, const float* spos, const float* epos);
/// <summary>
/// 获取射线检测的碰撞点,配合recast_raycast()函数使用
/// </summary>
/// <param name="id">地图Id</param>
/// <returns>返回碰撞点坐标</returns>
DllExport float* recast_gethitposition(int id);
/// <summary>
/// 寻路后,得到路线经过的凸多边形id的个数
/// </summary>
/// <param name="id">地图Id</param>
/// <returns></returns>
DllExport int recast_getcountpoly(int id);
/// <summary>
/// 寻路后,得到具体的路径节点的个数
/// </summary>
/// <param name="id">地图Id</param>
/// <returns></returns>
DllExport int recast_getcountsmooth(int id);
/// <summary>
/// 得到pathfind以后,从起点到终点所经过的所有凸多边形id的序列
/// </summary>
/// <param name="id">地图Id</param>
/// <returns>得到凸多边形id的序列</returns>
DllExport unsigned int* recast_getpathpoly(int id);
/// <summary>
/// 得到smooth以后,路线的三维坐标序列
/// </summary>
/// <param name="id">地图Id</param>
/// <returns>得到寻路坐标序列,每(x,y,z)三个数值为一个单元,所以实际返回的数量是smoothCount的3倍</returns>
DllExport float* recast_getpathsmooth(int id);
/// <summary>
/// 修正坐标
/// </summary>
/// <param name="id">地图Id</param>
/// <param name="spos">坐标,float[3]</param>
/// <returns>得到修复后的坐标点</returns>
DllExport float* recast_getfixposition(int id, const float* pos);
}
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
namespace ET
......@@ -20,7 +21,7 @@ namespace ET
self.BroadcastPath(path, i, 3);
}
Vector3 v3 = path[i];
//await self.Parent.GetComponent<MoveComponent>().MoveToAsync(v3, self.CancellationToken);
await self.Parent.GetComponent<MoveComponent>().MoveToAsync(v3, self.CancellationToken);
}
await ETTask.CompletedTask;
}
......@@ -35,15 +36,20 @@ namespace ET
self.Target = target;
Unit unit = self.GetParent<Unit>();
unit.Position = target;
M2C_PathfindingResult m2CPathfindingResult = new M2C_PathfindingResult();
m2CPathfindingResult.X = unit.Position.x;
m2CPathfindingResult.Y = unit.Position.y;
m2CPathfindingResult.Z = unit.Position.z;
m2CPathfindingResult.Id = unit.Id;
MessageHelper.Broadcast(unit, m2CPathfindingResult);
await ETTask.CompletedTask;
RecastPathComponent recastPathComponent = self.Domain.GetComponent<RecastPathComponent>();
RecastPath recastPath = EntityFactory.Create<RecastPath>((Entity)self.Domain);
recastPath.StartPos = unit.Position;
recastPath.EndPos = new Vector3(target.x, target.y, target.z);
self.RecastPath = recastPath;
//TODO 因为目前阶段只有一张地图,所以默认mapId为10001
recastPathComponent.SearchPath(10001, self.RecastPath);
//Log.Debug($"------start Pos: {self.RecastPath.StartPos}\n------end Pos: {self.RecastPath.EndPos}\n------find result: {self.RecastPath.Results.ListToString()}");
self.CancellationToken?.Cancel();
self.CancellationToken = new ETCancellationToken();
await self.MoveAsync(self.RecastPath.Results);
self.CancellationToken = null;
}
// 从index找接下来3个点,广播
......@@ -56,7 +62,19 @@ namespace ET
m2CPathfindingResult.Y = unitPos.y;
m2CPathfindingResult.Z = unitPos.z;
m2CPathfindingResult.Id = unit.Id;
MessageHelper.Broadcast(unit, m2CPathfindingResult);
for (int i = 0; i < offset; ++i)
{
if (index + i >= self.RecastPath.Results.Count)
{
break;
}
Vector3 v = self.RecastPath.Results[index + i];
m2CPathfindingResult.Xs.Add(v.x);
m2CPathfindingResult.Ys.Add(v.y);
m2CPathfindingResult.Zs.Add(v.z);
}
MessageHelper.Broadcast(self.GetParent<Unit>(), m2CPathfindingResult) ;
}
}
}
\ No newline at end of file
......@@ -32,6 +32,7 @@ namespace ET
break;
case SceneType.Map:
scene.AddComponent<UnitComponent>();
scene.AddComponent<RecastPathComponent>();
break;
case SceneType.Location:
scene.AddComponent<LocationComponent>();
......
......@@ -37,7 +37,7 @@ namespace ET
TimerComponent timerComponent = Game.Scene.GetComponent<TimerComponent>();
// 协程如果取消,将算出玩家的真实位置,赋值给玩家
cancellationToken.Add(() =>
cancellationToken?.Add(() =>
{
long timeNow = TimeHelper.ClientNow();
if (timeNow - this.StartTime >= this.needTime)
......@@ -53,7 +53,11 @@ namespace ET
while (true)
{
await timerComponent.WaitAsync(50, cancellationToken);
//新版TimerComponent实现不同于5.0的TimerComponent,需要自己判断是取消还是自然结束,并且return,否则将不会取消任务,并可能会造成cancellationToken泄漏
if (!await timerComponent.WaitAsync(50, cancellationToken))
{
return;
}
long timeNow = TimeHelper.ClientNow();
......
using System;
using System.Collections;
using System.Collections.Generic;
namespace cyh.game
{
public class BitArray : IList<bool>
{
private byte[] array;
private ulong count;
public int Count { get { return (int)this.count; } }
bool ICollection<bool>.IsReadOnly => false;
public BitArray()
{
this.array = Array.Empty<byte>();
this.count = 0;
}
public BitArray(int capacity)
{
this.array = new byte[capacity];
this.count = 0;
}
private void resize(int minsize)
{
if (minsize == 0)
{
if (this.array.Length == 0)
this.array = new byte[4];
else
Array.Resize<byte>(ref this.array, this.array.Length << 1);
}
else if (minsize > this.array.Length)
{
Array.Resize<byte>(ref this.array, minsize);
}
}
public bool this[int index]
{
get
{
if (index < 0)
throw new ArgumentOutOfRangeException();
int offset = index & 7;
index >>= 3;
return (this.array[index] & (1 << offset)) != 0;
}
set
{
int offset = index & 7;
if ((ulong)index >= this.count)
{
this.count = (ulong)index + 1;
index >>= 3;
if (index >= this.array.Length)
this.resize(index + 1);
}
else
index >>= 3;
if (value)
this.array[index] |= (byte)(1 << offset);
else
this.array[index] &= (byte)~(1 << offset);
}
}
public bool this[ulong index]
{
get
{
int offset = (int)(index & 7);
index >>= 3;
return (this.array[index] & (1 << offset)) != 0;
}
set
{
int offset = (int)(index & 7);
if ((ulong)index >= this.count)
{
this.count = (ulong)index + 1;
index >>= 3;
if (index >= (ulong)this.array.Length)
this.resize((int)index + 1);
}
else
index >>= 3;
if (value)
this.array[index] |= (byte)(1 << offset);
else
this.array[index] &= (byte)~(1 << offset);
}
}
public void Add(bool bit)
{
this.count++;
byte offset = (byte)(this.count & 7);
int index = (int)(this.count >> 3);
if (index >= this.array.Length)
this.resize(0);
if (bit)
this.array[index] |= (byte)(1 << offset);
else
this.array[index] &= (byte)~(1 << offset);
}
public override string ToString()
{
System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder((int)this.count);
for (ulong i = 0; i < this.count; i++)
{
stringBuilder.Append(this[i] ? '1' : '0');
}
return stringBuilder.ToString();
}
int IList<bool>.IndexOf(bool item)
{
throw new NotSupportedException();
}
void IList<bool>.Insert(int index, bool item)
{
throw new NotSupportedException();
}
void IList<bool>.RemoveAt(int index)
{
throw new NotSupportedException();
}
void ICollection<bool>.Clear()
{
this.array = Array.Empty<byte>();
this.count = 0;
}
bool ICollection<bool>.Contains(bool item)
{
throw new NotSupportedException();
}
void ICollection<bool>.CopyTo(bool[] array, int arrayIndex)
{
for (ulong i = 0; i < this.count; i++)
{
array[arrayIndex++] = this[i];
}
}
bool ICollection<bool>.Remove(bool item)
{
throw new NotSupportedException();
}
public IEnumerator<bool> GetEnumerator()
{
return new Enumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
class Enumerator : IEnumerator<bool>
{
private BitArray Array;
private long index = -1;
public Enumerator(BitArray array)
{
this.Array = array;
}
public bool Current { get { return this.Array[(ulong)this.index]; } }
object IEnumerator.Current { get { return this.Array[(ulong)this.index]; } }
public void Dispose()
{
this.index = -1;
}
public bool MoveNext()
{
this.index++;
return this.index < (long)this.Array.count;
}
public void Reset()
{
this.index = -1;
}
}
public static void Set(byte[] data, int index, bool value)
{
if (index < 0)
throw new ArgumentOutOfRangeException();
int offset = index & 7;
index >>= 3;
if (index >= data.Length)
throw new ArgumentOutOfRangeException();
if (value)
data[index] |= (byte)(1 << offset);
else
data[index] &= (byte)~(1 << offset);
}
public static bool Get(byte[] data, int index)
{
int offset = index & 7;
index >>= 3;
if (index >= data.Length)
throw new ArgumentOutOfRangeException();
return (data[index] & (1 << offset)) != 0;
}
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
fileFormatVersion: 2
guid: 14ae219f71c290748a6737911d468d1a
guid: daf158bc563a309438e4f90016ac2761
folderAsset: yes
DefaultImporter:
externalObjects: {}
......
fileFormatVersion: 2
guid: e46fb3caa1c3c483c8071b18a47e5936
folderAsset: yes
DefaultImporter:
guid: c25078034bc07e841a02f3d3bbed3963
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册