Compare commits
62 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
60ae5090be | ||
![]() |
e3dc744722 | ||
![]() |
91aac9611c | ||
![]() |
3a1f732d38 | ||
![]() |
d116fc41df | ||
![]() |
b5d015110f | ||
![]() |
2e61aa105a | ||
![]() |
fe61fb7d12 | ||
![]() |
e5028fdc79 | ||
![]() |
c45c43d7fa | ||
![]() |
dfb6837666 | ||
![]() |
9d65aff228 | ||
![]() |
3a5ae8d071 | ||
![]() |
ee5edc4d5e | ||
![]() |
999505efda | ||
![]() |
9240c61c92 | ||
![]() |
f30bf003ad | ||
![]() |
d8c3676876 | ||
![]() |
0ecb37d272 | ||
![]() |
3fdf02a816 | ||
![]() |
ef0938fb26 | ||
![]() |
24d221d934 | ||
![]() |
d129fb5bb3 | ||
![]() |
f53ce64d02 | ||
![]() |
72b2c19a55 | ||
![]() |
a80f2405a3 | ||
![]() |
07b134b10b | ||
![]() |
67b7c7297a | ||
![]() |
9a91ca6670 | ||
![]() |
deed6b525d | ||
![]() |
be26364882 | ||
![]() |
f7951cb780 | ||
![]() |
e18ba7c803 | ||
![]() |
244e92f1fa | ||
![]() |
db1d19a46e | ||
![]() |
3c4be5d3b8 | ||
![]() |
13a3180041 | ||
![]() |
d7c6c79045 | ||
![]() |
0223227a47 | ||
![]() |
8229584cbe | ||
![]() |
8f0a1bfc5a | ||
![]() |
ddba1267b8 | ||
![]() |
7ff62bc87e | ||
![]() |
1f57e552ff | ||
![]() |
e57d842cba | ||
![]() |
ee42c4e38e | ||
![]() |
de55876ca5 | ||
![]() |
710dc290f7 | ||
![]() |
ab8e7eee21 | ||
![]() |
d5abdfbc95 | ||
![]() |
0dd4f74bee | ||
![]() |
74baec90e6 | ||
![]() |
4ef4228bbb | ||
![]() |
d0ee13ac0a | ||
![]() |
e40397fa60 | ||
![]() |
6af635e301 | ||
![]() |
b4fb10686c | ||
![]() |
8452642179 | ||
![]() |
dd6369d5e6 | ||
![]() |
83be6833b6 | ||
![]() |
6341a1a31f | ||
![]() |
6e9cca9801 |
@ -23,8 +23,10 @@ include_directories(
|
||||
/usr/include/glm
|
||||
../../third_party
|
||||
../../third_party/behaviac/inc
|
||||
../../third_party/recastnavigation/Recast/Include
|
||||
../../third_party/recastnavigation/Detour/Include
|
||||
../../third_party/recastnavigation/DetourTileCache/Include
|
||||
../../third_party/recastnavigation/RecastDemo/Contrib/fastlz
|
||||
.
|
||||
)
|
||||
|
||||
@ -42,6 +44,14 @@ aux_source_directory(../../third_party/framework/cpp
|
||||
SRC_LIST
|
||||
)
|
||||
|
||||
aux_source_directory(../../third_party/recastnavigation/Recast/Source
|
||||
SRC_LIST
|
||||
)
|
||||
|
||||
aux_source_directory(../../third_party/recastnavigation/RecastDemo/Contrib/fastlz
|
||||
SRC_LIST
|
||||
)
|
||||
|
||||
aux_source_directory(../../third_party/recastnavigation/Detour/Source
|
||||
SRC_LIST
|
||||
)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "playermgr.h"
|
||||
#include "mapmgr.h"
|
||||
#include "entityfactory.h"
|
||||
#include "navmeshbuilder.h"
|
||||
#include "perfmonitor.h"
|
||||
|
||||
#include "ss_msgid.pb.h"
|
||||
@ -139,6 +140,7 @@ bool App::Init(int argc, char* argv[])
|
||||
EntityFactory::Instance()->Init();
|
||||
uuid.SetMachineId((node_id - 1) * MAX_NODE_ID + instance_id);
|
||||
RoomMgr::Instance()->Init();
|
||||
NavMeshBuilder::Instance()->Init();
|
||||
MapMgr::Instance()->Init();
|
||||
PlayerMgr::Instance()->Init();
|
||||
GGListener::Instance()->Init();
|
||||
@ -177,6 +179,7 @@ void App::UnInit()
|
||||
GGListener::Instance()->UnInit();
|
||||
PlayerMgr::Instance()->UnInit();
|
||||
MapMgr::Instance()->UnInit();
|
||||
NavMeshBuilder::Instance()->UnInit();
|
||||
RoomMgr::Instance()->UnInit();
|
||||
EntityFactory::Instance()->UnInit();
|
||||
MetaMgr::Instance()->UnInit();
|
||||
|
@ -385,3 +385,22 @@ const int TERMINATOR_BUFF_ID = 1033;
|
||||
|
||||
const int TURN_OVER_SKILL_ID = 41001;
|
||||
|
||||
/*
|
||||
1 2 3
|
||||
4 5 6
|
||||
7 8 9
|
||||
*/
|
||||
const int GRID9_AGROUD_CELL_NUM = 8;
|
||||
const int GRID9_AGROUND_CELL_INDEX[GRID9_AGROUD_CELL_NUM][2] =
|
||||
{
|
||||
{-1, -1}, //1
|
||||
{0, -1}, //2
|
||||
{1, -1}, //3
|
||||
|
||||
{-1, 0}, //4
|
||||
{1, 0}, //6
|
||||
|
||||
{-1, 1}, //7
|
||||
{0, 1}, //8
|
||||
{1, 1} //9
|
||||
};
|
||||
|
@ -45,6 +45,7 @@ class Entity
|
||||
float GetY() { return pos_.y; }
|
||||
void SetX(float x) { pos_.x = x; }
|
||||
void SetY(float y) { pos_.y = y; }
|
||||
std::list<ColliderComponent*>* GetColliders() { return &colliders_; }
|
||||
|
||||
protected:
|
||||
bool IsClientCached(Human* hum);
|
||||
|
@ -3206,6 +3206,14 @@ int Human::GetItemNum(int item_id)
|
||||
return itr != items_.end() ? itr->second : 0;
|
||||
}
|
||||
|
||||
void Human::ClearFindPathStatus()
|
||||
{
|
||||
if (findpath_status_) {
|
||||
room->map_service->DelFindPathRequest(this, findpath_status_);
|
||||
findpath_status_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Human::AddItem(int item_id, int item_num)
|
||||
{
|
||||
auto itr = items_.find(item_id);
|
||||
|
@ -26,6 +26,7 @@ enum HumanStatus
|
||||
};
|
||||
|
||||
struct xtimer_list;
|
||||
struct FindPathStatus;
|
||||
class CircleCollider;
|
||||
class AabbCollider;
|
||||
class Obstacle;
|
||||
@ -264,6 +265,9 @@ class Human : public MoveableEntity
|
||||
void ProcBuffEffect(Human* caster, Buff* buff);
|
||||
int GetLevel() {return level_;};
|
||||
int GetExp() {return exp_;};
|
||||
FindPathStatus* GetFindPathStatus() { return findpath_status_; }
|
||||
void SetFindPathStatus(FindPathStatus* status) { findpath_status_ = status; }
|
||||
void ClearFindPathStatus();
|
||||
void OnAttack() {};
|
||||
void OnHit() {};
|
||||
int GetItemNum(int item_id);
|
||||
@ -390,6 +394,7 @@ private:
|
||||
std::set<int> battling_items_;
|
||||
size_t normal_drop_times_ = 0;
|
||||
size_t box_drop_times_ = 0;
|
||||
FindPathStatus* findpath_status_ = nullptr;
|
||||
std::array<Buff*, kBET_End> buff_effect_ = {};
|
||||
|
||||
std::array<float, kHAT_End> buff_attr_abs_ = {};
|
||||
@ -417,3 +422,4 @@ void InternalShot(Human* hum,
|
||||
int skill_id,
|
||||
float fly_distance,
|
||||
bool is_tank_skin);
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include "precompile.h"
|
||||
#include "precompile.h"
|
||||
|
||||
#include "DetourNavMesh.h"
|
||||
|
||||
#include "mapinstance.h"
|
||||
#include "mapservice.h"
|
||||
@ -10,8 +12,15 @@
|
||||
#include "metamgr.h"
|
||||
#include "room.h"
|
||||
#include "entityfactory.h"
|
||||
#include "collider.h"
|
||||
#include "navmeshbuilder.h"
|
||||
#include "navmeshhelper.h"
|
||||
|
||||
#ifdef FIND_PATH_TEST
|
||||
const int MAP_GRID_WIDTH = 40;
|
||||
#else
|
||||
const int MAP_GRID_WIDTH = 64;
|
||||
#endif
|
||||
|
||||
void MapInstance::Init()
|
||||
{
|
||||
@ -53,6 +62,15 @@ void MapInstance::Init()
|
||||
if (current_uniid_ >= FIXED_OBJECT_MAXID) {
|
||||
abort();
|
||||
}
|
||||
#ifdef DEBUG
|
||||
NavMeshHelper::OutputObjFile(this);
|
||||
#endif
|
||||
#ifdef FIND_PATH_TEST
|
||||
if (map_meta_->i->map_id() == 4001) {
|
||||
NavMeshBuilder::Instance()->Build(this);
|
||||
map_service_->SetNavMesh(navmesh_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MapInstance::UnInit()
|
||||
|
@ -6,6 +6,8 @@ namespace MetaData
|
||||
struct MapTplThing;
|
||||
}
|
||||
|
||||
class dtNavMesh;
|
||||
class dtTileCache;
|
||||
class Entity;
|
||||
class Obstacle;
|
||||
class Building;
|
||||
@ -33,6 +35,8 @@ class MapInstance
|
||||
int AllocUniid();
|
||||
|
||||
private:
|
||||
dtTileCache* tile_cache_ = nullptr;
|
||||
dtNavMesh* navmesh_ = nullptr;
|
||||
int current_uniid_ = 0;
|
||||
std::map<int, Entity*> uniid_hash_;
|
||||
|
||||
@ -53,4 +57,7 @@ class MapInstance
|
||||
int obstacle0_num_ = 0;
|
||||
int obstacle1_num_ = 0;
|
||||
int obstacle2_num_ = 0;
|
||||
|
||||
friend class NavMeshBuilder;
|
||||
friend class NavMeshHelper;
|
||||
};
|
||||
|
68
server/gameserver/mapsearchnode.cc
Normal file
68
server/gameserver/mapsearchnode.cc
Normal file
@ -0,0 +1,68 @@
|
||||
#include "precompile.h"
|
||||
|
||||
#include "mapsearchnode.h"
|
||||
#include "mapservice.h"
|
||||
#include "human.h"
|
||||
#include "room.h"
|
||||
|
||||
float MapSearchNode::GoalDistanceEstimate(MapSearchNode &node_goal)
|
||||
{
|
||||
float xd = float(((float)x - (float)node_goal.x));
|
||||
float yd = float(((float)y - (float)node_goal.y));
|
||||
|
||||
return xd + yd;
|
||||
}
|
||||
|
||||
bool MapSearchNode::IsGoal(MapSearchNode &node_goal)
|
||||
{
|
||||
if (x == node_goal.x && y == node_goal.y) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MapSearchNode::GetSuccessors(AStarSearch<MapSearchNode> *astarsearch, MapSearchNode *parent_node)
|
||||
{
|
||||
int parent_x = -1;
|
||||
int parent_y = -1;
|
||||
|
||||
if (parent_node) {
|
||||
parent_x = parent_node->x;
|
||||
parent_y = parent_node->y;
|
||||
}
|
||||
|
||||
MapSearchNode new_node;
|
||||
new_node.hum = hum;
|
||||
for (int index = 0; index < GRID9_AGROUD_CELL_NUM; ++index) {
|
||||
int cell_x = x + GRID9_AGROUND_CELL_INDEX[index][0];
|
||||
int cell_y = y + GRID9_AGROUND_CELL_INDEX[index][1];
|
||||
if (parent_x == cell_x && parent_y == cell_y) {
|
||||
continue;
|
||||
}
|
||||
if (hum->room->map_service->GetMap(cell_x, cell_y ) < TILE_STATE_CLOSED) {
|
||||
new_node = MapSearchNode(cell_x, cell_y);
|
||||
new_node.hum = hum;
|
||||
astarsearch->AddSuccessor(new_node);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float MapSearchNode::GetCost(MapSearchNode &successor)
|
||||
{
|
||||
if (x != successor.x && y != successor.y) {
|
||||
return (float)(hum->room->map_service->GetMap(x, y) + 0.41421356);
|
||||
}
|
||||
return (float)hum->room->map_service->GetMap(x, y);
|
||||
}
|
||||
|
||||
bool MapSearchNode::IsSameState(MapSearchNode &rhs)
|
||||
{
|
||||
if (x == rhs.x && y == rhs.y) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
22
server/gameserver/mapsearchnode.h
Normal file
22
server/gameserver/mapsearchnode.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "framework/stlastar.h"
|
||||
|
||||
class Human;
|
||||
class MapSearchNode
|
||||
{
|
||||
public:
|
||||
int x = 0; // the (x,y) positions of the node
|
||||
int y = 0;
|
||||
Human* hum = nullptr;
|
||||
|
||||
MapSearchNode() {}
|
||||
MapSearchNode(int px, int py) {x = px; y = py; }
|
||||
|
||||
float GoalDistanceEstimate(MapSearchNode &node_goal);
|
||||
bool IsGoal(MapSearchNode &node_goal);
|
||||
bool GetSuccessors(AStarSearch<MapSearchNode> *astarsearch, MapSearchNode *parent_node);
|
||||
float GetCost(MapSearchNode &successor);
|
||||
bool IsSameState(MapSearchNode &rhs);
|
||||
|
||||
};
|
@ -3,14 +3,24 @@
|
||||
#include <math.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include "DetourNavMeshBuilder.h"
|
||||
#include "DetourNavMeshQuery.h"
|
||||
#include "DetourCommon.h"
|
||||
#include "DetourNavMesh.h"
|
||||
|
||||
#include "mapservice.h"
|
||||
#include "collider.h"
|
||||
#include "entity.h"
|
||||
#include "human.h"
|
||||
#include "room.h"
|
||||
#include "roomobstacle.h"
|
||||
|
||||
const int MAX_POLYS = 256;
|
||||
static const int INVALID_NAVMESH_POLYREF = 0;
|
||||
|
||||
MapService::MapService()
|
||||
{
|
||||
|
||||
INIT_LIST_HEAD(&findpath_list_);
|
||||
}
|
||||
|
||||
MapService::~MapService()
|
||||
@ -31,6 +41,10 @@ MapService::~MapService()
|
||||
free(map_cells_);
|
||||
map_cells_ = nullptr;
|
||||
}
|
||||
if (navmesh_) {
|
||||
dtFreeNavMesh(navmesh_);
|
||||
navmesh_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void MapService::Init(int width, int height, int cell_width)
|
||||
@ -38,6 +52,9 @@ void MapService::Init(int width, int height, int cell_width)
|
||||
if (width < 1 || height < 1 || cell_width < 1) {
|
||||
abort();
|
||||
}
|
||||
#ifdef FIND_PATH_TEST
|
||||
use_navmesh_ = true;
|
||||
#endif
|
||||
map_width_ = width;
|
||||
map_height_ = height;
|
||||
cell_width_ = cell_width;
|
||||
@ -128,6 +145,10 @@ void MapService::AddCollider(ColliderComponent* collider)
|
||||
node->collider = collider;
|
||||
list_add_tail(&node->entry, head);
|
||||
node->next = top_node;
|
||||
#ifdef DEBUG
|
||||
node->x = x;
|
||||
node->y = y;
|
||||
#endif
|
||||
top_node = node;
|
||||
if (!bot_node) {
|
||||
bot_node = node;
|
||||
@ -225,26 +246,290 @@ void MapService::GetColliders(Room* room,
|
||||
}
|
||||
}
|
||||
|
||||
int MapService::GetMap(int x, int y)
|
||||
{
|
||||
if (!IsValidPos(x, y)) {
|
||||
#ifdef DEBUG1
|
||||
abort();
|
||||
#endif
|
||||
return TILE_STATE_CLOSED;
|
||||
}
|
||||
list_head* head = &map_cells_[x + map_width_ * y];
|
||||
if (list_empty(head)) {
|
||||
return TILE_STATE_OPENED_COST1;
|
||||
} else {
|
||||
CellNode* node = list_first_entry(head, CellNode, entry);
|
||||
switch (node->collider->owner->GetEntityType()) {
|
||||
case ET_Obstacle:
|
||||
{
|
||||
Obstacle* obstacle = (Obstacle*)node->collider->owner;
|
||||
if (!obstacle->IsPermanent()) {
|
||||
return TILE_STATE_OPENED_COST1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
}
|
||||
break;
|
||||
}
|
||||
return TILE_STATE_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
int MapService::GetGridId(float world_x, float world_y)
|
||||
{
|
||||
int grid_id = (int)(world_x/cell_width_) + (int)(world_y/cell_width_) * map_width_;
|
||||
return grid_id;
|
||||
}
|
||||
|
||||
int MapService::FindPathRequest(Human* hum,
|
||||
const a8::Vec2& start_pos,
|
||||
const a8::Vec2& end_pos,
|
||||
int max_step_num)
|
||||
bool MapService::IsValidPos(int x, int y)
|
||||
{
|
||||
return 0;
|
||||
if (x < 0 ||
|
||||
y < 0 ||
|
||||
x >= map_width_ ||
|
||||
y >= map_height_) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FindPathStatus* MapService::QueryFindPathStatus(int query_id)
|
||||
FindPathStatus* MapService::AddFindPathRequest(Human* hum,
|
||||
const a8::Vec2& start_pos,
|
||||
const a8::Vec2& end_pos,
|
||||
int max_step_num)
|
||||
{
|
||||
return nullptr;
|
||||
FindPathStatus* status = new FindPathStatus();
|
||||
status->frameno = hum->room->GetFrameNo();
|
||||
status->hum = hum;
|
||||
status->start_pos = start_pos;
|
||||
status->end_pos = end_pos;
|
||||
status->search_step = 0;
|
||||
status->max_step_num = max_step_num;
|
||||
if (use_navmesh_) {
|
||||
status->navmesh_query = new dtNavMeshQuery();
|
||||
status->navmesh_query->init(navmesh_, 1024);
|
||||
} else {
|
||||
MapSearchNode node_start;
|
||||
MapSearchNode node_goal;
|
||||
node_start.hum = hum;
|
||||
node_goal.hum = hum;
|
||||
|
||||
node_start.x = int(start_pos.x / cell_width_);
|
||||
node_start.y = int(start_pos.y / cell_width_);
|
||||
|
||||
node_goal.x = int(end_pos.x / cell_width_);
|
||||
node_goal.y = int(end_pos.y / cell_width_);
|
||||
|
||||
status->astar_search.SetStartAndGoalStates(node_start, node_goal);
|
||||
}
|
||||
list_add_tail(&status->entry, &findpath_list_);
|
||||
findpath_requests_hash_[hum] = status;
|
||||
return status;
|
||||
}
|
||||
|
||||
void MapService::RemoveFindPathRequest(Human* hum, int query_id)
|
||||
void MapService::DelFindPathRequest(Human* hum, FindPathStatus* status)
|
||||
{
|
||||
|
||||
auto itr = findpath_requests_hash_.find(hum);
|
||||
if (itr == findpath_requests_hash_.end()) {
|
||||
abort();
|
||||
}
|
||||
if (itr->second != status) {
|
||||
abort();
|
||||
}
|
||||
if (use_navmesh_) {
|
||||
if (status->navmesh_query) {
|
||||
dtFreeNavMeshQuery(status->navmesh_query);
|
||||
status->navmesh_query = nullptr;
|
||||
}
|
||||
}
|
||||
list_del_init(&status->entry);
|
||||
A8_SAFE_DELETE(status);
|
||||
findpath_requests_hash_.erase(itr);
|
||||
}
|
||||
|
||||
void MapService::FindPathUpdate(FindPathStatus* find_status)
|
||||
{
|
||||
if (use_navmesh_) {
|
||||
UpdateNavmesh(find_status);
|
||||
} else {
|
||||
UpdateAStar(find_status);
|
||||
}
|
||||
}
|
||||
|
||||
bool MapService::FindOk(FindPathStatus* find_status)
|
||||
{
|
||||
if (use_navmesh_) {
|
||||
return find_status->navmesh_search_state == kNavMesh_SearchOk;
|
||||
} else {
|
||||
return find_status->search_state == AStarSearch<MapSearchNode>::SEARCH_STATE_SUCCEEDED;
|
||||
}
|
||||
}
|
||||
|
||||
bool MapService::FindFailed(FindPathStatus* find_status)
|
||||
{
|
||||
if (use_navmesh_) {
|
||||
return find_status->navmesh_search_state == kNavMesh_SearchFailed;
|
||||
} else {
|
||||
return find_status->search_state == AStarSearch<MapSearchNode>::SEARCH_STATE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
bool MapService::MoveDone(FindPathStatus* find_status)
|
||||
{
|
||||
if (find_status->path_index >= (int)find_status->out_points.size()) {
|
||||
return true;
|
||||
}
|
||||
if (find_status->path_index >= (int)find_status->out_points.size() ||
|
||||
find_status->out_points.empty()) {
|
||||
abort();
|
||||
}
|
||||
if (find_status->path_index + 1 == (int)find_status->out_points.size()) {
|
||||
MovePathPoint* point = &find_status->out_points[find_status->path_index];
|
||||
if (point->pos.ManhattanDistance(find_status->hum->GetPos()) > 2.0f) {
|
||||
return false;
|
||||
}
|
||||
float distance = point->pos.Distance(find_status->hum->GetPos());
|
||||
if (distance < 0.000001f) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MapService::DoMove(FindPathStatus* find_status)
|
||||
{
|
||||
if (find_status->path_index >= (int)find_status->out_points.size() ||
|
||||
find_status->out_points.empty()) {
|
||||
abort();
|
||||
}
|
||||
Human* myself = find_status->hum;
|
||||
MovePathPoint* point = &find_status->out_points[find_status->path_index];
|
||||
myself->move_dir = point->pos - myself->GetPos();
|
||||
myself->move_dir.Normalize();
|
||||
float target_distance = point->pos.Distance(myself->GetPos());
|
||||
float speed = std::min(myself->GetSpeed(), target_distance);
|
||||
a8::Vec2 new_pos = myself->GetPos() + myself->move_dir * speed;
|
||||
myself->SetPos(new_pos);
|
||||
target_distance = point->pos.Distance(myself->GetPos());
|
||||
if (target_distance < 1.0001f) {
|
||||
myself->SetPos(point->pos);
|
||||
++find_status->path_index;
|
||||
#ifdef DEBUG
|
||||
if (myself->IsCollisionInMapService()) {
|
||||
myself->IsCollisionInMapService();
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MapService::UpdateAStar(FindPathStatus* find_status)
|
||||
{
|
||||
do {
|
||||
find_status->search_state = find_status->astar_search.SearchStep();
|
||||
++find_status->search_step;
|
||||
} while (find_status->search_state == AStarSearch<MapSearchNode>::SEARCH_STATE_SEARCHING);
|
||||
|
||||
if (FindOk(find_status)) {
|
||||
MapSearchNode *node = find_status->astar_search.GetSolutionStart();
|
||||
int steps = 0;
|
||||
for( ;; ) {
|
||||
node = find_status->astar_search.GetSolutionNext();
|
||||
if (!node) {
|
||||
break;
|
||||
}
|
||||
|
||||
steps++;
|
||||
MovePathPoint point;
|
||||
point.pos = a8::Vec2(
|
||||
node->x * cell_width_ + cell_width_ / 2,
|
||||
node->y * cell_width_ + cell_width_ / 2
|
||||
);
|
||||
find_status->out_points.push_back(point);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void MapService::UpdateNavmesh(FindPathStatus* find_status)
|
||||
{
|
||||
dtNavMeshQuery* navmesh_query = find_status->navmesh_query;
|
||||
|
||||
float spos[3];
|
||||
spos[0] = find_status->start_pos.x;
|
||||
spos[1] = 0;
|
||||
spos[2] = find_status->start_pos.y;
|
||||
|
||||
float epos[3];
|
||||
epos[0] = find_status->end_pos.x;
|
||||
epos[1] = 0;
|
||||
epos[2] = find_status->end_pos.y;
|
||||
|
||||
dtQueryFilter filter;
|
||||
filter.setIncludeFlags(0xffff);
|
||||
filter.setExcludeFlags(0);
|
||||
|
||||
const float extents[3] = {20.f, 0.f, 20.f};
|
||||
|
||||
dtPolyRef start_ref = INVALID_NAVMESH_POLYREF;
|
||||
dtPolyRef end_ref = INVALID_NAVMESH_POLYREF;
|
||||
|
||||
float start_nearest_pt[3];
|
||||
float end_nearest_pt[3];
|
||||
navmesh_query->findNearestPoly(spos, extents, &filter, &start_ref, start_nearest_pt);
|
||||
navmesh_query->findNearestPoly(epos, extents, &filter, &end_ref, end_nearest_pt);
|
||||
if (!start_ref || !end_ref) {
|
||||
find_status->navmesh_search_state = kNavMesh_SearchFailed;
|
||||
return;
|
||||
}
|
||||
dtPolyRef polys[MAX_POLYS];
|
||||
int npolys;
|
||||
float straight_path[MAX_POLYS * 3];
|
||||
unsigned char straight_path_flags[MAX_POLYS];
|
||||
dtPolyRef straight_path_polys[MAX_POLYS];
|
||||
int nstraight_path;
|
||||
|
||||
navmesh_query->findPath(start_ref,
|
||||
end_ref,
|
||||
start_nearest_pt,
|
||||
end_nearest_pt,
|
||||
&filter,
|
||||
polys,
|
||||
&npolys,
|
||||
MAX_POLYS);
|
||||
nstraight_path = 0;
|
||||
|
||||
if (npolys) {
|
||||
float epos1[3];
|
||||
dtVcopy(epos1, end_nearest_pt);
|
||||
|
||||
if (polys[npolys-1] != end_ref) {
|
||||
navmesh_query->closestPointOnPoly(polys[npolys-1], end_nearest_pt, epos1, 0);
|
||||
}
|
||||
|
||||
navmesh_query->findStraightPath(start_nearest_pt,
|
||||
end_nearest_pt,
|
||||
polys,
|
||||
npolys,
|
||||
straight_path,
|
||||
straight_path_flags,
|
||||
straight_path_polys,
|
||||
&nstraight_path,
|
||||
MAX_POLYS);
|
||||
|
||||
for (int i = 0; i < nstraight_path * 3; ) {
|
||||
MovePathPoint point;
|
||||
point.pos.x = straight_path[i++];
|
||||
point.pos.y = straight_path[i++];
|
||||
straight_path[i++];
|
||||
find_status->out_points.push_back(point);
|
||||
}
|
||||
find_status->navmesh_search_state = kNavMesh_SearchOk;
|
||||
} else {
|
||||
find_status->navmesh_search_state = kNavMesh_SearchFailed;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "mapsearchnode.h"
|
||||
|
||||
class dtNavMesh;
|
||||
class dtNavMeshQuery;
|
||||
class Room;
|
||||
class Human;
|
||||
class ColliderComponent;
|
||||
|
||||
enum TILE_STATE
|
||||
{
|
||||
TILE_STATE_OPENED_COST0 = 0,
|
||||
TILE_STATE_OPENED_COST1 = 1,
|
||||
TILE_STATE_OPENED_COST2 = 2,
|
||||
TILE_STATE_OPENED_COST3 = 3,
|
||||
TILE_STATE_OPENED_COST4 = 4,
|
||||
TILE_STATE_OPENED_COST5 = 5,
|
||||
TILE_STATE_CLOSED = 9
|
||||
};
|
||||
|
||||
enum NavMeshSearchState_e
|
||||
{
|
||||
kNavMesh_Searching = 0,
|
||||
kNavMesh_SearchOk = 1,
|
||||
kNavMesh_SearchFailed = 2,
|
||||
};
|
||||
|
||||
struct CellNode
|
||||
{
|
||||
ColliderComponent* collider;
|
||||
list_head entry;
|
||||
CellNode* next = nullptr;
|
||||
#ifdef DEBUG
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct MovePathPoint
|
||||
@ -22,12 +49,24 @@ struct FindPathStatus
|
||||
Human* hum = nullptr;
|
||||
a8::Vec2 start_pos;
|
||||
a8::Vec2 end_pos;
|
||||
int curr_step = 0;
|
||||
|
||||
int search_step = 0;
|
||||
int max_step_num = 0;
|
||||
std::vector<MovePathPoint> out_ponits;
|
||||
int path_index = 0;
|
||||
std::vector<MovePathPoint> out_points;
|
||||
list_head entry;
|
||||
int search_state = AStarSearch<MapSearchNode>::SEARCH_STATE_SEARCHING;
|
||||
AStarSearch<MapSearchNode> astar_search;
|
||||
|
||||
int navmesh_search_state = kNavMesh_Searching;
|
||||
dtNavMeshQuery* navmesh_query = nullptr;
|
||||
|
||||
FindPathStatus()
|
||||
{
|
||||
INIT_LIST_HEAD(&entry);
|
||||
}
|
||||
};
|
||||
|
||||
class Room;
|
||||
class MapService
|
||||
{
|
||||
public:
|
||||
@ -43,23 +82,36 @@ class MapService
|
||||
float world_x,
|
||||
float world_y,
|
||||
std::set<ColliderComponent*>& colliders);
|
||||
int FindPathRequest(Human* hum,
|
||||
const a8::Vec2& start_pos,
|
||||
const a8::Vec2& end_pos,
|
||||
int max_step_num);
|
||||
FindPathStatus* QueryFindPathStatus(int query_id);
|
||||
void RemoveFindPathRequest(Human* hum, int query_id);
|
||||
int GetMap(int x, int y);
|
||||
|
||||
FindPathStatus* AddFindPathRequest(Human* hum,
|
||||
const a8::Vec2& start_pos,
|
||||
const a8::Vec2& end_pos,
|
||||
int max_step_num);
|
||||
void DelFindPathRequest(Human* hum, FindPathStatus* status);
|
||||
void FindPathUpdate(FindPathStatus* find_status);
|
||||
bool FindOk(FindPathStatus* find_status);
|
||||
bool FindFailed(FindPathStatus* find_status);
|
||||
bool MoveDone(FindPathStatus* find_status);
|
||||
void DoMove(FindPathStatus* find_status);
|
||||
void SetNavMesh(dtNavMesh* navmesh) { navmesh_ = navmesh; };
|
||||
|
||||
private:
|
||||
int GetGridId(float world_x, float world_y);
|
||||
bool IsValidPos(int x, int y);
|
||||
void UpdateAStar(FindPathStatus* find_status);
|
||||
void UpdateNavmesh(FindPathStatus* find_status);
|
||||
|
||||
private:
|
||||
bool use_navmesh_ = false;
|
||||
dtNavMesh* navmesh_ = nullptr;
|
||||
list_head* map_cells_ = nullptr;
|
||||
int map_width_ = 0;
|
||||
int map_height_ = 0;
|
||||
int cell_width_ = 0;
|
||||
int max_grid_id_ = 0;
|
||||
int grid_offset_arr_[9] = {0};
|
||||
list_head findpath_list_;
|
||||
std::map<Human*, FindPathStatus*> findpath_requests_hash_;
|
||||
std::map<ColliderComponent*, CellNode*> node_hash_;
|
||||
|
||||
};
|
||||
|
525
server/gameserver/navmeshbuilder.cc
Normal file
525
server/gameserver/navmeshbuilder.cc
Normal file
@ -0,0 +1,525 @@
|
||||
#include "precompile.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <a8/udplog.h>
|
||||
|
||||
#include "navmeshbuilder.h"
|
||||
#include "mapinstance.h"
|
||||
#include "collider.h"
|
||||
#include "entity.h"
|
||||
#include "metamgr.h"
|
||||
|
||||
#include "navmeshhelper.h"
|
||||
|
||||
#include "framework/cpp/inputgeom.h"
|
||||
|
||||
void NavMeshBuilder::Init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NavMeshBuilder::UnInit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NavMeshBuilder::Build(MapInstance* map_instance)
|
||||
{
|
||||
BuilderParams builder_params;
|
||||
{
|
||||
builder_params.map_instance = map_instance;
|
||||
builder_params.geom = new f8::InputGeom();
|
||||
builder_params.geom->Init(map_instance->map_meta_->i->map_width(),
|
||||
map_instance->map_meta_->i->map_height());
|
||||
builder_params.ctx = new BuildContext();
|
||||
}
|
||||
InitBuilderParams(builder_params);
|
||||
CreateTileCache(builder_params);
|
||||
CreateNavMesh(builder_params);
|
||||
BuildTiles(builder_params);
|
||||
#if 0
|
||||
BuildMapObstalce(builder_params);
|
||||
#endif
|
||||
map_instance->navmesh_ = builder_params.navmesh;
|
||||
Save(builder_params);
|
||||
}
|
||||
|
||||
void NavMeshBuilder::InitBuilderParams(BuilderParams& builder_params)
|
||||
{
|
||||
// Init cache
|
||||
rcCalcGridSize(builder_params.geom->GetMeshBoundsMin(),
|
||||
builder_params.geom->GetMeshBoundsMax(),
|
||||
builder_params.kCellSize,
|
||||
&builder_params.grid_width,
|
||||
&builder_params.grid_height);
|
||||
builder_params.tile_size = (int)builder_params.kTileSize;
|
||||
builder_params.tile_width = (builder_params.grid_width + builder_params.tile_size-1) /
|
||||
builder_params.tile_size;
|
||||
builder_params.tile_height = (builder_params.grid_height + builder_params.tile_size-1) /
|
||||
builder_params.tile_size;
|
||||
}
|
||||
|
||||
void NavMeshBuilder::InitTileCacheParams(BuilderParams& builder_params, dtTileCacheParams& tcparams)
|
||||
{
|
||||
// Tile cache params.
|
||||
memset(&tcparams, 0, sizeof(tcparams));
|
||||
rcVcopy(tcparams.orig, builder_params.geom->GetMeshBoundsMin());
|
||||
tcparams.cs = builder_params.kCellSize;
|
||||
tcparams.ch = builder_params.kCellHeight;
|
||||
tcparams.width = (int)builder_params.kTileSize;
|
||||
tcparams.height = (int)builder_params.kTileSize;
|
||||
tcparams.walkableHeight = builder_params.kAgentHeight;
|
||||
tcparams.walkableRadius = builder_params.kAgentRadius;
|
||||
tcparams.walkableClimb = builder_params.kAgentMaxClimb;
|
||||
tcparams.maxSimplificationError = builder_params.kEdgeMaxError;
|
||||
tcparams.maxTiles = builder_params.tile_width * builder_params.tile_height * EXPECTED_LAYERS_PER_TILE;
|
||||
tcparams.maxObstacles = 128 * 1000;
|
||||
}
|
||||
|
||||
void NavMeshBuilder::InitNavMeshParams(BuilderParams& builder_params, dtNavMeshParams& params)
|
||||
{
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
rcVcopy(params.orig, builder_params.geom->GetMeshBoundsMin());
|
||||
params.tileWidth = builder_params.kTileSize * builder_params.kCellSize;
|
||||
params.tileHeight = builder_params.kTileSize * builder_params.kCellSize;
|
||||
#if 1
|
||||
int tile_bits = rcMin((int)dtIlog2(
|
||||
dtNextPow2(params.tileWidth*params.tileHeight*EXPECTED_LAYERS_PER_TILE)),
|
||||
14);
|
||||
if (tile_bits > 14) {
|
||||
tile_bits = 14;
|
||||
}
|
||||
int poly_bits = 22 - tile_bits;
|
||||
params.maxTiles = 1 << tile_bits;
|
||||
params.maxPolys = 1 << poly_bits;
|
||||
#else
|
||||
params.maxTiles = builder_params.kMaxTiles;
|
||||
params.maxPolys = builder_params.kMaxPolysPerTile;
|
||||
#endif
|
||||
}
|
||||
|
||||
void NavMeshBuilder::BuildTiles(BuilderParams& builder_params)
|
||||
{
|
||||
rcConfig cfg;
|
||||
{
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.cs = builder_params.kCellSize;
|
||||
cfg.ch = builder_params.kCellHeight;
|
||||
cfg.walkableSlopeAngle = builder_params.kAgentMaxSlope;
|
||||
cfg.walkableHeight = (int)ceilf(builder_params.kAgentHeight / cfg.ch);
|
||||
cfg.walkableClimb = (int)floorf(builder_params.kAgentMaxClimb / cfg.ch);
|
||||
cfg.walkableRadius = (int)ceilf(builder_params.kAgentRadius / cfg.cs);
|
||||
cfg.maxEdgeLen = (int)(builder_params.kEdgeMaxLen / builder_params.kCellSize);
|
||||
cfg.maxSimplificationError = builder_params.kEdgeMaxError;
|
||||
cfg.minRegionArea = (int)rcSqr(builder_params.kRegionMinSize); // Note: area = size*size
|
||||
cfg.mergeRegionArea = (int)rcSqr(builder_params.kRegionMergeSize); // Note: area = size*size
|
||||
cfg.maxVertsPerPoly = (int)builder_params.kVertsPerPoly;
|
||||
cfg.tileSize = (int)builder_params.kTileSize;
|
||||
cfg.borderSize = cfg.walkableRadius + 3; // Reserve enough padding.
|
||||
cfg.width = cfg.tileSize + cfg.borderSize*2;
|
||||
cfg.height = cfg.tileSize + cfg.borderSize*2;
|
||||
cfg.detailSampleDist = builder_params.kDetailSampleDist < 0.9f ? 0 : builder_params.kCellSize * builder_params.kDetailSampleDist;
|
||||
cfg.detailSampleMaxError = builder_params.kCellHeight * builder_params.kDetailSampleMaxError;
|
||||
rcVcopy(cfg.bmin, builder_params.geom->GetMeshBoundsMin());
|
||||
rcVcopy(cfg.bmax, builder_params.geom->GetMeshBoundsMax());
|
||||
}
|
||||
|
||||
for (int y = 0; y < builder_params.tile_height; ++y) {
|
||||
for (int x = 0; x < builder_params.tile_width; ++x) {
|
||||
TileCacheData tiles[MAX_LAYERS];
|
||||
memset(tiles, 0, sizeof(tiles));
|
||||
int ntiles = RasterizeTileLayers(builder_params, x, y, cfg, tiles, MAX_LAYERS);
|
||||
|
||||
for (int i = 0; i < ntiles; ++i) {
|
||||
TileCacheData* tile = &tiles[i];
|
||||
dtStatus status = builder_params.tile_cache->addTile(tile->data,
|
||||
tile->dataSize,
|
||||
DT_COMPRESSEDTILE_FREE_DATA,
|
||||
0);
|
||||
if (dtStatusFailed(status)) {
|
||||
abort();
|
||||
dtFree(tile->data);
|
||||
tile->data = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build initial meshes
|
||||
for (int y = 0; y < builder_params.tile_height; ++y) {
|
||||
for (int x = 0; x < builder_params.tile_width; ++x) {
|
||||
builder_params.tile_cache->buildNavMeshTilesAt(x, y, builder_params.navmesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int NavMeshBuilder::RasterizeTileLayers(BuilderParams& builder_params,
|
||||
const int tx,
|
||||
const int ty,
|
||||
const rcConfig& cfg,
|
||||
TileCacheData* tiles,
|
||||
const int maxTiles)
|
||||
{
|
||||
rcAreaModification SAMPLE_AREAMOD_GROUND(SAMPLE_POLYAREA_TYPE_GROUND, SAMPLE_POLYAREA_TYPE_MASK);
|
||||
|
||||
FastLZCompressor comp;
|
||||
RasterizationContext rc;
|
||||
|
||||
const float* verts = builder_params.geom->GetVerts();
|
||||
const int nverts = builder_params.geom->GetVertCount();
|
||||
const rcChunkyTriMesh* chunkyMesh = builder_params.geom->GetChunkyMesh();
|
||||
|
||||
// Tile bounds.
|
||||
const float tcs = cfg.tileSize * cfg.cs;
|
||||
|
||||
rcConfig tcfg;
|
||||
memcpy(&tcfg, &cfg, sizeof(tcfg));
|
||||
|
||||
tcfg.bmin[0] = cfg.bmin[0] + tx*tcs;
|
||||
tcfg.bmin[1] = cfg.bmin[1];
|
||||
tcfg.bmin[2] = cfg.bmin[2] + ty*tcs;
|
||||
tcfg.bmax[0] = cfg.bmin[0] + (tx+1)*tcs;
|
||||
tcfg.bmax[1] = cfg.bmax[1];
|
||||
tcfg.bmax[2] = cfg.bmin[2] + (ty+1)*tcs;
|
||||
tcfg.bmin[0] -= tcfg.borderSize*tcfg.cs;
|
||||
tcfg.bmin[2] -= tcfg.borderSize*tcfg.cs;
|
||||
tcfg.bmax[0] += tcfg.borderSize*tcfg.cs;
|
||||
tcfg.bmax[2] += tcfg.borderSize*tcfg.cs;
|
||||
|
||||
// Allocate voxel heightfield where we rasterize our input data to.
|
||||
rc.solid = rcAllocHeightfield();
|
||||
if (!rc.solid) {
|
||||
return 0;
|
||||
}
|
||||
if (!rcCreateHeightfield(builder_params.ctx,
|
||||
*rc.solid,
|
||||
tcfg.width,
|
||||
tcfg.height,
|
||||
tcfg.bmin,
|
||||
tcfg.bmax,
|
||||
tcfg.cs,
|
||||
tcfg.ch)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate array that can hold triangle flags.
|
||||
// If you have multiple meshes you need to process, allocate
|
||||
// and array which can hold the max number of triangles you need to process.
|
||||
rc.triareas = new unsigned char[chunkyMesh->maxTrisPerChunk];
|
||||
if (!rc.triareas) {
|
||||
builder_params.ctx->log(RC_LOG_ERROR,
|
||||
"buildNavigation: Out of memory 'm_triareas' (%d).",
|
||||
chunkyMesh->maxTrisPerChunk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
float tbmin[2], tbmax[2];
|
||||
tbmin[0] = tcfg.bmin[0];
|
||||
tbmin[1] = tcfg.bmin[2];
|
||||
tbmax[0] = tcfg.bmax[0];
|
||||
tbmax[1] = tcfg.bmax[2];
|
||||
int cid[512];// TODO: Make grow when returning too many items.
|
||||
const int ncid = rcGetChunksOverlappingRect(chunkyMesh, tbmin, tbmax, cid, 512);
|
||||
if (!ncid) {
|
||||
return 0; // empty
|
||||
}
|
||||
|
||||
for (int i = 0; i < ncid; ++i) {
|
||||
const rcChunkyTriMeshNode& node = chunkyMesh->nodes[cid[i]];
|
||||
const int* tris = &chunkyMesh->tris[node.i*3];
|
||||
const int ntris = node.n;
|
||||
|
||||
memset(rc.triareas, 0, ntris*sizeof(unsigned char));
|
||||
rcMarkWalkableTriangles(builder_params.ctx, tcfg.walkableSlopeAngle,
|
||||
verts, nverts, tris, ntris, rc.triareas,
|
||||
SAMPLE_AREAMOD_GROUND);
|
||||
|
||||
if (!rcRasterizeTriangles(builder_params.ctx,
|
||||
verts,
|
||||
nverts,
|
||||
tris,
|
||||
rc.triareas,
|
||||
ntris,
|
||||
*rc.solid,
|
||||
tcfg.walkableClimb)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Once all geometry is rasterized, we do initial pass of filtering to
|
||||
// remove unwanted overhangs caused by the conservative rasterization
|
||||
// as well as filter spans where the character cannot possibly stand.
|
||||
if (builder_params.kFilterLowHangingObstacles) {
|
||||
rcFilterLowHangingWalkableObstacles(builder_params.ctx, tcfg.walkableClimb, *rc.solid);
|
||||
}
|
||||
if (builder_params.kFilterLedgeSpans) {
|
||||
rcFilterLedgeSpans(builder_params.ctx, tcfg.walkableHeight, tcfg.walkableClimb, *rc.solid);
|
||||
}
|
||||
if (builder_params.kFilterWalkableLowHeightSpans) {
|
||||
rcFilterWalkableLowHeightSpans(builder_params.ctx, tcfg.walkableHeight, *rc.solid);
|
||||
}
|
||||
|
||||
rc.chf = rcAllocCompactHeightfield();
|
||||
if (!rc.chf) {
|
||||
builder_params.ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'.");
|
||||
return 0;
|
||||
}
|
||||
if (!rcBuildCompactHeightfield(builder_params.ctx,
|
||||
tcfg.walkableHeight,
|
||||
tcfg.walkableClimb,
|
||||
*rc.solid,
|
||||
*rc.chf)) {
|
||||
builder_params.ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build compact data.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Erode the walkable area by agent radius.
|
||||
if (!rcErodeWalkableArea(builder_params.ctx, tcfg.walkableRadius, *rc.chf)) {
|
||||
builder_params.ctx->log(RC_LOG_ERROR, "buildNavigation: Could not erode.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// (Optional) Mark areas.
|
||||
const ConvexVolume* vols = builder_params.geom->GetConvexVolumes();
|
||||
for (int i = 0; i < builder_params.geom->GetConvexVolumeCount(); ++i) {
|
||||
rcMarkConvexPolyArea(builder_params.ctx, vols[i].verts, vols[i].nverts,
|
||||
vols[i].hmin, vols[i].hmax,
|
||||
vols[i].areaMod, *rc.chf);
|
||||
}
|
||||
#endif
|
||||
|
||||
rc.lset = rcAllocHeightfieldLayerSet();
|
||||
if (!rc.lset) {
|
||||
builder_params.ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'lset'.");
|
||||
return 0;
|
||||
}
|
||||
if (!rcBuildHeightfieldLayers(builder_params.ctx,
|
||||
*rc.chf,
|
||||
tcfg.borderSize,
|
||||
tcfg.walkableHeight,
|
||||
*rc.lset)) {
|
||||
builder_params.ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build heighfield layers.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc.ntiles = 0;
|
||||
for (int i = 0; i < rcMin(rc.lset->nlayers, MAX_LAYERS); ++i) {
|
||||
TileCacheData* tile = &rc.tiles[rc.ntiles++];
|
||||
const rcHeightfieldLayer* layer = &rc.lset->layers[i];
|
||||
|
||||
// Store header
|
||||
dtTileCacheLayerHeader header;
|
||||
header.magic = DT_TILECACHE_MAGIC;
|
||||
header.version = DT_TILECACHE_VERSION;
|
||||
|
||||
// Tile layer location in the navmesh.
|
||||
header.tx = tx;
|
||||
header.ty = ty;
|
||||
header.tlayer = i;
|
||||
dtVcopy(header.bmin, layer->bmin);
|
||||
dtVcopy(header.bmax, layer->bmax);
|
||||
|
||||
// Tile info.
|
||||
header.width = (unsigned char)layer->width;
|
||||
header.height = (unsigned char)layer->height;
|
||||
header.minx = (unsigned char)layer->minx;
|
||||
header.maxx = (unsigned char)layer->maxx;
|
||||
header.miny = (unsigned char)layer->miny;
|
||||
header.maxy = (unsigned char)layer->maxy;
|
||||
header.hmin = (unsigned short)layer->hmin;
|
||||
header.hmax = (unsigned short)layer->hmax;
|
||||
|
||||
dtStatus status = dtBuildTileCacheLayer(&comp,
|
||||
&header,
|
||||
layer->heights,
|
||||
layer->areas,
|
||||
layer->cons,
|
||||
&tile->data,
|
||||
&tile->dataSize);
|
||||
if (dtStatusFailed(status)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer ownsership of tile data from build context to the caller.
|
||||
int n = 0;
|
||||
for (int i = 0; i < rcMin(rc.ntiles, maxTiles); ++i) {
|
||||
tiles[n++] = rc.tiles[i];
|
||||
rc.tiles[i].data = 0;
|
||||
rc.tiles[i].dataSize = 0;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
bool NavMeshBuilder::CreateTileCache(BuilderParams& builder_params)
|
||||
{
|
||||
LinearAllocator* talloc = new LinearAllocator(320000);
|
||||
FastLZCompressor* tcomp = new FastLZCompressor();
|
||||
MeshProcess* tmproc = new MeshProcess();
|
||||
|
||||
dtTileCacheParams tcparams;
|
||||
InitTileCacheParams(builder_params, tcparams);
|
||||
builder_params.tile_cache = dtAllocTileCache();
|
||||
dtStatus status = builder_params.tile_cache->init
|
||||
(&tcparams,
|
||||
talloc,
|
||||
tcomp,
|
||||
tmproc);
|
||||
if (dtStatusFailed(status)) {
|
||||
abort();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NavMeshBuilder::CreateNavMesh(BuilderParams& builder_params)
|
||||
{
|
||||
dtNavMeshParams params;
|
||||
InitNavMeshParams(builder_params, params);
|
||||
|
||||
builder_params.navmesh = dtAllocNavMesh();
|
||||
dtStatus status = builder_params.navmesh->init(¶ms);
|
||||
if (dtStatusFailed(status)) {
|
||||
abort();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NavMeshBuilder::BuildMapObstalce(BuilderParams& builder_params)
|
||||
{
|
||||
dtTileCache* tile_cache = builder_params.tile_cache;
|
||||
for (auto& pair : builder_params.map_instance->uniid_hash_) {
|
||||
for (ColliderComponent* collider : *pair.second->GetColliders()) {
|
||||
switch (collider->type) {
|
||||
case CT_Aabb:
|
||||
{
|
||||
AabbCollider* aabb = (AabbCollider*)collider;
|
||||
|
||||
float bmin[3] = {0};
|
||||
bmin[0] = (aabb->owner->GetPos() + aabb->_min).x;
|
||||
bmin[1] = 0;
|
||||
bmin[2] = (aabb->owner->GetPos() + aabb->_min).y;
|
||||
|
||||
float bmax[3] = {0};
|
||||
bmax[0] = (aabb->owner->GetPos() + aabb->_max).x;
|
||||
bmax[1] = 0;
|
||||
bmax[2] = (aabb->owner->GetPos() + aabb->_max).y;
|
||||
|
||||
dtObstacleRef ref;
|
||||
dtStatus status = tile_cache->addBoxObstacle(bmin,
|
||||
bmax,
|
||||
&ref
|
||||
);
|
||||
if (status != DT_SUCCESS) {
|
||||
abort();
|
||||
}
|
||||
{
|
||||
bool uptodate = false;
|
||||
int update_count = 0;
|
||||
while (!uptodate) {
|
||||
tile_cache->update(0, builder_params.navmesh, &uptodate);
|
||||
++update_count;
|
||||
if (update_count > 10000 * 10) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CT_Circle:
|
||||
{
|
||||
CircleCollider* circle = (CircleCollider*)collider;
|
||||
|
||||
float pos[3] = {0};
|
||||
pos[0] = (circle->owner->GetPos() + circle->pos).x;
|
||||
pos[1] = 0;
|
||||
pos[2] = (circle->owner->GetPos() + circle->pos).y;
|
||||
|
||||
dtObstacleRef ref;
|
||||
dtStatus status = tile_cache->addObstacle(pos,
|
||||
circle->rad,
|
||||
0,
|
||||
&ref
|
||||
);
|
||||
if (status != DT_SUCCESS) {
|
||||
abort();
|
||||
}
|
||||
{
|
||||
bool uptodate = false;
|
||||
int update_count = 0;
|
||||
while (!uptodate) {
|
||||
tile_cache->update(0, builder_params.navmesh, &uptodate);
|
||||
++update_count;
|
||||
if (update_count > 10000 * 10) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}//end for pair
|
||||
}
|
||||
|
||||
void NavMeshBuilder::Save(BuilderParams& builder_params)
|
||||
{
|
||||
FILE* fp = fopen("all_tiles_tilecache.bin", "wb");
|
||||
if (!fp) {
|
||||
abort();
|
||||
}
|
||||
|
||||
static const int TILECACHESET_MAGIC = 'T'<<24 | 'S'<<16 | 'E'<<8 | 'T'; //'TSET';
|
||||
static const int TILECACHESET_VERSION = 1;
|
||||
|
||||
struct TileCacheSetHeader
|
||||
{
|
||||
int magic;
|
||||
int version;
|
||||
int numTiles;
|
||||
dtNavMeshParams meshParams;
|
||||
dtTileCacheParams cacheParams;
|
||||
};
|
||||
|
||||
struct TileCacheTileHeader
|
||||
{
|
||||
dtCompressedTileRef tileRef;
|
||||
int dataSize;
|
||||
};
|
||||
|
||||
TileCacheSetHeader header;
|
||||
header.magic = TILECACHESET_MAGIC;
|
||||
header.version = TILECACHESET_VERSION;
|
||||
header.numTiles = 0;
|
||||
for (int i = 0; i < builder_params.tile_cache->getTileCount(); ++i) {
|
||||
const dtCompressedTile* tile = builder_params.tile_cache->getTile(i);
|
||||
if (!tile || !tile->header || !tile->dataSize) continue;
|
||||
header.numTiles++;
|
||||
}
|
||||
memcpy(&header.cacheParams, builder_params.tile_cache->getParams(), sizeof(dtTileCacheParams));
|
||||
memcpy(&header.meshParams, builder_params.navmesh->getParams(), sizeof(dtNavMeshParams));
|
||||
fwrite(&header, sizeof(TileCacheSetHeader), 1, fp);
|
||||
|
||||
// Store tiles.
|
||||
for (int i = 0; i < builder_params.tile_cache->getTileCount(); ++i) {
|
||||
const dtCompressedTile* tile = builder_params.tile_cache->getTile(i);
|
||||
if (!tile || !tile->header || !tile->dataSize) continue;
|
||||
|
||||
TileCacheTileHeader tileHeader;
|
||||
tileHeader.tileRef = builder_params.tile_cache->getTileRef(tile);
|
||||
tileHeader.dataSize = tile->dataSize;
|
||||
fwrite(&tileHeader, sizeof(tileHeader), 1, fp);
|
||||
|
||||
fwrite(tile->data, tile->dataSize, 1, fp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
a8::UdpLog::Instance()->Debug("Save OK", {});
|
||||
}
|
37
server/gameserver/navmeshbuilder.h
Normal file
37
server/gameserver/navmeshbuilder.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
struct rcConfig;
|
||||
struct dtTileCacheParams;
|
||||
struct dtNavMeshParams;
|
||||
struct BuilderParams;
|
||||
struct TileCacheData;
|
||||
class dtNavMesh;
|
||||
class MapInstance;
|
||||
class NavMeshBuilder : public a8::Singleton<NavMeshBuilder>
|
||||
{
|
||||
private:
|
||||
NavMeshBuilder() {};
|
||||
friend class a8::Singleton<NavMeshBuilder>;
|
||||
|
||||
public:
|
||||
void Init();
|
||||
void UnInit();
|
||||
|
||||
void Build(MapInstance* map_instance);
|
||||
|
||||
private:
|
||||
void InitBuilderParams(BuilderParams& builder_params);
|
||||
void InitTileCacheParams(BuilderParams& builder_params, dtTileCacheParams& tcparams);
|
||||
void InitNavMeshParams(BuilderParams& builder_params, dtNavMeshParams& params);
|
||||
void BuildTiles(BuilderParams& builder_params);
|
||||
int RasterizeTileLayers(BuilderParams& builder_params,
|
||||
const int tx,
|
||||
const int ty,
|
||||
const rcConfig& cfg,
|
||||
TileCacheData* tiles,
|
||||
const int maxTiles);
|
||||
bool CreateTileCache(BuilderParams& builder_params);
|
||||
bool CreateNavMesh(BuilderParams& builder_params);
|
||||
void BuildMapObstalce(BuilderParams& builder_params);
|
||||
void Save(BuilderParams& builder_params);
|
||||
};
|
145
server/gameserver/navmeshhelper.cc
Normal file
145
server/gameserver/navmeshhelper.cc
Normal file
@ -0,0 +1,145 @@
|
||||
#include "precompile.h"
|
||||
|
||||
#include <a8/udplog.h>
|
||||
|
||||
#include "mapinstance.h"
|
||||
#include "metamgr.h"
|
||||
#include "navmeshhelper.h"
|
||||
#include "collider.h"
|
||||
#include "entity.h"
|
||||
|
||||
void BuildContext::doResetLog()
|
||||
{
|
||||
a8::UdpLog::Instance()->Debug("doResetLog", {});
|
||||
}
|
||||
|
||||
void BuildContext::doLog(const rcLogCategory category, const char* msg, const int len)
|
||||
{
|
||||
a8::UdpLog::Instance()->Debug("doLog category:%d msg:%s",
|
||||
{
|
||||
category,
|
||||
msg
|
||||
});
|
||||
}
|
||||
|
||||
void BuildContext::doResetTimers()
|
||||
{
|
||||
a8::UdpLog::Instance()->Debug("doResetTimers", {});
|
||||
}
|
||||
|
||||
void BuildContext::doStartTimer(const rcTimerLabel label)
|
||||
{
|
||||
a8::UdpLog::Instance()->Debug("doStartTimer lable:%d", {label});
|
||||
}
|
||||
|
||||
void BuildContext::doStopTimer(const rcTimerLabel label)
|
||||
{
|
||||
a8::UdpLog::Instance()->Debug("doStopTimer lable:%d", {label});
|
||||
}
|
||||
|
||||
int BuildContext::doGetAccumulatedTime(const rcTimerLabel label) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NavMeshHelper::OutputObjFile(MapInstance* map_instance)
|
||||
{
|
||||
std::vector<a8::Vec2> vertexs;
|
||||
std::vector<std::tuple<int, int, int>> faces;
|
||||
vertexs.reserve(10000);
|
||||
for (auto& pair : map_instance->uniid_hash_) {
|
||||
for (ColliderComponent* collider : *pair.second->GetColliders()) {
|
||||
if (collider->type == CT_Aabb) {
|
||||
AabbCollider* aabb_box = (AabbCollider*)collider;
|
||||
{
|
||||
a8::Vec2 vert = collider->owner->GetPos() + aabb_box->_min;
|
||||
vert.x = vert.x - map_instance->map_meta_->i->map_width() / 2.0f;
|
||||
vert.y = vert.y - map_instance->map_meta_->i->map_height() / 2.0f;
|
||||
|
||||
vertexs.push_back(vert);
|
||||
vert.y += aabb_box->_max.y - aabb_box->_min.y;
|
||||
vertexs.push_back(vert);
|
||||
vert.x += aabb_box->_max.x - aabb_box->_min.x;
|
||||
vertexs.push_back(vert);
|
||||
vert.y -= aabb_box->_max.y - aabb_box->_min.y;
|
||||
vertexs.push_back(vert);
|
||||
}
|
||||
//0 1 2
|
||||
faces.push_back
|
||||
(std::make_tuple
|
||||
(
|
||||
vertexs.size() - 4 + 1,
|
||||
vertexs.size() - 3 + 1,
|
||||
vertexs.size() - 2 + 1
|
||||
));
|
||||
//0 2 3
|
||||
faces.push_back
|
||||
(std::make_tuple
|
||||
(
|
||||
vertexs.size() - 4 + 1,
|
||||
vertexs.size() - 2 + 1,
|
||||
vertexs.size() - 1 + 1
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
std::string filename = a8::Format("%s.obj", {map_instance->map_tpl_name_});
|
||||
FILE* fp = fopen(filename.c_str(), "wb");
|
||||
#if 1
|
||||
{
|
||||
vertexs.clear();
|
||||
faces.clear();
|
||||
{
|
||||
a8::Vec2 vert;
|
||||
//vert.x = 0 - map_instance->map_meta_->i->map_width() / 2.0f;
|
||||
//vert.y = 0 - map_instance->map_meta_->i->map_height() / 2.0f;
|
||||
|
||||
vertexs.push_back(vert);
|
||||
vert.y += map_instance->map_meta_->i->map_height();
|
||||
vertexs.push_back(vert);
|
||||
vert.x += map_instance->map_meta_->i->map_width();
|
||||
vertexs.push_back(vert);
|
||||
vert.y -= map_instance->map_meta_->i->map_height();
|
||||
vertexs.push_back(vert);
|
||||
|
||||
//0 1 2
|
||||
faces.push_back
|
||||
(std::make_tuple
|
||||
(
|
||||
vertexs.size() - 4 + 1,
|
||||
vertexs.size() - 3 + 1,
|
||||
vertexs.size() - 2 + 1
|
||||
));
|
||||
//0 2 3
|
||||
faces.push_back
|
||||
(std::make_tuple
|
||||
(
|
||||
vertexs.size() - 4 + 1,
|
||||
vertexs.size() - 2 + 1,
|
||||
vertexs.size() - 1 + 1
|
||||
));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (auto& vert : vertexs) {
|
||||
std::string data = a8::Format("v %f %f %f\r\n",
|
||||
{
|
||||
vert.x,
|
||||
0,
|
||||
vert.y,
|
||||
});
|
||||
fwrite(data.data(), 1, data.size(), fp);
|
||||
}
|
||||
for (auto& tuple : faces) {
|
||||
std::string data = a8::Format("f %d %d %d\r\n",
|
||||
{
|
||||
std::get<0>(tuple),
|
||||
std::get<1>(tuple),
|
||||
std::get<2>(tuple)
|
||||
});
|
||||
fwrite(data.data(), 1, data.size(), fp);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
257
server/gameserver/navmeshhelper.h
Normal file
257
server/gameserver/navmeshhelper.h
Normal file
@ -0,0 +1,257 @@
|
||||
#pragma once
|
||||
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "DetourTileCache.h"
|
||||
#include "DetourTileCacheBuilder.h"
|
||||
|
||||
#include "DetourNavMeshBuilder.h"
|
||||
#include "DetourNavMeshQuery.h"
|
||||
#include "DetourCommon.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "fastlz.h"
|
||||
|
||||
static const int EXPECTED_LAYERS_PER_TILE = 4;
|
||||
|
||||
static const int MAX_LAYERS = 32;
|
||||
|
||||
/// Mask of the ceil part of the area id (3 lower bits)
|
||||
/// the 0 value (RC_NULL_AREA) is left unused
|
||||
static const unsigned char SAMPLE_POLYAREA_TYPE_MASK = 0x07;
|
||||
/// Value for the kind of ceil "ground"
|
||||
static const unsigned char SAMPLE_POLYAREA_TYPE_GROUND = 0x1;
|
||||
/// Value for the kind of ceil "water"
|
||||
static const unsigned char SAMPLE_POLYAREA_TYPE_WATER = 0x2;
|
||||
/// Value for the kind of ceil "road"
|
||||
static const unsigned char SAMPLE_POLYAREA_TYPE_ROAD = 0x3;
|
||||
/// Value for the kind of ceil "grass"
|
||||
static const unsigned char SAMPLE_POLYAREA_TYPE_GRASS = 0x4;
|
||||
/// Flag for door area. Can be combined with area types and jump flag.
|
||||
static const unsigned char SAMPLE_POLYAREA_FLAG_DOOR = 0x08;
|
||||
/// Flag for jump area. Can be combined with area types and door flag.
|
||||
static const unsigned char SAMPLE_POLYAREA_FLAG_JUMP = 0x10;
|
||||
|
||||
struct TileCacheData
|
||||
{
|
||||
unsigned char* data;
|
||||
int dataSize;
|
||||
};
|
||||
|
||||
struct RasterizationContext
|
||||
{
|
||||
RasterizationContext() :
|
||||
solid(0),
|
||||
triareas(0),
|
||||
lset(0),
|
||||
chf(0),
|
||||
ntiles(0)
|
||||
{
|
||||
memset(tiles, 0, sizeof(TileCacheData)*MAX_LAYERS);
|
||||
}
|
||||
|
||||
~RasterizationContext()
|
||||
{
|
||||
rcFreeHeightField(solid);
|
||||
delete [] triareas;
|
||||
rcFreeHeightfieldLayerSet(lset);
|
||||
rcFreeCompactHeightfield(chf);
|
||||
for (int i = 0; i < MAX_LAYERS; ++i)
|
||||
{
|
||||
dtFree(tiles[i].data);
|
||||
tiles[i].data = 0;
|
||||
}
|
||||
}
|
||||
|
||||
rcHeightfield* solid;
|
||||
unsigned char* triareas;
|
||||
rcHeightfieldLayerSet* lset;
|
||||
rcCompactHeightfield* chf;
|
||||
TileCacheData tiles[MAX_LAYERS];
|
||||
int ntiles;
|
||||
};
|
||||
|
||||
struct LinearAllocator : public dtTileCacheAlloc
|
||||
{
|
||||
unsigned char* buffer;
|
||||
size_t capacity;
|
||||
size_t top;
|
||||
size_t high;
|
||||
|
||||
LinearAllocator(const size_t cap) : buffer(0), capacity(0), top(0), high(0)
|
||||
{
|
||||
resize(cap);
|
||||
}
|
||||
|
||||
~LinearAllocator()
|
||||
{
|
||||
dtFree(buffer);
|
||||
}
|
||||
|
||||
void resize(const size_t cap)
|
||||
{
|
||||
if (buffer) dtFree(buffer);
|
||||
buffer = (unsigned char*)dtAlloc(cap, DT_ALLOC_PERM);
|
||||
capacity = cap;
|
||||
}
|
||||
|
||||
virtual void reset()
|
||||
{
|
||||
high = dtMax(high, top);
|
||||
top = 0;
|
||||
}
|
||||
|
||||
virtual void* alloc(const size_t size)
|
||||
{
|
||||
if (!buffer)
|
||||
return 0;
|
||||
if (top+size > capacity)
|
||||
return 0;
|
||||
unsigned char* mem = &buffer[top];
|
||||
top += size;
|
||||
return mem;
|
||||
}
|
||||
|
||||
virtual void free(void* /*ptr*/)
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
};
|
||||
|
||||
struct FastLZCompressor : public dtTileCacheCompressor
|
||||
{
|
||||
virtual int maxCompressedSize(const int bufferSize)
|
||||
{
|
||||
return (int)(bufferSize* 1.05f);
|
||||
}
|
||||
|
||||
virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
|
||||
unsigned char* compressed, const int /*maxCompressedSize*/, int* compressedSize)
|
||||
{
|
||||
*compressedSize = fastlz_compress((const void *const)buffer, bufferSize, compressed);
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
virtual dtStatus decompress(const unsigned char* compressed, const int compressedSize,
|
||||
unsigned char* buffer, const int maxBufferSize, int* bufferSize)
|
||||
{
|
||||
*bufferSize = fastlz_decompress(compressed, compressedSize, buffer, maxBufferSize);
|
||||
return *bufferSize < 0 ? DT_FAILURE : DT_SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
struct MeshProcess : public dtTileCacheMeshProcess
|
||||
{
|
||||
inline MeshProcess()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void process(struct dtNavMeshCreateParams* params,
|
||||
unsigned char* polyAreas,
|
||||
unsigned short* polyFlags)
|
||||
{
|
||||
#if 0
|
||||
// Update poly flags from areas.
|
||||
for (int i = 0; i < params->polyCount; ++i)
|
||||
{
|
||||
polyFlags[i] = sampleAreaToFlags(polyAreas[i]);
|
||||
}
|
||||
|
||||
// Pass in off-mesh connections.
|
||||
if (m_geom)
|
||||
{
|
||||
params->offMeshConVerts = m_geom->getOffMeshConnectionVerts();
|
||||
params->offMeshConRad = m_geom->getOffMeshConnectionRads();
|
||||
params->offMeshConDir = m_geom->getOffMeshConnectionDirs();
|
||||
params->offMeshConAreas = m_geom->getOffMeshConnectionAreas();
|
||||
params->offMeshConFlags = m_geom->getOffMeshConnectionFlags();
|
||||
params->offMeshConUserID = m_geom->getOffMeshConnectionId();
|
||||
params->offMeshConCount = m_geom->getOffMeshConnectionCount();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class BuildContext : public rcContext
|
||||
{
|
||||
protected:
|
||||
virtual void doResetLog() override;
|
||||
virtual void doLog(const rcLogCategory category, const char* msg, const int len) override;
|
||||
virtual void doResetTimers() override;
|
||||
virtual void doStartTimer(const rcTimerLabel label) override;
|
||||
virtual void doStopTimer(const rcTimerLabel label) override;
|
||||
virtual int doGetAccumulatedTime(const rcTimerLabel label) const override;
|
||||
};
|
||||
|
||||
class MapInstance;
|
||||
class NavMeshHelper
|
||||
{
|
||||
public:
|
||||
static void OutputObjFile(MapInstance* map_instance);
|
||||
};
|
||||
|
||||
namespace f8
|
||||
{
|
||||
class InputGeom;
|
||||
}
|
||||
|
||||
struct BuilderParams
|
||||
{
|
||||
const float kCellSize = 1; //体素大小
|
||||
const float kCellHeight = 1; //体素高度
|
||||
const float kAgentMaxSlope = 90; //角色可走的最大坡度
|
||||
const float kAgentHeight = 1; //角色高
|
||||
const float kAgentMaxClimb = 1; //角色能爬的最大高度
|
||||
const float kAgentRadius = 20; //角色半径
|
||||
|
||||
const float kEdgeMaxLen = 6; //简化列表中相邻两点间的距离
|
||||
const float kEdgeMaxError = 6; //从简化边到实边的最大距离
|
||||
|
||||
const float kRegionMinSize = 6; //最小区域的大小, 网格面积小于该值的地方,将不生成导航网格
|
||||
/*
|
||||
限制: >= 0
|
||||
当一些区域的尺寸小于此参数时,如果可以,会和较大区域合并.
|
||||
取值以体素为单位
|
||||
该参数可以帮助减少一些尺寸较小的区域
|
||||
由于区域生成算法的固有错误,会产生一些不期望的小区域.这在三角路径区域划分时,更为明显.
|
||||
如果小区域无法被合并到临近的区域,则这些小区域会被保留,不会改变.比如合并后会导致非简单多边形等原因导致无法合并.
|
||||
[该参数取值过小,产生了一些细长三角形]
|
||||
[该参数取值足够大,一些细长三角形被合并,网格有更少的细长三角形]
|
||||
*/
|
||||
#if 1
|
||||
const float kRegionMergeSize = 400;
|
||||
#else
|
||||
const float kRegionMergeSize = 20;
|
||||
#endif
|
||||
|
||||
/*
|
||||
限制: >= 3 在体素到多边形转换过程中生成的多边形中每个多边形的最大顶点数
|
||||
较大的取值会导致较高的处理耗时,但是会在寻路网格上生成较好的多边形.
|
||||
一般取6左右的值即可,更高的取值,收效递减*/
|
||||
const int kVertsPerPoly = 6;
|
||||
|
||||
const int kTileSize = 64; //单个瓦片大小
|
||||
|
||||
const float kDetailSampleDist = 6;
|
||||
const float kDetailSampleMaxError = 1;
|
||||
|
||||
#if 0
|
||||
const int kMaxTiles = 0;
|
||||
const int kMaxPolysPerTile = 0;
|
||||
#endif
|
||||
|
||||
const bool kFilterLowHangingObstacles = false;
|
||||
const bool kFilterLedgeSpans = false;
|
||||
const bool kFilterWalkableLowHeightSpans = false;
|
||||
|
||||
int grid_width = 0;
|
||||
int grid_height = 0;
|
||||
int tile_size = 0;
|
||||
int tile_width = 0;
|
||||
int tile_height = 0;
|
||||
|
||||
MapInstance* map_instance = nullptr;
|
||||
f8::InputGeom* geom = nullptr;
|
||||
dtNavMesh* navmesh = nullptr;
|
||||
rcContext* ctx = nullptr;
|
||||
dtTileCache* tile_cache = nullptr;
|
||||
};
|
@ -22,3 +22,6 @@ namespace google
|
||||
#include "framework/cpp/types.h"
|
||||
#include "framework/cpp/protoutils.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define FIND_PATH_TEST 1
|
||||
#endif
|
||||
|
@ -2941,12 +2941,24 @@ void Room::ZombieModeStart()
|
||||
#endif
|
||||
std::vector<Human*> human_list;
|
||||
for (auto& pair : human_hash_) {
|
||||
#ifdef FIND_PATH_TEST
|
||||
if (pair.second->IsAndroid()) {
|
||||
human_list.push_back(pair.second);
|
||||
}
|
||||
#else
|
||||
human_list.push_back(pair.second);
|
||||
#endif
|
||||
if (RoomMgr::Instance()->IsGM(pair.second->account_id)) {
|
||||
debug_trace = true;
|
||||
}
|
||||
}
|
||||
std::random_shuffle(human_list.begin(), human_list.end());
|
||||
#ifdef FIND_PATH_TEST
|
||||
for (size_t i = 0; i < 1; ++i) {
|
||||
Human* hum = human_list[i];
|
||||
hum->ChangeToRace(kZombieRace, 1);
|
||||
}
|
||||
#else
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
Human* hum = human_list[i];
|
||||
hum->ChangeToRace(kZombieRace, 1);
|
||||
@ -2976,6 +2988,7 @@ void Room::ZombieModeStart()
|
||||
room->battle_report_timer_ = nullptr;
|
||||
}
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
Human* Room::GetOneCanEnableAndroid()
|
||||
@ -3324,7 +3337,11 @@ int Room::GetOnlinePlayerNum()
|
||||
size_t Room::GetRoomMaxPlayerNum()
|
||||
{
|
||||
if (room_mode_ == kZombieMode) {
|
||||
#ifdef FIND_PATH_TEST
|
||||
return 2;
|
||||
#else
|
||||
return MetaMgr::Instance()->zbmode_player_num;
|
||||
#endif
|
||||
} else {
|
||||
if (IsMiniRoom()) {
|
||||
return MINI_ROOM_MAX_PLAYER_NUM;
|
||||
|
@ -120,6 +120,16 @@ void ZombieModeAI::UpdateAI()
|
||||
UpdatePursuit();
|
||||
}
|
||||
break;
|
||||
case ZSE_FindPath:
|
||||
{
|
||||
UpdateFindPath();
|
||||
}
|
||||
break;
|
||||
case ZSE_FindPathMoving:
|
||||
{
|
||||
UpdateFindPathMoving();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
abort();
|
||||
@ -198,14 +208,14 @@ void ZombieModeAI::UpdateAttack()
|
||||
//站桩
|
||||
ChangeToState(ZSE_Thinking);
|
||||
} else {
|
||||
#if 1
|
||||
#if 0
|
||||
ChangeToState(ZSE_Pursuit);
|
||||
#else
|
||||
if (distance < node_->ai_meta->i->pursuit_radius()) {
|
||||
//追击
|
||||
ChangeToState(ZSE_Pursuit);
|
||||
} else {
|
||||
ChangeToState(ZSE_Thinking);
|
||||
ChangeToState(ZSE_FindPath);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -269,6 +279,45 @@ void ZombieModeAI::UpdatePursuit()
|
||||
}
|
||||
}
|
||||
|
||||
void ZombieModeAI::UpdateFindPath()
|
||||
{
|
||||
Human* myself = (Human*)owner;
|
||||
FindPathStatus* find_status = myself->GetFindPathStatus();
|
||||
if (!find_status) {
|
||||
ChangeToState(ZSE_Thinking);
|
||||
} else {
|
||||
if (myself->room->map_service->FindOk(find_status)) {
|
||||
ChangeToState(ZSE_FindPathMoving);
|
||||
} else {
|
||||
myself->room->map_service->FindPathUpdate(find_status);
|
||||
if (myself->room->map_service->FindFailed(find_status)) {
|
||||
#ifdef DEBUG
|
||||
a8::XPrintf("寻路失败 step:%d\n", {find_status->search_step});
|
||||
#endif
|
||||
myself->ClearFindPathStatus();
|
||||
ChangeToState(ZSE_RandomWalk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ZombieModeAI::UpdateFindPathMoving()
|
||||
{
|
||||
Human* myself = (Human*)owner;
|
||||
FindPathStatus* find_status = myself->GetFindPathStatus();
|
||||
if (!find_status) {
|
||||
ChangeToState(ZSE_Thinking);
|
||||
} else {
|
||||
if (myself->room->map_service->MoveDone(find_status)) {
|
||||
myself->ClearFindPathStatus();
|
||||
ChangeToState(ZSE_Thinking);
|
||||
} else {
|
||||
node_->moving = false;
|
||||
myself->room->map_service->DoMove(find_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ZombieModeAI::DoMove()
|
||||
{
|
||||
Human* hum = (Human*)owner;
|
||||
@ -361,10 +410,35 @@ void ZombieModeAI::ChangeToState(ZombieState_e to_state)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZSE_FindPath:
|
||||
{
|
||||
hum->ClearFindPathStatus();
|
||||
FindPathStatus* status = hum->room->map_service->AddFindPathRequest
|
||||
(
|
||||
hum,
|
||||
hum->GetPos(),
|
||||
node_->target->GetPos(),
|
||||
1000
|
||||
);
|
||||
if (status) {
|
||||
hum->SetFindPathStatus(status);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZSE_FindPathMoving:
|
||||
{
|
||||
#ifdef DEBUG
|
||||
a8::XPrintf("寻路成功\n", {});
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
node_->main_state = to_state;
|
||||
node_->frameno = hum->room->GetFrameNo();
|
||||
node_->exec_frame_num = 0;
|
||||
#ifdef DEBUG1
|
||||
a8::XPrintf("changetostate %d\n", {to_state});
|
||||
#endif
|
||||
}
|
||||
|
||||
Human* ZombieModeAI::GetTarget()
|
||||
|
@ -8,7 +8,9 @@ enum ZombieState_e
|
||||
ZSE_Thinking = 1,
|
||||
ZSE_Attack = 2,
|
||||
ZSE_RandomWalk = 3,
|
||||
ZSE_Pursuit = 4
|
||||
ZSE_Pursuit = 4,
|
||||
ZSE_FindPath = 5,
|
||||
ZSE_FindPathMoving = 6
|
||||
};
|
||||
|
||||
class Human;
|
||||
@ -30,6 +32,8 @@ private:
|
||||
void UpdateAttack();
|
||||
void UpdateRandomWalk();
|
||||
void UpdatePursuit();
|
||||
void UpdateFindPath();
|
||||
void UpdateFindPathMoving();
|
||||
void DoMove();
|
||||
void ChangeToState(ZombieState_e to_state);
|
||||
void DoShot();
|
||||
|
@ -12,7 +12,7 @@ package cs;
|
||||
|
||||
网络包格式:msghead + msgbody
|
||||
msghead: packagelen + msgid + seqid + magiccode + reserved = 2 + 2 + 4 + 2 + 2 = 12字节
|
||||
msgbody: protobuf数据
|
||||
n msgbody: protobuf数据
|
||||
msghead说明
|
||||
packagelen(unsigned short): 双字节网络包长度,
|
||||
msgid(unsigned short): 双字节消息id
|
||||
|
2
third_party/framework
vendored
2
third_party/framework
vendored
@ -1 +1 @@
|
||||
Subproject commit 98f1d402cba91a1983dac2eb00de731657e133a5
|
||||
Subproject commit a9481a26152bd030516b2e546702982f445e2494
|
Loading…
x
Reference in New Issue
Block a user