Refactoring A* algorithm to use a set structure instead of an array.
This commit is contained in:
parent
7abb714144
commit
5f1b07d8b5
@ -303,7 +303,6 @@
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\..\Downloads\olcPixelGameEngine (1).h" />
|
||||
<ClInclude Include="Ability.h" />
|
||||
<ClInclude Include="Animation.h" />
|
||||
<ClInclude Include="Attributable.h" />
|
||||
|
||||
@ -405,9 +405,6 @@
|
||||
<ClInclude Include="olcPGEX_SplashScreen.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\Downloads\olcPixelGameEngine (1).h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TitleScreen.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@ -234,6 +234,18 @@ void Monster::Draw(){
|
||||
game->view.DrawDecal(GetPos()-vf2d{3,3}*shadowScale/2+vf2d{0,6*GetSizeMult()},GFX["circle.png"].Decal(),shadowScale,BLACK);
|
||||
}
|
||||
game->view.DrawPartialRotatedDecal(GetPos()-vf2d{0,GetZ()},GetFrame().GetSourceImage()->Decal(),0,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,vf2d(GetSizeMult()*(GetFacingDirection()==RIGHT?-1:1),GetSizeMult()),GetBuffs(BuffType::SLOWDOWN).size()>0?Pixel{uint8_t(255*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration))),uint8_t(255*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration))),uint8_t(128+127*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration)))}:WHITE);
|
||||
|
||||
for(size_t counter=0;Pathfinding::sPoint2D&point:path.points){
|
||||
Pixel col=CYAN;
|
||||
if(counter<pathIndex){
|
||||
col=RED;
|
||||
}
|
||||
if(counter>pathIndex+1){
|
||||
col=YELLOW;
|
||||
}
|
||||
game->view.FillRectDecal(point.pos*float(game->GetCurrentMapData().tilewidth),{3,3},col);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
void Monster::DrawReflection(float drawRatioX,float multiplierX){
|
||||
game->SetDecalMode(DecalMode::ADDITIVE);
|
||||
@ -391,18 +403,18 @@ void Monster::AddBuff(BuffType type,float duration,float intensity){
|
||||
|
||||
void Monster::StartPathfinding(float pathingTime){
|
||||
SetState(State::PATH_AROUND);
|
||||
path=game->pathfinder.Solve_AStar(pos,target,12,OnUpperLevel());
|
||||
if(path.size()>0){
|
||||
pathIndex=0;
|
||||
path=game->pathfinder.Solve_WalkPath(pos,target,12,OnUpperLevel());
|
||||
if(path.points.size()>0){
|
||||
pathIndex=0.f;
|
||||
//We gives this mob 5 seconds to figure out a path to the target.
|
||||
targetAcquireTimer=pathingTime;
|
||||
}
|
||||
}
|
||||
|
||||
void Monster::PathAroundBehavior(float fElapsedTime){
|
||||
if(path.size()>0){
|
||||
if(path.points.size()>0){
|
||||
//Move towards the new path.
|
||||
geom2d::line moveTowardsLine=geom2d::line(pos,path[pathIndex]*float(game->GetCurrentMapData().tilewidth));
|
||||
geom2d::line moveTowardsLine=geom2d::line(pos,path.GetSplinePoint(pathIndex).pos*float(game->GetCurrentMapData().tilewidth));
|
||||
if(moveTowardsLine.length()>2){
|
||||
SetPos(pos+moveTowardsLine.vector().norm()*100*fElapsedTime*GetMoveSpdMult());
|
||||
if(moveTowardsLine.vector().x>0){
|
||||
@ -411,11 +423,11 @@ void Monster::PathAroundBehavior(float fElapsedTime){
|
||||
facingDirection=LEFT;
|
||||
}
|
||||
}else{
|
||||
if(size_t(pathIndex+1)>=path.size()){
|
||||
if(size_t(pathIndex+1)>=path.points.size()){
|
||||
//We have reached the end of the path!
|
||||
targetAcquireTimer=0;
|
||||
}else{
|
||||
pathIndex++;
|
||||
pathIndex+=0.2f;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -45,6 +45,7 @@ All rights reserved.
|
||||
#include "Attributable.h"
|
||||
#include "Item.h"
|
||||
#include "safemap.h"
|
||||
#include "Pathfinding.h"
|
||||
|
||||
INCLUDE_ITEM_DATA
|
||||
|
||||
@ -197,8 +198,8 @@ private:
|
||||
bool canMove=true; //Set to false when stuck due to collisions.
|
||||
bool upperLevel=false;
|
||||
vf2d pathTarget={};
|
||||
std::vector<vf2d>path;
|
||||
int pathIndex=0;
|
||||
Pathfinding::sSpline path;
|
||||
float pathIndex=0;
|
||||
float lastHitTimer=0;
|
||||
std::shared_ptr<DamageNumber>damageNumberPtr;
|
||||
int phase=0;
|
||||
|
||||
@ -42,55 +42,62 @@ All rights reserved.
|
||||
INCLUDE_game
|
||||
|
||||
void Pathfinding::Initialize(){
|
||||
if(nodes!=nullptr){
|
||||
delete[] nodes;
|
||||
}
|
||||
nodes = NEW sNode[game->GetCurrentMapData().width * game->GetCurrentMapData().height];
|
||||
for (int x = 0; x < game->GetCurrentMapData().width; x++)
|
||||
for (int y = 0; y < game->GetCurrentMapData().height; y++)
|
||||
{
|
||||
nodes[y * game->GetCurrentMapData().width + x].x = x; // ...because we give each node its own coordinates
|
||||
nodes[y * game->GetCurrentMapData().width + x].y = y;
|
||||
geom2d::rect<int>tile=game->GetTileCollision(game->GetCurrentLevel(),{float(x*game->GetCurrentMapData().tilewidth),float(y*game->GetCurrentMapData().tilewidth)});
|
||||
nodes[y * game->GetCurrentMapData().width + x].bObstacle = tile.pos!=game->NO_COLLISION.pos||tile.size!=game->NO_COLLISION.size;
|
||||
tile=game->GetTileCollision(game->GetCurrentLevel(),{float(x*game->GetCurrentMapData().tilewidth),float(y*game->GetCurrentMapData().tilewidth)},true);
|
||||
nodes[y * game->GetCurrentMapData().width + x].bObstacleUpper = tile.pos!=game->NO_COLLISION.pos||tile.size!=game->NO_COLLISION.size;
|
||||
nodes[y * game->GetCurrentMapData().width + x].parent = nullptr;
|
||||
nodes[y * game->GetCurrentMapData().width + x].bVisited = false;
|
||||
}
|
||||
nodes.clear();
|
||||
sNode*lastNodeAdded=nullptr;
|
||||
for (int x = 0; x < game->GetCurrentMapData().width; x++)
|
||||
for (int y = 0; y < game->GetCurrentMapData().height; y++)
|
||||
{
|
||||
nodes.insert({x,y});
|
||||
sNode node=*nodes.find({x,y});
|
||||
node.x = x; // ...because we give each node its own coordinates
|
||||
node.y = y; // ...because we give each node its own coordinates
|
||||
geom2d::rect<int>tile=game->GetTileCollision(game->GetCurrentLevel(),{float(x*game->GetCurrentMapData().tilewidth),float(y*game->GetCurrentMapData().tilewidth)});
|
||||
node.bObstacle = tile.pos!=game->NO_COLLISION.pos||tile.size!=game->NO_COLLISION.size;
|
||||
tile=game->GetTileCollision(game->GetCurrentLevel(),{float(x*game->GetCurrentMapData().tilewidth),float(y*game->GetCurrentMapData().tilewidth)},true);
|
||||
node.bObstacleUpper = tile.pos!=game->NO_COLLISION.pos||tile.size!=game->NO_COLLISION.size;
|
||||
node.parent = nullptr;
|
||||
node.bVisited = false;
|
||||
if(nodes.size()==1){//This is the first node added, set as the start node.
|
||||
nodeStart=&node;
|
||||
}
|
||||
nodeEnd=&node;
|
||||
}
|
||||
|
||||
for (int x = 0; x < game->GetCurrentMapData().width; x++)
|
||||
for (int y = 0; y < game->GetCurrentMapData().height; y++)
|
||||
{
|
||||
if(y>0)
|
||||
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y - 1) * game->GetCurrentMapData().width + (x + 0)]);
|
||||
if(y<game->GetCurrentMapData().height-1)
|
||||
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y + 1) * game->GetCurrentMapData().width + (x + 0)]);
|
||||
if (x>0)
|
||||
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y + 0) * game->GetCurrentMapData().width + (x - 1)]);
|
||||
if(x<game->GetCurrentMapData().width-1)
|
||||
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y + 0) * game->GetCurrentMapData().width + (x + 1)]);
|
||||
/*if (y>0 && x>0)
|
||||
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y - 1) * game->GetCurrentMapData().width + (x - 1)]);
|
||||
if (y<game->GetCurrentMapData().height-1 && x>0)
|
||||
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y + 1) * game->GetCurrentMapData().width + (x - 1)]);
|
||||
if (y>0 && x<game->GetCurrentMapData().width-1)
|
||||
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y - 1) * game->GetCurrentMapData().width + (x + 1)]);
|
||||
if (y<game->GetCurrentMapData().height - 1 && x<game->GetCurrentMapData().width-1)
|
||||
nodes[y*game->GetCurrentMapData().width + x].vecNeighbours.push_back(&nodes[(y + 1) * game->GetCurrentMapData().width + (x + 1)]);*/
|
||||
}
|
||||
|
||||
// Manually position the start and end markers so they are not nullptr
|
||||
nodeStart = &nodes[(game->GetCurrentMapData().height / 2) * game->GetCurrentMapData().width + 1];
|
||||
nodeEnd = &nodes[(game->GetCurrentMapData().height / 2) * game->GetCurrentMapData().width + game->GetCurrentMapData().width-2];
|
||||
for (int x = 0; x < game->GetCurrentMapData().width; x++)
|
||||
for (int y = 0; y < game->GetCurrentMapData().height; y++)
|
||||
{
|
||||
sNode&node=const_cast<sNode&>(*nodes.find({x,y}));
|
||||
if(y>0){
|
||||
if(nodes.find({x,y-1})!=nodes.end()){
|
||||
node.vecNeighbours.push_back(&*nodes.find({x,y-1}));
|
||||
}
|
||||
}
|
||||
if(y<game->GetCurrentMapData().height-1){
|
||||
if(nodes.find({x,y+1})!=nodes.end()){
|
||||
node.vecNeighbours.push_back(&*nodes.find({x,y+1}));
|
||||
}
|
||||
}
|
||||
if(x>0){
|
||||
if(nodes.find({x-1,y})!=nodes.end()){
|
||||
node.vecNeighbours.push_back(&*nodes.find({x-1,y}));
|
||||
}
|
||||
}
|
||||
if(x<game->GetCurrentMapData().width-1){
|
||||
if(nodes.find({x+1,y})!=nodes.end()){
|
||||
node.vecNeighbours.push_back(&*nodes.find({x+1,y}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<vf2d> Pathfinding::Solve_AStar(vf2d startPos,vf2d endPos,float maxRange,bool upperLevel){
|
||||
float dist=float(sqrt(pow(endPos.x-startPos.x,2)+pow(endPos.y-startPos.y,2)));
|
||||
if(dist>maxRange*game->GetCurrentMapData().tilewidth)return {};
|
||||
if(nodes.find(sNode{int(startPos.x),int(startPos.y)})==nodes.end())return{};
|
||||
if(nodes.find(sNode{int(endPos.x),int(endPos.y)})==nodes.end())return{};
|
||||
|
||||
nodeStart=&nodes[int(startPos.y/game->GetCurrentMapData().tilewidth)*game->GetCurrentMapData().width+int(startPos.x/game->GetCurrentMapData().tilewidth)];
|
||||
nodeEnd=&nodes[int(endPos.y/game->GetCurrentMapData().tilewidth)*game->GetCurrentMapData().width+int(endPos.x/game->GetCurrentMapData().tilewidth)];
|
||||
nodeStart=const_cast<sNode*>(&*nodes.find(sNode{int(startPos.x),int(startPos.y)}));
|
||||
nodeEnd=const_cast<sNode*>(&*nodes.find(sNode{int(endPos.x),int(endPos.y)}));
|
||||
|
||||
|
||||
geom2d::rect<int>posPerimeter{{int(std::min(startPos.x,endPos.x)),int(std::min(startPos.y,endPos.y))},{int(abs(endPos.x-startPos.x)),int(abs(endPos.y-startPos.y))}};
|
||||
@ -175,4 +182,126 @@ std::vector<vf2d> Pathfinding::Solve_AStar(vf2d startPos,vf2d endPos,float maxRa
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pathfinding::sSpline Pathfinding::Solve_WalkPath(vf2d startPos,vf2d endPos,float maxRange,bool upperLevel){
|
||||
Pathfinding::sSpline newSpline{};
|
||||
newSpline.Initialize(Solve_AStar(startPos,endPos,maxRange,upperLevel));
|
||||
return newSpline;
|
||||
}
|
||||
|
||||
void Pathfinding::sSpline::Initialize(const std::vector<vf2d>&points){
|
||||
this->points.clear();
|
||||
for(const vf2d&point:points){
|
||||
this->points.push_back({point});
|
||||
}
|
||||
fTotalSplineLength=0.f;
|
||||
for (int i = 0; i < this->points.size(); i++)
|
||||
{
|
||||
fTotalSplineLength += (this->points[i].length = CalculateSegmentLength(i, true));
|
||||
}
|
||||
}
|
||||
|
||||
Pathfinding::sPoint2D Pathfinding::sSpline::GetSplinePoint(float t, bool bLooped){
|
||||
int p0, p1, p2, p3;
|
||||
float t1=t;
|
||||
if (!bLooped)
|
||||
{
|
||||
p1 = (int)t1 + 1;
|
||||
p2 = p1 + 1;
|
||||
p3 = p2 + 1;
|
||||
p0 = p1 - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
p1 = (int)t1;
|
||||
p2 = (p1 + 1) % points.size();
|
||||
p3 = (p2 + 1) % points.size();
|
||||
p0 = p1 >= 1 ? p1 - 1 : points.size() - 1;
|
||||
}
|
||||
|
||||
t = t - (int)t;
|
||||
|
||||
float tt = t * t;
|
||||
float ttt = tt * t;
|
||||
|
||||
float q1 = -ttt + 2.0f*tt - t;
|
||||
float q2 = 3.0f*ttt - 5.0f*tt + 2.0f;
|
||||
float q3 = -3.0f*ttt + 4.0f*tt + t;
|
||||
float q4 = ttt - tt;
|
||||
|
||||
float tx = 0.5f * (points[p0].pos.x * q1 + points[p1].pos.x * q2 + points[p2].pos.x * q3 + points[p3].pos.x * q4);
|
||||
float ty = 0.5f * (points[p0].pos.y * q1 + points[p1].pos.y * q2 + points[p2].pos.y * q3 + points[p3].pos.y * q4);
|
||||
|
||||
return{ {tx, ty} };
|
||||
}
|
||||
|
||||
Pathfinding::sPoint2D Pathfinding::sSpline::GetSplineGradient(float t, bool bLooped){
|
||||
int p0, p1, p2, p3;
|
||||
float t1=t;
|
||||
if (!bLooped)
|
||||
{
|
||||
p1 = (int)t1 + 1;
|
||||
p2 = p1 + 1;
|
||||
p3 = p2 + 1;
|
||||
p0 = p1 - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
p1 = (int)t1;
|
||||
p2 = (p1 + 1) % points.size();
|
||||
p3 = (p2 + 1) % points.size();
|
||||
p0 = p1 >= 1 ? p1 - 1 : points.size() - 1;
|
||||
}
|
||||
|
||||
t = t - (int)t;
|
||||
|
||||
float tt = t * t;
|
||||
float ttt = tt * t;
|
||||
|
||||
float q1 = -3.0f * tt + 4.0f*t - 1;
|
||||
float q2 = 9.0f*tt - 10.0f*t;
|
||||
float q3 = -9.0f*tt + 8.0f*t + 1.0f;
|
||||
float q4 = 3.0f*tt - 2.0f*t;
|
||||
|
||||
float tx = 0.5f * (points[p0].pos.x * q1 + points[p1].pos.x * q2 + points[p2].pos.x * q3 + points[p3].pos.x * q4);
|
||||
float ty = 0.5f * (points[p0].pos.y * q1 + points[p1].pos.y * q2 + points[p2].pos.y * q3 + points[p3].pos.y * q4);
|
||||
|
||||
return{ {tx, ty} };
|
||||
}
|
||||
|
||||
float Pathfinding::sSpline::CalculateSegmentLength(int node, bool bLooped){
|
||||
float fLength = 0.0f;
|
||||
float fStepSize = 0.005;
|
||||
|
||||
sPoint2D old_point, new_point;
|
||||
old_point = GetSplinePoint((float)node, bLooped);
|
||||
|
||||
for (float t = 0; t < 1.0f; t += fStepSize)
|
||||
{
|
||||
new_point = GetSplinePoint(fmod((float)node + t,1.0f), bLooped);
|
||||
fLength += sqrtf((new_point.pos.x - old_point.pos.x)*(new_point.pos.x - old_point.pos.x)
|
||||
+ (new_point.pos.y - old_point.pos.y)*(new_point.pos.y - old_point.pos.y));
|
||||
old_point = new_point;
|
||||
}
|
||||
|
||||
return fLength;
|
||||
}
|
||||
|
||||
float Pathfinding::sSpline::GetNormalisedOffset(float p){
|
||||
// Which node is the base?
|
||||
int i = 0;
|
||||
while (p > points[i].length)
|
||||
{
|
||||
p -= points[i].length;
|
||||
i++;
|
||||
}
|
||||
|
||||
// The fractional is the offset
|
||||
return (float)i + (p / points[i].length);
|
||||
}
|
||||
|
||||
inline bool operator < (const Pathfinding::sNode& lhs, const Pathfinding::sNode& rhs)
|
||||
{ return lhs.y < rhs.y || (lhs.y == rhs.y && lhs.x < rhs.x); }
|
||||
inline bool operator > (const Pathfinding::sNode& lhs, const Pathfinding::sNode& rhs)
|
||||
{ return lhs.y > rhs.y || (lhs.y == rhs.y && lhs.x > rhs.x); }
|
||||
@ -41,18 +41,35 @@ All rights reserved.
|
||||
struct Pathfinding{
|
||||
struct sNode
|
||||
{
|
||||
int x=0; // Nodes position in 2D space
|
||||
int y=0;
|
||||
bool bObstacle = false; // Is the node an obstruction?
|
||||
bool bObstacleUpper = false; // Is the node an obstruction on the upper level?
|
||||
bool bVisited = false; // Have we searched this node before?
|
||||
float fGlobalGoal=0; // Distance to goal so far
|
||||
float fLocalGoal=0; // Distance to goal if we took the alternative route
|
||||
int x=0; // Nodes position in 2D space
|
||||
int y=0;
|
||||
std::vector<sNode*> vecNeighbours; // Connections to neighbours
|
||||
std::vector<const sNode*> vecNeighbours; // Connections to neighbours
|
||||
sNode* parent=nullptr; // Node connecting to this node that offers shortest parent
|
||||
};
|
||||
|
||||
sNode *nodes = nullptr;
|
||||
struct sPoint2D
|
||||
{
|
||||
vf2d pos;
|
||||
float length;
|
||||
};
|
||||
struct sSpline
|
||||
{
|
||||
std::vector<sPoint2D> points;
|
||||
float fTotalSplineLength = 0.0f;
|
||||
|
||||
void Initialize(const std::vector<vf2d>&points);
|
||||
sPoint2D GetSplinePoint(float t, bool bLooped = true);
|
||||
sPoint2D GetSplineGradient(float t, bool bLooped = true);
|
||||
float CalculateSegmentLength(int node, bool bLooped = true);
|
||||
float GetNormalisedOffset(float p);
|
||||
};
|
||||
|
||||
std::set<sNode>nodes;
|
||||
|
||||
sNode *nodeStart = nullptr;
|
||||
sNode *nodeEnd = nullptr;
|
||||
@ -60,4 +77,5 @@ struct Pathfinding{
|
||||
void Initialize();
|
||||
//maxRange in tiles. Returns the path as points.
|
||||
std::vector<vf2d> Solve_AStar(vf2d startPos,vf2d endPos,float maxRange=8,bool upperLevel=false);
|
||||
sSpline Solve_WalkPath(vf2d startPos,vf2d endPos,float maxRange=8,bool upperLevel=false);
|
||||
};
|
||||
@ -109,7 +109,7 @@ void Monster::STRATEGY::SHOOT_AFAR(Monster&m,float fElapsedTime,std::string stra
|
||||
if(!pathfindingDecision&&m.targetAcquireTimer==0){
|
||||
m.StartPathfinding(2.5);
|
||||
}else
|
||||
if((m.path.size()==0&&!m.canMove)||line.length()>=24.f*ConfigInt("Range")/100.f){
|
||||
if((m.path.points.size()==0&&!m.canMove)||line.length()>=24.f*ConfigInt("Range")/100.f){
|
||||
m.SetState(State::NORMAL);
|
||||
}
|
||||
if(moveTowardsLine.vector().x>0){
|
||||
|
||||
@ -39,7 +39,7 @@ All rights reserved.
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 2
|
||||
#define VERSION_PATCH 1
|
||||
#define VERSION_BUILD 5574
|
||||
#define VERSION_BUILD 5582
|
||||
|
||||
#define stringify(a) stringify_(a)
|
||||
#define stringify_(a) #a
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user