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 <Thor/Vectors.hpp>
|
||||||
|
|
||||||
|
#include "util/Interval.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a drawable into the group. Drawables should only be handled with shared_ptr.
|
* 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.
|
* 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));
|
float movementB = thor::dotProduct(axis, spriteB->getSpeed() * (elapsed / 1000.0f));
|
||||||
|
|
||||||
// Allow movement if sprites are moving apart.
|
// Allow movement if sprites are moving apart.
|
||||||
return Interval(centerA, radiusA).getOverlap(Interval(centerB, radiusB)).getLength() <
|
return Interval::IntervalFromRadius(centerA, radiusA).getOverlap(
|
||||||
Interval(centerA + movementA, radiusA).getOverlap(
|
Interval::IntervalFromRadius(centerB, radiusB)).getLength() <
|
||||||
Interval(centerB + movementB, radiusB)).getLength();
|
Interval::IntervalFromRadius(centerA + movementA, radiusA).getOverlap(
|
||||||
|
Interval::IntervalFromRadius(centerB + movementB, radiusB)).getLength();
|
||||||
}
|
}
|
||||||
// circle-rect collision
|
// circle-rect collision
|
||||||
if (((spriteA->mShape.type == Sprite::Shape::Type::CIRCLE) &&
|
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);
|
sf::Vector2f circleMovement = circle->getSpeed() * (elapsed / 1000.0f);
|
||||||
|
|
||||||
// We assume that rectangles are always axis aligned.
|
// We assume that rectangles are always axis aligned.
|
||||||
float overlapNoMovementX = Interval(circlePos.x, radius)
|
float overlapNoMovementX = Interval::IntervalFromRadius(circlePos.x, radius)
|
||||||
.getOverlap(Interval (rectPos.x, halfsize.x)).getLength();
|
.getOverlap(Interval::IntervalFromRadius(rectPos.x, halfsize.x)).getLength();
|
||||||
float overlapMovementX = Interval(circlePos.x + circleMovement.x, radius)
|
float overlapMovementX = Interval::IntervalFromRadius(circlePos.x + circleMovement.x, radius)
|
||||||
.getOverlap(Interval (rectPos.x, halfsize.x)).getLength();
|
.getOverlap(Interval::IntervalFromRadius(rectPos.x, halfsize.x)).getLength();
|
||||||
float overlapNoMovementY = Interval(circlePos.y, radius)
|
float overlapNoMovementY = Interval::IntervalFromRadius(circlePos.y, radius)
|
||||||
.getOverlap(Interval (rectPos.y, halfsize.y)).getLength();
|
.getOverlap(Interval::IntervalFromRadius(rectPos.y, halfsize.y)).getLength();
|
||||||
float overlapMovementY = Interval(circlePos.y + circleMovement.y, radius)
|
float overlapMovementY = Interval::IntervalFromRadius(circlePos.y + circleMovement.y, radius)
|
||||||
.getOverlap(Interval (rectPos.y, halfsize.y)).getLength();
|
.getOverlap(Interval::IntervalFromRadius(rectPos.y, halfsize.y)).getLength();
|
||||||
|
|
||||||
bool xyCollisionResult = (((overlapNoMovementX < overlapMovementX) &&
|
bool xyCollisionResult = (((overlapNoMovementX < overlapMovementX) &&
|
||||||
(overlapNoMovementY > 0)) ||
|
(overlapNoMovementY > 0)) ||
|
||||||
((overlapNoMovementY < overlapMovementY) && (overlapNoMovementX > 0)));
|
((overlapNoMovementY < overlapMovementY) && (overlapNoMovementX > 0)));
|
||||||
// If circle center is overlapping rectangle on x or y axis, we can take xyCollisionResult.
|
// If circle center is overlapping rectangle on x or y axis, we can take xyCollisionResult.
|
||||||
if (Interval(rectPos.x, halfsize.x).isInside(circlePos.x) ||
|
if (Interval::IntervalFromRadius(rectPos.x, halfsize.x).isInside(circlePos.x) ||
|
||||||
Interval(rectPos.y, halfsize.y).isInside(circlePos.y)) {
|
Interval::IntervalFromRadius(rectPos.y, halfsize.y).isInside(circlePos.y)) {
|
||||||
return xyCollisionResult;
|
return xyCollisionResult;
|
||||||
}
|
}
|
||||||
// Test if the circle is colliding with a corner of the rectangle.
|
// 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))));
|
sf::Vector2f(halfsize.x, -halfsize.y))));
|
||||||
|
|
||||||
// Allow movement if sprites are moving apart.
|
// Allow movement if sprites are moving apart.
|
||||||
return Interval(circlePosProjected, radius)
|
return Interval::IntervalFromRadius(circlePosProjected, radius)
|
||||||
.getOverlap(Interval(rectPosProjected, rectHalfWidthProjected))
|
.getOverlap(Interval::IntervalFromRadius(rectPosProjected,
|
||||||
|
rectHalfWidthProjected))
|
||||||
.getLength() <
|
.getLength() <
|
||||||
Interval(circlePosProjected + movementProjected, radius)
|
Interval::IntervalFromRadius(circlePosProjected + movementProjected, radius)
|
||||||
.getOverlap(Interval(rectPosProjected, rectHalfWidthProjected))
|
.getOverlap(Interval::IntervalFromRadius(rectPosProjected,
|
||||||
|
rectHalfWidthProjected))
|
||||||
.getLength();
|
.getLength();
|
||||||
}
|
}
|
||||||
// If there is no collision on x and y axis, there can't be one at all.
|
// 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> >
|
std::vector<std::shared_ptr<Character> >
|
||||||
getCharacters(const sf::Vector2f& position, float maxDistance) const;
|
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 types.
|
||||||
private:
|
private:
|
||||||
struct Area;
|
struct Area;
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
|
|
||||||
#include "TileManager.h"
|
#include "TileManager.h"
|
||||||
|
|
||||||
|
#include <Thor/Vectors.hpp>
|
||||||
|
|
||||||
|
#include "../util/Interval.h"
|
||||||
#include "../util/Loader.h"
|
#include "../util/Loader.h"
|
||||||
#include "../util/Yaml.h"
|
#include "../util/Yaml.h"
|
||||||
#include "../World.h"
|
#include "../World.h"
|
||||||
|
@ -104,7 +107,50 @@ TileManager::removeTile(const TilePosition& position) {
|
||||||
mTiles.erase(it);
|
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 insertTile(const TilePosition& position, Type type);
|
||||||
void removeTile(const TilePosition& position);
|
void removeTile(const TilePosition& position);
|
||||||
|
bool raycast(const sf::Vector2f& lineStart,
|
||||||
|
const sf::Vector2f& lineEnd) const;
|
||||||
|
|
||||||
// Private types.
|
// Private types.
|
||||||
private:
|
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