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,
|
||||
mGenerator.getPlayerSpawn()));
|
||||
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.
|
||||
*/
|
||||
void
|
||||
Pathfinder::insertArea(const sf::IntRect& rect) {
|
||||
Pathfinder::insertArea(const sf::FloatRect& rect) {
|
||||
Area a;
|
||||
// Not sure why the offset of -50 is required, but with it, areas align
|
||||
// with tiles perfectly.
|
||||
a.area = sf::IntRect(rect.left * Tile::TILE_SIZE.x - 50,
|
||||
rect.top * Tile::TILE_SIZE.y - 50,
|
||||
a.area = sf::FloatRect(rect.left * Tile::TILE_SIZE.x - Tile::TILE_SIZE.x / 2.0f,
|
||||
rect.top * Tile::TILE_SIZE.y - Tile::TILE_SIZE.y / 2.0f,
|
||||
rect.width * Tile::TILE_SIZE.x,
|
||||
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);
|
||||
mAreas.push_back(a);
|
||||
}
|
||||
|
@ -176,8 +174,8 @@ Pathfinder::generatePortals() {
|
|||
.getOverlap(Interval::IntervalFromPoints(other.area.top,
|
||||
other.area.top + other.area.height));
|
||||
if (overlap.getLength() > 0) {
|
||||
portal.start = sf::Vector2i(other.area.left, overlap.start);
|
||||
portal.end = sf::Vector2i(other.area.left, overlap.end);
|
||||
portal.start = sf::Vector2f(other.area.left, overlap.start);
|
||||
portal.end = sf::Vector2f(other.area.left, overlap.end);
|
||||
it.portals.push_back(portal);
|
||||
}
|
||||
}
|
||||
|
@ -187,8 +185,8 @@ Pathfinder::generatePortals() {
|
|||
.getOverlap(Interval::IntervalFromPoints(other.area.top,
|
||||
other.area.top + other.area.height));
|
||||
if (overlap.getLength() > 0) {
|
||||
portal.start = sf::Vector2i(it.area.left, overlap.start);
|
||||
portal.end = sf::Vector2i(it.area.left, overlap.end);
|
||||
portal.start = sf::Vector2f(it.area.left, overlap.start);
|
||||
portal.end = sf::Vector2f(it.area.left, overlap.end);
|
||||
it.portals.push_back(portal);
|
||||
}
|
||||
}
|
||||
|
@ -198,8 +196,8 @@ Pathfinder::generatePortals() {
|
|||
.getOverlap(Interval::IntervalFromPoints(other.area.left,
|
||||
other.area.left + other.area.width));
|
||||
if (overlap.getLength() > 0) {
|
||||
portal.start = sf::Vector2i(overlap.start, other.area.top);
|
||||
portal.end = sf::Vector2i(overlap.end, other.area.top);
|
||||
portal.start = sf::Vector2f(overlap.start, other.area.top);
|
||||
portal.end = sf::Vector2f(overlap.end, other.area.top);
|
||||
it.portals.push_back(portal);
|
||||
}
|
||||
}
|
||||
|
@ -209,8 +207,8 @@ Pathfinder::generatePortals() {
|
|||
.getOverlap(Interval::IntervalFromPoints(other.area.left,
|
||||
other.area.left + other.area.width));
|
||||
if (overlap.getLength() > 0) {
|
||||
portal.start = sf::Vector2i(overlap.start, it.area.top);
|
||||
portal.end = sf::Vector2i(overlap.end, it.area.top);
|
||||
portal.start = sf::Vector2f(overlap.start, it.area.top);
|
||||
portal.end = sf::Vector2f(overlap.end, it.area.top);
|
||||
it.portals.push_back(portal);
|
||||
}
|
||||
}
|
||||
|
@ -224,9 +222,24 @@ Pathfinder::generatePortals() {
|
|||
Pathfinder::Area*
|
||||
Pathfinder::getArea(const sf::Vector2f& point) const {
|
||||
for (auto& area : mAreas) {
|
||||
if (sf::FloatRect(area.area).contains(point))
|
||||
if (area.area.contains(point))
|
||||
// Make the return value non-const for convenience.
|
||||
return &const_cast<Area&>(area);
|
||||
}
|
||||
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/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:
|
||||
struct Area;
|
||||
struct Portal;
|
||||
|
||||
public:
|
||||
void insertArea(const sf::IntRect& rect);
|
||||
void insertArea(const sf::FloatRect& rect);
|
||||
void generatePortals();
|
||||
std::vector<sf::Vector2f> getPath(const sf::Vector2f& start,
|
||||
const sf::Vector2f& end, float radius) const;
|
||||
|
@ -25,6 +31,7 @@ public:
|
|||
private:
|
||||
Area* getArea(const sf::Vector2f& point) const;
|
||||
std::vector<Portal*> astarArea(Area* start, Area* end) const;
|
||||
void draw(sf::RenderTarget& target, sf::RenderStates states) const;
|
||||
|
||||
private:
|
||||
static const float WALL_DISTANCE_MULTIPLIER;
|
||||
|
@ -37,8 +44,8 @@ private:
|
|||
* Redundant data as portals are saved twice.
|
||||
*/
|
||||
struct Pathfinder::Portal {
|
||||
sf::Vector2i start;
|
||||
sf::Vector2i end;
|
||||
sf::Vector2f start;
|
||||
sf::Vector2f end;
|
||||
Area* area;
|
||||
};
|
||||
|
||||
|
@ -46,8 +53,8 @@ struct Pathfinder::Portal {
|
|||
* Nodes
|
||||
*/
|
||||
struct Pathfinder::Area {
|
||||
sf::IntRect area;
|
||||
sf::Vector2i center;
|
||||
sf::FloatRect area;
|
||||
sf::Vector2f center;
|
||||
std::vector<Portal> portals;
|
||||
};
|
||||
|
||||
|
|
|
@ -18,24 +18,11 @@
|
|||
|
||||
#include "../Pathfinder.h"
|
||||
#include "../World.h"
|
||||
#include "../util/Log.h"
|
||||
|
||||
/// Seed for usage with simplexnoise.h
|
||||
uint8_t perm[512];
|
||||
#include "../sprites/Enemy.h"
|
||||
|
||||
const int Generator::GENERATE_AREA_SIZE = 4;
|
||||
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.
|
||||
*/
|
||||
|
@ -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).
|
||||
*/
|
||||
void
|
||||
Generator::generateCurrentAreaIfNeeded(const sf::Vector2f& position) {
|
||||
Generator::generateCurrentAreaIfNeeded(const sf::Vector2f& playerPosition) {
|
||||
auto compare = [](const sf::Vector2i& a, const sf::Vector2i& b) {
|
||||
return a.x < b.x || (a.x == b.x && a.y < b.y);
|
||||
};
|
||||
std::map<sf::Vector2i, float, decltype(compare)> open(compare);
|
||||
std::set<sf::Vector2i, decltype(compare)> closed(compare);
|
||||
|
||||
sf::Vector2i start((int) floor(position.x / Tile::TILE_SIZE.x),
|
||||
(int) floor(position.y / Tile::TILE_SIZE.y));
|
||||
sf::Vector2i start((int) floor(playerPosition.x / Tile::TILE_SIZE.x),
|
||||
(int) floor(playerPosition.y / Tile::TILE_SIZE.y));
|
||||
start /= GENERATE_AREA_SIZE;
|
||||
auto makePair = [&start](const sf::Vector2i& point) {
|
||||
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::Vector2i(GENERATE_AREA_SIZE, GENERATE_AREA_SIZE) / 2,
|
||||
sf::Vector2i(GENERATE_AREA_SIZE, GENERATE_AREA_SIZE));
|
||||
LOG_I("Generating area " << 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 (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)));
|
||||
}
|
||||
|
||||
generateAreas(area, sf::Vector2f(area.left, area.top));
|
||||
generateAreas(area);
|
||||
mPathfinder.generatePortals();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns coordinates where enemies should spawn.
|
||||
*/
|
||||
std::vector<sf::Vector2f>
|
||||
Generator::getEnemySpawns(const sf::IntRect& area) {
|
||||
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);
|
||||
for (int x = area.left; x < area.left + area.width; x++) {
|
||||
for (int y = area.top; y < area.top + area.height; y++) {
|
||||
if (mCharacterNoise.getNoise(x, y) < 0.05f) {
|
||||
ret.insert(sf::Vector2f(thor::cwiseProduct(
|
||||
findClosestFloor(sf::Vector2i(x, y)), Tile::TILE_SIZE)));
|
||||
if (mCharacterNoise.getNoise(x, y) < -0.85f) {
|
||||
sf::Vector2i tilePosition = findClosestFloor(sf::Vector2i(x, y));
|
||||
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;
|
||||
for (int x = area.left; x < area.left + area.width; x++) {
|
||||
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);
|
||||
}
|
||||
return count;
|
||||
|
@ -210,30 +206,31 @@ Generator::filterWalls(array& tiles, int x, int y, int longside,
|
|||
* tiles where possible.
|
||||
*
|
||||
* @param area The area to generate areas for.
|
||||
* @param offset Offset of tiles[0][0] from World coordinate (0, 0).
|
||||
*/
|
||||
void
|
||||
Generator::generateAreas(const sf::IntRect& area,
|
||||
const sf::Vector2f& offset) {
|
||||
Generator::generateAreas(const sf::IntRect& area) {
|
||||
assert(area.width > 0 && area.height > 0);
|
||||
int count = countWalls(area);
|
||||
if (count == 0) {
|
||||
mPathfinder.insertArea(sf::IntRect(area));
|
||||
}
|
||||
else if (count == area.width * area.height) {
|
||||
|
||||
int wallCount = 0;
|
||||
for (int x = area.left; x < area.left + area.width; x++)
|
||||
for (int y = area.top; y < area.top + area.height; y++)
|
||||
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;
|
||||
}
|
||||
else {
|
||||
int halfWidth = area.width / 2.0f;
|
||||
int halfHeight = area.height / 2.0f;
|
||||
int halfWidth = area.width / 2;
|
||||
int halfHeight = area.height / 2;
|
||||
generateAreas(sf::IntRect(area.left,
|
||||
area.top, halfWidth, halfHeight), offset);
|
||||
area.top, halfWidth, halfHeight));
|
||||
generateAreas(sf::IntRect(area.left + halfWidth,
|
||||
area.top, halfWidth, halfHeight), offset);
|
||||
area.top, halfWidth, halfHeight));
|
||||
generateAreas(sf::IntRect(area.left,
|
||||
area.top + halfHeight, halfWidth, halfHeight), offset);
|
||||
area.top + halfHeight, halfWidth, halfHeight));
|
||||
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);
|
||||
if (mTiles.count(current.x) != 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;
|
||||
}
|
||||
else {
|
||||
if (closed.find(sf::Vector2i(current.x + 1, current.y)) == closed.end())
|
||||
open.insert(makePair(sf::Vector2i(current.x + 1, current.y)));
|
||||
|
|
|
@ -31,7 +31,7 @@ private:
|
|||
typedef std::map<int, std::map<int, type> > array;
|
||||
|
||||
private:
|
||||
void generateAreas(const sf::IntRect& area, const sf::Vector2f& offset);
|
||||
void generateAreas(const sf::IntRect& area);
|
||||
void generateTiles(const sf::IntRect& area);
|
||||
sf::Vector2i findClosestFloor(const sf::Vector2i& position) const;
|
||||
|
||||
|
@ -44,9 +44,6 @@ private:
|
|||
private:
|
||||
static const int GENERATE_AREA_SIZE;
|
||||
static const float GENERATE_AREA_RANGE;
|
||||
static const int MARGIN;
|
||||
static const float LAYER_TILES;
|
||||
static const float LAYER_ENEMIES;
|
||||
|
||||
World& mWorld;
|
||||
Pathfinder& mPathfinder;
|
||||
|
|
Reference in a new issue