Added enemies back in.
Fixed a bug where Pathfinder areas were created incorrectly, also added a debug draw function to Pathfinder
This commit is contained in:
parent
94814e0172
commit
51cf1d9c68
5 changed files with 81 additions and 73 deletions
|
@ -32,13 +32,6 @@ Game::Game(sf::RenderWindow& window) :
|
||||||
mPlayer = std::shared_ptr<Player>(new Player(mWorld, mPathfinder,
|
mPlayer = std::shared_ptr<Player>(new Player(mWorld, mPathfinder,
|
||||||
mGenerator.getPlayerSpawn()));
|
mGenerator.getPlayerSpawn()));
|
||||||
mWorld.insertCharacter(mPlayer);
|
mWorld.insertCharacter(mPlayer);
|
||||||
/*
|
|
||||||
auto enemyPositions = mGenerator.getEnemySpawns(area);
|
|
||||||
for (const auto& position : enemyPositions) {
|
|
||||||
if (thor::length(mPlayer->getPosition() - position) > Character::VISION_DISTANCE)
|
|
||||||
mWorld.insertCharacter(std::shared_ptr<Enemy>(new Enemy(mWorld, mPathfinder, position)));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -142,15 +142,13 @@ Pathfinder::getPath(const sf::Vector2f& start, const sf::Vector2f& end,
|
||||||
* @parm rect Rectangle the area covers.
|
* @parm rect Rectangle the area covers.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
Pathfinder::insertArea(const sf::IntRect& rect) {
|
Pathfinder::insertArea(const sf::FloatRect& rect) {
|
||||||
Area a;
|
Area a;
|
||||||
// Not sure why the offset of -50 is required, but with it, areas align
|
a.area = sf::FloatRect(rect.left * Tile::TILE_SIZE.x - Tile::TILE_SIZE.x / 2.0f,
|
||||||
// with tiles perfectly.
|
rect.top * Tile::TILE_SIZE.y - Tile::TILE_SIZE.y / 2.0f,
|
||||||
a.area = sf::IntRect(rect.left * Tile::TILE_SIZE.x - 50,
|
|
||||||
rect.top * Tile::TILE_SIZE.y - 50,
|
|
||||||
rect.width * Tile::TILE_SIZE.x,
|
rect.width * Tile::TILE_SIZE.x,
|
||||||
rect.height * Tile::TILE_SIZE.y);
|
rect.height * Tile::TILE_SIZE.y);
|
||||||
a.center = sf::Vector2i(a.area.left + a.area.width / 2,
|
a.center = sf::Vector2f(a.area.left + a.area.width / 2,
|
||||||
a.area.top + a.area.height / 2);
|
a.area.top + a.area.height / 2);
|
||||||
mAreas.push_back(a);
|
mAreas.push_back(a);
|
||||||
}
|
}
|
||||||
|
@ -176,8 +174,8 @@ Pathfinder::generatePortals() {
|
||||||
.getOverlap(Interval::IntervalFromPoints(other.area.top,
|
.getOverlap(Interval::IntervalFromPoints(other.area.top,
|
||||||
other.area.top + other.area.height));
|
other.area.top + other.area.height));
|
||||||
if (overlap.getLength() > 0) {
|
if (overlap.getLength() > 0) {
|
||||||
portal.start = sf::Vector2i(other.area.left, overlap.start);
|
portal.start = sf::Vector2f(other.area.left, overlap.start);
|
||||||
portal.end = sf::Vector2i(other.area.left, overlap.end);
|
portal.end = sf::Vector2f(other.area.left, overlap.end);
|
||||||
it.portals.push_back(portal);
|
it.portals.push_back(portal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,8 +185,8 @@ Pathfinder::generatePortals() {
|
||||||
.getOverlap(Interval::IntervalFromPoints(other.area.top,
|
.getOverlap(Interval::IntervalFromPoints(other.area.top,
|
||||||
other.area.top + other.area.height));
|
other.area.top + other.area.height));
|
||||||
if (overlap.getLength() > 0) {
|
if (overlap.getLength() > 0) {
|
||||||
portal.start = sf::Vector2i(it.area.left, overlap.start);
|
portal.start = sf::Vector2f(it.area.left, overlap.start);
|
||||||
portal.end = sf::Vector2i(it.area.left, overlap.end);
|
portal.end = sf::Vector2f(it.area.left, overlap.end);
|
||||||
it.portals.push_back(portal);
|
it.portals.push_back(portal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,8 +196,8 @@ Pathfinder::generatePortals() {
|
||||||
.getOverlap(Interval::IntervalFromPoints(other.area.left,
|
.getOverlap(Interval::IntervalFromPoints(other.area.left,
|
||||||
other.area.left + other.area.width));
|
other.area.left + other.area.width));
|
||||||
if (overlap.getLength() > 0) {
|
if (overlap.getLength() > 0) {
|
||||||
portal.start = sf::Vector2i(overlap.start, other.area.top);
|
portal.start = sf::Vector2f(overlap.start, other.area.top);
|
||||||
portal.end = sf::Vector2i(overlap.end, other.area.top);
|
portal.end = sf::Vector2f(overlap.end, other.area.top);
|
||||||
it.portals.push_back(portal);
|
it.portals.push_back(portal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,8 +207,8 @@ Pathfinder::generatePortals() {
|
||||||
.getOverlap(Interval::IntervalFromPoints(other.area.left,
|
.getOverlap(Interval::IntervalFromPoints(other.area.left,
|
||||||
other.area.left + other.area.width));
|
other.area.left + other.area.width));
|
||||||
if (overlap.getLength() > 0) {
|
if (overlap.getLength() > 0) {
|
||||||
portal.start = sf::Vector2i(overlap.start, it.area.top);
|
portal.start = sf::Vector2f(overlap.start, it.area.top);
|
||||||
portal.end = sf::Vector2i(overlap.end, it.area.top);
|
portal.end = sf::Vector2f(overlap.end, it.area.top);
|
||||||
it.portals.push_back(portal);
|
it.portals.push_back(portal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,9 +222,24 @@ Pathfinder::generatePortals() {
|
||||||
Pathfinder::Area*
|
Pathfinder::Area*
|
||||||
Pathfinder::getArea(const sf::Vector2f& point) const {
|
Pathfinder::getArea(const sf::Vector2f& point) const {
|
||||||
for (auto& area : mAreas) {
|
for (auto& area : mAreas) {
|
||||||
if (sf::FloatRect(area.area).contains(point))
|
if (area.area.contains(point))
|
||||||
// Make the return value non-const for convenience.
|
// Make the return value non-const for convenience.
|
||||||
return &const_cast<Area&>(area);
|
return &const_cast<Area&>(area);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws areas.
|
||||||
|
*/
|
||||||
|
#ifndef NDEBUG
|
||||||
|
void
|
||||||
|
Pathfinder::draw(sf::RenderTarget& target, sf::RenderStates states) const {
|
||||||
|
for (auto& area : mAreas) {
|
||||||
|
sf::RectangleShape rect(sf::Vector2f(area.area.width, area.area.height));
|
||||||
|
rect.setPosition(sf::Vector2f(area.area.left, area.area.top));
|
||||||
|
rect.setFillColor(sf::Color(area.area.width * 30, 127, 0, 96));
|
||||||
|
target.draw(rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -11,13 +11,19 @@
|
||||||
#include <SFML/System.hpp>
|
#include <SFML/System.hpp>
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
class Pathfinder {
|
/**
|
||||||
|
* Used to find paths between points in the world.
|
||||||
|
*
|
||||||
|
* For this, a representation of all walkable areas in the world is
|
||||||
|
* saved, which is then used to run A* on.
|
||||||
|
*/
|
||||||
|
class Pathfinder : public sf::Drawable {
|
||||||
private:
|
private:
|
||||||
struct Area;
|
struct Area;
|
||||||
struct Portal;
|
struct Portal;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void insertArea(const sf::IntRect& rect);
|
void insertArea(const sf::FloatRect& rect);
|
||||||
void generatePortals();
|
void generatePortals();
|
||||||
std::vector<sf::Vector2f> getPath(const sf::Vector2f& start,
|
std::vector<sf::Vector2f> getPath(const sf::Vector2f& start,
|
||||||
const sf::Vector2f& end, float radius) const;
|
const sf::Vector2f& end, float radius) const;
|
||||||
|
@ -25,6 +31,7 @@ public:
|
||||||
private:
|
private:
|
||||||
Area* getArea(const sf::Vector2f& point) const;
|
Area* getArea(const sf::Vector2f& point) const;
|
||||||
std::vector<Portal*> astarArea(Area* start, Area* end) const;
|
std::vector<Portal*> astarArea(Area* start, Area* end) const;
|
||||||
|
void draw(sf::RenderTarget& target, sf::RenderStates states) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const float WALL_DISTANCE_MULTIPLIER;
|
static const float WALL_DISTANCE_MULTIPLIER;
|
||||||
|
@ -37,8 +44,8 @@ private:
|
||||||
* Redundant data as portals are saved twice.
|
* Redundant data as portals are saved twice.
|
||||||
*/
|
*/
|
||||||
struct Pathfinder::Portal {
|
struct Pathfinder::Portal {
|
||||||
sf::Vector2i start;
|
sf::Vector2f start;
|
||||||
sf::Vector2i end;
|
sf::Vector2f end;
|
||||||
Area* area;
|
Area* area;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,8 +53,8 @@ struct Pathfinder::Portal {
|
||||||
* Nodes
|
* Nodes
|
||||||
*/
|
*/
|
||||||
struct Pathfinder::Area {
|
struct Pathfinder::Area {
|
||||||
sf::IntRect area;
|
sf::FloatRect area;
|
||||||
sf::Vector2i center;
|
sf::Vector2f center;
|
||||||
std::vector<Portal> portals;
|
std::vector<Portal> portals;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,24 +18,11 @@
|
||||||
|
|
||||||
#include "../Pathfinder.h"
|
#include "../Pathfinder.h"
|
||||||
#include "../World.h"
|
#include "../World.h"
|
||||||
#include "../util/Log.h"
|
#include "../sprites/Enemy.h"
|
||||||
|
|
||||||
/// Seed for usage with simplexnoise.h
|
|
||||||
uint8_t perm[512];
|
|
||||||
|
|
||||||
const int Generator::GENERATE_AREA_SIZE = 4;
|
const int Generator::GENERATE_AREA_SIZE = 4;
|
||||||
const float Generator::GENERATE_AREA_RANGE = 4.0f;
|
const float Generator::GENERATE_AREA_RANGE = 4.0f;
|
||||||
|
|
||||||
/**
|
|
||||||
* Amount of tiles extra to generate, to get consistent walls
|
|
||||||
* across multiple generateTiles calls for bordering areas.
|
|
||||||
*/
|
|
||||||
const int Generator::MARGIN = 10;
|
|
||||||
|
|
||||||
// Different layers in 3d noise so we don't use the same noise values.
|
|
||||||
const float Generator::LAYER_TILES = 0;
|
|
||||||
const float Generator::LAYER_ENEMIES = 1.0f;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates new random seed.
|
* Generates new random seed.
|
||||||
*/
|
*/
|
||||||
|
@ -45,19 +32,19 @@ Generator::Generator(World& world, Pathfinder& pathfinder) :
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates tiles near position (maximum distance is determined by
|
* Generates tiles near player position (maximum distance is determined by
|
||||||
* GENERATE_AREA_SIZE and GENERATE_AREA_RANGE).
|
* GENERATE_AREA_SIZE and GENERATE_AREA_RANGE).
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
Generator::generateCurrentAreaIfNeeded(const sf::Vector2f& position) {
|
Generator::generateCurrentAreaIfNeeded(const sf::Vector2f& playerPosition) {
|
||||||
auto compare = [](const sf::Vector2i& a, const sf::Vector2i& b) {
|
auto compare = [](const sf::Vector2i& a, const sf::Vector2i& b) {
|
||||||
return a.x < b.x || (a.x == b.x && a.y < b.y);
|
return a.x < b.x || (a.x == b.x && a.y < b.y);
|
||||||
};
|
};
|
||||||
std::map<sf::Vector2i, float, decltype(compare)> open(compare);
|
std::map<sf::Vector2i, float, decltype(compare)> open(compare);
|
||||||
std::set<sf::Vector2i, decltype(compare)> closed(compare);
|
std::set<sf::Vector2i, decltype(compare)> closed(compare);
|
||||||
|
|
||||||
sf::Vector2i start((int) floor(position.x / Tile::TILE_SIZE.x),
|
sf::Vector2i start((int) floor(playerPosition.x / Tile::TILE_SIZE.x),
|
||||||
(int) floor(position.y / Tile::TILE_SIZE.y));
|
(int) floor(playerPosition.y / Tile::TILE_SIZE.y));
|
||||||
start /= GENERATE_AREA_SIZE;
|
start /= GENERATE_AREA_SIZE;
|
||||||
auto makePair = [&start](const sf::Vector2i& point) {
|
auto makePair = [&start](const sf::Vector2i& point) {
|
||||||
return std::make_pair(point, thor::length(sf::Vector2f(point - start)));
|
return std::make_pair(point, thor::length(sf::Vector2f(point - start)));
|
||||||
|
@ -79,8 +66,13 @@ Generator::generateCurrentAreaIfNeeded(const sf::Vector2f& position) {
|
||||||
sf::IntRect area(current * GENERATE_AREA_SIZE -
|
sf::IntRect area(current * GENERATE_AREA_SIZE -
|
||||||
sf::Vector2i(GENERATE_AREA_SIZE, GENERATE_AREA_SIZE) / 2,
|
sf::Vector2i(GENERATE_AREA_SIZE, GENERATE_AREA_SIZE) / 2,
|
||||||
sf::Vector2i(GENERATE_AREA_SIZE, GENERATE_AREA_SIZE));
|
sf::Vector2i(GENERATE_AREA_SIZE, GENERATE_AREA_SIZE));
|
||||||
LOG_I("Generating area " << area);
|
|
||||||
generateTiles(area);
|
generateTiles(area);
|
||||||
|
for (const auto& enemyPosition : getEnemySpawns(area)) {
|
||||||
|
float distance = thor::length(enemyPosition - playerPosition);
|
||||||
|
if (distance > Character::VISION_DISTANCE) {
|
||||||
|
mWorld.insertCharacter(std::shared_ptr<Enemy>(new Enemy(mWorld, mPathfinder, enemyPosition)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mGenerated[current.x][current.y] && distance <= GENERATE_AREA_RANGE) {
|
if (mGenerated[current.x][current.y] && distance <= GENERATE_AREA_RANGE) {
|
||||||
if (closed.find(sf::Vector2i(current.x + 1, current.y)) == closed.end())
|
if (closed.find(sf::Vector2i(current.x + 1, current.y)) == closed.end())
|
||||||
|
@ -127,10 +119,13 @@ Generator::generateTiles(const sf::IntRect& area) {
|
||||||
new Tile(generatedTiles.at(x).at(y), x, y)));
|
new Tile(generatedTiles.at(x).at(y), x, y)));
|
||||||
}
|
}
|
||||||
|
|
||||||
generateAreas(area, sf::Vector2f(area.left, area.top));
|
generateAreas(area);
|
||||||
mPathfinder.generatePortals();
|
mPathfinder.generatePortals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns coordinates where enemies should spawn.
|
||||||
|
*/
|
||||||
std::vector<sf::Vector2f>
|
std::vector<sf::Vector2f>
|
||||||
Generator::getEnemySpawns(const sf::IntRect& area) {
|
Generator::getEnemySpawns(const sf::IntRect& area) {
|
||||||
auto compare = [](const sf::Vector2f& a, const sf::Vector2f& b) {
|
auto compare = [](const sf::Vector2f& a, const sf::Vector2f& b) {
|
||||||
|
@ -139,9 +134,10 @@ Generator::getEnemySpawns(const sf::IntRect& area) {
|
||||||
std::set<sf::Vector2f, decltype(compare)> ret(compare);
|
std::set<sf::Vector2f, decltype(compare)> ret(compare);
|
||||||
for (int x = area.left; x < area.left + area.width; x++) {
|
for (int x = area.left; x < area.left + area.width; x++) {
|
||||||
for (int y = area.top; y < area.top + area.height; y++) {
|
for (int y = area.top; y < area.top + area.height; y++) {
|
||||||
if (mCharacterNoise.getNoise(x, y) < 0.05f) {
|
if (mCharacterNoise.getNoise(x, y) < -0.85f) {
|
||||||
ret.insert(sf::Vector2f(thor::cwiseProduct(
|
sf::Vector2i tilePosition = findClosestFloor(sf::Vector2i(x, y));
|
||||||
findClosestFloor(sf::Vector2i(x, y)), Tile::TILE_SIZE)));
|
ret.insert(sf::Vector2f(tilePosition.x * Tile::TILE_SIZE.x,
|
||||||
|
tilePosition.y * Tile::TILE_SIZE.y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,7 +169,7 @@ Generator::countWalls(const sf::IntRect& area) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (int x = area.left; x < area.left + area.width; x++) {
|
for (int x = area.left; x < area.left + area.width; x++) {
|
||||||
for (int y = area.top; y < area.top + area.height; y++)
|
for (int y = area.top; y < area.top + area.height; y++)
|
||||||
count += (int) (getTileType(mCharacterNoise.getNoise(x, y)) ==
|
count += (int) (getTileType(mTileNoise.getNoise(x, y)) ==
|
||||||
type::WALL);
|
type::WALL);
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
|
@ -210,30 +206,31 @@ Generator::filterWalls(array& tiles, int x, int y, int longside,
|
||||||
* tiles where possible.
|
* tiles where possible.
|
||||||
*
|
*
|
||||||
* @param area The area to generate areas for.
|
* @param area The area to generate areas for.
|
||||||
* @param offset Offset of tiles[0][0] from World coordinate (0, 0).
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
Generator::generateAreas(const sf::IntRect& area,
|
Generator::generateAreas(const sf::IntRect& area) {
|
||||||
const sf::Vector2f& offset) {
|
|
||||||
assert(area.width > 0 && area.height > 0);
|
assert(area.width > 0 && area.height > 0);
|
||||||
int count = countWalls(area);
|
|
||||||
if (count == 0) {
|
int wallCount = 0;
|
||||||
mPathfinder.insertArea(sf::IntRect(area));
|
for (int x = area.left; x < area.left + area.width; x++)
|
||||||
}
|
for (int y = area.top; y < area.top + area.height; y++)
|
||||||
else if (count == area.width * area.height) {
|
wallCount += (int) (mTiles[x][y] == type::WALL);
|
||||||
|
|
||||||
|
if (wallCount == 0)
|
||||||
|
mPathfinder.insertArea(sf::FloatRect(area.left, area.top, area.width, area.height));
|
||||||
|
else if (wallCount == area.width * area.height)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
int halfWidth = area.width / 2.0f;
|
int halfWidth = area.width / 2;
|
||||||
int halfHeight = area.height / 2.0f;
|
int halfHeight = area.height / 2;
|
||||||
generateAreas(sf::IntRect(area.left,
|
generateAreas(sf::IntRect(area.left,
|
||||||
area.top, halfWidth, halfHeight), offset);
|
area.top, halfWidth, halfHeight));
|
||||||
generateAreas(sf::IntRect(area.left + halfWidth,
|
generateAreas(sf::IntRect(area.left + halfWidth,
|
||||||
area.top, halfWidth, halfHeight), offset);
|
area.top, halfWidth, halfHeight));
|
||||||
generateAreas(sf::IntRect(area.left,
|
generateAreas(sf::IntRect(area.left,
|
||||||
area.top + halfHeight, halfWidth, halfHeight), offset);
|
area.top + halfHeight, halfWidth, halfHeight));
|
||||||
generateAreas(sf::IntRect(area.left + halfWidth,
|
generateAreas(sf::IntRect(area.left + halfWidth,
|
||||||
area.top + halfHeight, halfWidth, halfHeight), offset);
|
area.top + halfHeight, halfWidth, halfHeight));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,8 +284,9 @@ Generator::findClosestFloor(const sf::Vector2i& position) const {
|
||||||
closed.insert(current);
|
closed.insert(current);
|
||||||
if (mTiles.count(current.x) != 0 &&
|
if (mTiles.count(current.x) != 0 &&
|
||||||
mTiles.at(current.x).count(current.y) != 0 &&
|
mTiles.at(current.x).count(current.y) != 0 &&
|
||||||
mTiles.at(current.x).at(current.y) == Tile::Type::FLOOR)
|
mTiles.at(current.x).at(current.y) == type::FLOOR) {
|
||||||
return current;
|
return current;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if (closed.find(sf::Vector2i(current.x + 1, current.y)) == closed.end())
|
if (closed.find(sf::Vector2i(current.x + 1, current.y)) == closed.end())
|
||||||
open.insert(makePair(sf::Vector2i(current.x + 1, current.y)));
|
open.insert(makePair(sf::Vector2i(current.x + 1, current.y)));
|
||||||
|
|
|
@ -31,7 +31,7 @@ private:
|
||||||
typedef std::map<int, std::map<int, type> > array;
|
typedef std::map<int, std::map<int, type> > array;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void generateAreas(const sf::IntRect& area, const sf::Vector2f& offset);
|
void generateAreas(const sf::IntRect& area);
|
||||||
void generateTiles(const sf::IntRect& area);
|
void generateTiles(const sf::IntRect& area);
|
||||||
sf::Vector2i findClosestFloor(const sf::Vector2i& position) const;
|
sf::Vector2i findClosestFloor(const sf::Vector2i& position) const;
|
||||||
|
|
||||||
|
@ -44,9 +44,6 @@ private:
|
||||||
private:
|
private:
|
||||||
static const int GENERATE_AREA_SIZE;
|
static const int GENERATE_AREA_SIZE;
|
||||||
static const float GENERATE_AREA_RANGE;
|
static const float GENERATE_AREA_RANGE;
|
||||||
static const int MARGIN;
|
|
||||||
static const float LAYER_TILES;
|
|
||||||
static const float LAYER_ENEMIES;
|
|
||||||
|
|
||||||
World& mWorld;
|
World& mWorld;
|
||||||
Pathfinder& mPathfinder;
|
Pathfinder& mPathfinder;
|
||||||
|
|
Reference in a new issue