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:
Felix Ableitner 2013-06-21 17:40:39 +02:00
parent 94814e0172
commit 51cf1d9c68
5 changed files with 81 additions and 73 deletions

View file

@ -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)));
}
*/
} }
/** /**

View file

@ -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

View file

@ -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;
}; };

View file

@ -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)));

View file

@ -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;