Added raycast.
This commit is contained in:
parent
7ebf912aad
commit
a094c01d55
6 changed files with 171 additions and 78 deletions
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include <Thor/Vectors.hpp>
|
||||
|
||||
#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<Sprite> 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<Sprite> 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<Sprite> 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;
|
||||
}
|
||||
|
|
|
@ -35,18 +35,6 @@ public:
|
|||
std::vector<std::shared_ptr<Character> >
|
||||
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;
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
#include "TileManager.h"
|
||||
|
||||
#include <Thor/Vectors.hpp>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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:
|
||||
|
|
74
source/util/Interval.cpp
Normal file
74
source/util/Interval.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Interval.cpp
|
||||
*
|
||||
* Created on: 03.04.2013
|
||||
* Author: Felix
|
||||
*/
|
||||
|
||||
#include "Interval.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
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;
|
||||
}
|
27
source/util/Interval.h
Normal file
27
source/util/Interval.h
Normal file
|
@ -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_ */
|
Reference in a new issue