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

@ -31,14 +31,7 @@ Game::Game(sf::RenderWindow& window) :
mGenerator.generateCurrentAreaIfNeeded(sf::Vector2f());
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)));
}
*/
mWorld.insertCharacter(mPlayer);
}
/**

View file

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

View file

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

View file

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

View file

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