diff --git a/source/World.cpp b/source/World.cpp index 8f31c92..3e812f6 100755 --- a/source/World.cpp +++ b/source/World.cpp @@ -12,6 +12,8 @@ #include +#include "util/Interval.h" + /** * Insert a drawable into the group. Drawables should only be handled with shared_ptr. * An object can't be inserted more than once at the same level. @@ -335,9 +337,10 @@ World::testCollision(std::shared_ptr spriteA, float movementB = thor::dotProduct(axis, spriteB->getSpeed() * (elapsed / 1000.0f)); // Allow movement if sprites are moving apart. - return Interval(centerA, radiusA).getOverlap(Interval(centerB, radiusB)).getLength() < - Interval(centerA + movementA, radiusA).getOverlap( - Interval(centerB + movementB, radiusB)).getLength(); + return Interval::IntervalFromRadius(centerA, radiusA).getOverlap( + Interval::IntervalFromRadius(centerB, radiusB)).getLength() < + Interval::IntervalFromRadius(centerA + movementA, radiusA).getOverlap( + Interval::IntervalFromRadius(centerB + movementB, radiusB)).getLength(); } // circle-rect collision if (((spriteA->mShape.type == Sprite::Shape::Type::CIRCLE) && @@ -357,21 +360,21 @@ World::testCollision(std::shared_ptr spriteA, sf::Vector2f circleMovement = circle->getSpeed() * (elapsed / 1000.0f); // We assume that rectangles are always axis aligned. - float overlapNoMovementX = Interval(circlePos.x, radius) - .getOverlap(Interval (rectPos.x, halfsize.x)).getLength(); - float overlapMovementX = Interval(circlePos.x + circleMovement.x, radius) - .getOverlap(Interval (rectPos.x, halfsize.x)).getLength(); - float overlapNoMovementY = Interval(circlePos.y, radius) - .getOverlap(Interval (rectPos.y, halfsize.y)).getLength(); - float overlapMovementY = Interval(circlePos.y + circleMovement.y, radius) - .getOverlap(Interval (rectPos.y, halfsize.y)).getLength(); + float overlapNoMovementX = Interval::IntervalFromRadius(circlePos.x, radius) + .getOverlap(Interval::IntervalFromRadius(rectPos.x, halfsize.x)).getLength(); + float overlapMovementX = Interval::IntervalFromRadius(circlePos.x + circleMovement.x, radius) + .getOverlap(Interval::IntervalFromRadius(rectPos.x, halfsize.x)).getLength(); + float overlapNoMovementY = Interval::IntervalFromRadius(circlePos.y, radius) + .getOverlap(Interval::IntervalFromRadius(rectPos.y, halfsize.y)).getLength(); + float overlapMovementY = Interval::IntervalFromRadius(circlePos.y + circleMovement.y, radius) + .getOverlap(Interval::IntervalFromRadius(rectPos.y, halfsize.y)).getLength(); bool xyCollisionResult = (((overlapNoMovementX < overlapMovementX) && (overlapNoMovementY > 0)) || ((overlapNoMovementY < overlapMovementY) && (overlapNoMovementX > 0))); // If circle center is overlapping rectangle on x or y axis, we can take xyCollisionResult. - if (Interval(rectPos.x, halfsize.x).isInside(circlePos.x) || - Interval(rectPos.y, halfsize.y).isInside(circlePos.y)) { + if (Interval::IntervalFromRadius(rectPos.x, halfsize.x).isInside(circlePos.x) || + Interval::IntervalFromRadius(rectPos.y, halfsize.y).isInside(circlePos.y)) { return xyCollisionResult; } // Test if the circle is colliding with a corner of the rectangle. @@ -397,11 +400,13 @@ World::testCollision(std::shared_ptr spriteA, sf::Vector2f(halfsize.x, -halfsize.y)))); // Allow movement if sprites are moving apart. - return Interval(circlePosProjected, radius) - .getOverlap(Interval(rectPosProjected, rectHalfWidthProjected)) + return Interval::IntervalFromRadius(circlePosProjected, radius) + .getOverlap(Interval::IntervalFromRadius(rectPosProjected, + rectHalfWidthProjected)) .getLength() < - Interval(circlePosProjected + movementProjected, radius) - .getOverlap(Interval(rectPosProjected, rectHalfWidthProjected)) + Interval::IntervalFromRadius(circlePosProjected + movementProjected, radius) + .getOverlap(Interval::IntervalFromRadius(rectPosProjected, + rectHalfWidthProjected)) .getLength(); } // If there is no collision on x and y axis, there can't be one at all. @@ -439,52 +444,3 @@ World::draw(sf::RenderTarget& target, sf::RenderStates states) const { } } } - -/** - * Creates an interval from a center point and a radius. The interval - * ranges from center - radius to center + radius. - */ -World::Interval::Interval(float center, float radius) : - start(center - radius), - end(center + radius) { -} - -/** - * Returns the overlap between two intervals, e.g. the overlap between - * intervals (1,3) and (2,4) is (2,3). - */ -World::Interval -World::Interval::getOverlap(Interval other) const { - if ((start == other.start) && (end == other.end)) { - return *this; - } - Interval smaller = *this; - Interval bigger = other; - if (smaller.start > bigger.start) { - std::swap(smaller, bigger); - } - Interval iv(0, 0); - if (bigger.start < smaller.end) { - iv.start = bigger.start; - iv.end = smaller.end; - } - else { - iv.start = iv.end = 0.0f; - } - return iv; -} - -/** - * Returns true if the point is inside the interval. - */ -bool -World::Interval::isInside(float point) const { - return start < point && point < end; -} -/** - * Returns the length of the interval (distance between start and end). - */ -float -World::Interval::getLength() { - return end - start; -} diff --git a/source/World.h b/source/World.h index 57defed..6d7a301 100755 --- a/source/World.h +++ b/source/World.h @@ -35,18 +35,6 @@ public: std::vector > getCharacters(const sf::Vector2f& position, float maxDistance) const; -// Private types. -private: - class Interval { - public: - float start; - float end; - Interval(float center, float radius); - Interval getOverlap(Interval other) const; - bool isInside(float point) const; - float getLength(); - }; - // Private types. private: struct Area; diff --git a/source/sprites/TileManager.cpp b/source/sprites/TileManager.cpp index 96c9d4d..00db2a5 100644 --- a/source/sprites/TileManager.cpp +++ b/source/sprites/TileManager.cpp @@ -7,6 +7,9 @@ #include "TileManager.h" +#include + +#include "../util/Interval.h" #include "../util/Loader.h" #include "../util/Yaml.h" #include "../World.h" @@ -104,7 +107,50 @@ TileManager::removeTile(const TilePosition& position) { mTiles.erase(it); } } +} +/* + * Performs a raycast between two points to check if the path between them is + * clear of walls. + * + * @param lineStart First point of the line to test. + * @param lineEnd Second point of the line to test. + * @return True if the ray was not blocked. + */ +bool +TileManager::raycast(const sf::Vector2f& lineStart, + const sf::Vector2f& lineEnd) const { + assert(lineStart != lineEnd); + sf::Vector2f lineCenter = lineStart + 0.5f * (lineEnd - lineStart); + for (auto it : mTiles) { + if (it->getType() == Type::FLOOR) { + continue; + } + sf::Vector2f axis = it->getPosition() - lineCenter; + if (axis == sf::Vector2f()) { + return false; + } + + axis = thor::unitVector(axis); + sf::Vector2f halfsize = it->getSize() / 2.0f; + float rectPosProjected = thor::dotProduct(axis, it->getPosition()); + float lineStartProjected = thor::dotProduct(axis, lineStart); + float lineEndProjected = thor::dotProduct(axis, lineEnd); + // For corner projections, those on the same line with the rect + // center are equal by value, so we only need one on each axis + // and take the maximum. + float rectHalfWidthProjected = std::max( + abs(thor::dotProduct(axis, halfsize)), + abs(thor::dotProduct(axis, + sf::Vector2f(halfsize.x, -halfsize.y)))); + Interval line = Interval::IntervalFromPoints(lineStartProjected, lineEndProjected); + Interval rect = Interval::IntervalFromRadius(rectPosProjected, rectHalfWidthProjected); + // Allow movement if sprites are moving apart. + if (line.getOverlap(rect).getLength() > 0.0f) { + return false; + } + } + return true; } /** diff --git a/source/sprites/TileManager.h b/source/sprites/TileManager.h index 55373a1..0d116cf 100644 --- a/source/sprites/TileManager.h +++ b/source/sprites/TileManager.h @@ -36,6 +36,8 @@ public: void insertTile(const TilePosition& position, Type type); void removeTile(const TilePosition& position); + bool raycast(const sf::Vector2f& lineStart, + const sf::Vector2f& lineEnd) const; // Private types. private: diff --git a/source/util/Interval.cpp b/source/util/Interval.cpp new file mode 100644 index 0000000..bf1a3a5 --- /dev/null +++ b/source/util/Interval.cpp @@ -0,0 +1,74 @@ +/* + * Interval.cpp + * + * Created on: 03.04.2013 + * Author: Felix + */ + +#include "Interval.h" + +#include + +Interval::Interval(float start, float end) : + start(start), + end(end) { + if (this->start > this->end) { + std::swap(this->start, this->end); + } +}; +/** + * Creates an interval from a center point and a radius. The interval + * ranges from center - radius to center + radius. + */ +Interval +Interval::IntervalFromRadius(float center, float radius) { + return Interval(center - radius, center + radius); +} + +/** + * Creates an Interval from a start and end point. + */ +Interval +Interval::IntervalFromPoints(float start, float end) { + return Interval(start, end); +} + +/** + * Returns the overlap between two intervals, e.g. the overlap between + * intervals (1,3) and (2,4) is (2,3). + */ +Interval +Interval::getOverlap(Interval other) const { + if ((start == other.start) && (end == other.end)) { + return *this; + } + Interval smaller = *this; + Interval bigger = other; + if (smaller.start > bigger.start) { + std::swap(smaller, bigger); + } + Interval iv(0, 0); + if (bigger.start < smaller.end) { + iv.start = bigger.start; + iv.end = smaller.end; + } + else { + iv.start = iv.end = 0.0f; + } + return iv; +} + +/** + * Returns true if the point is inside the interval. + */ +bool +Interval::isInside(float point) const { + return start < point && point < end; +} +/** + * Returns the length of the interval (distance between start and end). + */ +float +Interval::getLength() { + return end - start; +} diff --git a/source/util/Interval.h b/source/util/Interval.h new file mode 100644 index 0000000..22add5b --- /dev/null +++ b/source/util/Interval.h @@ -0,0 +1,27 @@ +/* + * Interval.h + * + * Created on: 03.04.2013 + * Author: Felix + */ + +#ifndef DG_INTERVAL_H_ +#define DG_INTERVAL_H_ + +class Interval { +public: + static Interval IntervalFromRadius(float center, float radius); + static Interval IntervalFromPoints(float start, float end); + Interval getOverlap(Interval other) const; + bool isInside(float point) const; + float getLength(); + +private: + Interval(float start, float end); + +private: + float start; + float end; +}; + +#endif /* DG_INTERVAL_H_ */