Added collision detection for rotated rectangles.

This commit is contained in:
Felix Ableitner 2013-07-09 22:29:56 +02:00
parent db3bf60b2c
commit 16ad937c57
2 changed files with 18 additions and 15 deletions

View file

@ -31,58 +31,60 @@ bool
CollisionModel::testCollision(const Circle& circle, const Rectangle& rect, CollisionModel::testCollision(const Circle& circle, const Rectangle& rect,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond) { sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond) {
sf::Vector2f halfSize = rect.getSize() / 2.0f; sf::Vector2f halfSize = rect.getSize() / 2.0f;
sf::Vector2f circleNewPos = circle.getPosition() + offsetFirst;
sf::Vector2f rectNewPos = rect.getPosition() + offsetSecond; sf::Vector2f rectNewPos = rect.getPosition() + offsetSecond;
sf::Vector2f circleRotatedPos = circle.getPosition() + offsetFirst - rectNewPos;
circleRotatedPos = thor::rotatedVector(circleRotatedPos, -rect.mShape.getRotation());
circleRotatedPos += rectNewPos;
// If circle center is inside rect on x plane, we just take y direction result. // If circle center is inside rect on x plane, we just take y direction result.
if (Interval::IntervalFromRadius(rectNewPos.x, halfSize.x) if (Interval::IntervalFromRadius(rectNewPos.x, halfSize.x)
.isInside(circleNewPos.x)) { .isInside(circleRotatedPos.x)) {
float overlapY = float overlapY =
Interval::IntervalFromRadius(circleNewPos.y, circle.getRadius()) Interval::IntervalFromRadius(circleRotatedPos.y, circle.getRadius())
.getOverlap(Interval::IntervalFromRadius(rectNewPos.y, halfSize.y)) .getOverlap(Interval::IntervalFromRadius(rectNewPos.y, halfSize.y))
.getLength(); .getLength();
offsetFirst.y += (circleNewPos.y > rectNewPos.y) offsetFirst += ((circleRotatedPos.y > rectNewPos.y) ? 1.0f : - 1.0f) *
? overlapY : - overlapY; thor::rotatedVector(sf::Vector2f(0, overlapY), rect.mShape.getRotation());
return overlapY > 0; return overlapY > 0;
} }
// Same here (just switched x/y). // Same here (just switched x/y).
else if (Interval::IntervalFromRadius(rectNewPos.y, halfSize.y) else if (Interval::IntervalFromRadius(rectNewPos.y, halfSize.y)
.isInside(circleNewPos.y)) { .isInside(circleRotatedPos.y)) {
float overlapX = float overlapX =
Interval::IntervalFromRadius(circleNewPos.x, circle.getRadius()) Interval::IntervalFromRadius(circleRotatedPos.x, circle.getRadius())
.getOverlap(Interval::IntervalFromRadius(rectNewPos.x, halfSize.x)) .getOverlap(Interval::IntervalFromRadius(rectNewPos.x, halfSize.x))
.getLength(); .getLength();
offsetFirst.x += (circleNewPos.x > rectNewPos.x) offsetFirst += ((circleRotatedPos.x > rectNewPos.x) ? 1.0f : - 1.0f) *
? overlapX : - overlapX; thor::rotatedVector(sf::Vector2f(overlapX, 0), rect.mShape.getRotation());
return overlapX > 0; return overlapX > 0;
} }
// Test if the circle is colliding with a corner of the rectangle, using // Test if the circle is colliding with a corner of the rectangle, using
// the same method as circle-circle collision (distance to corner instead // the same method as circle-circle collision (distance to corner instead
// of radius. // of radius.
else { else {
sf::Vector2f axis(thor::unitVector(rectNewPos - circleNewPos)); sf::Vector2f axis(thor::unitVector(rectNewPos - circleRotatedPos));
// Use correct vector for corner projections (positive/negative // Use correct vector for corner projections (positive/negative
// direction does not matter). // direction does not matter).
float rectHalfSizeProjected; float rectHalfSizeProjected;
if ((circleNewPos.x > rectNewPos.x && circleNewPos.y > rectNewPos.y) || if ((circleRotatedPos.x > rectNewPos.x && circleRotatedPos.y > rectNewPos.y) ||
(circleNewPos.x < rectNewPos.x && circleNewPos.y < rectNewPos.y)) (circleRotatedPos.x < rectNewPos.x && circleRotatedPos.y < rectNewPos.y))
rectHalfSizeProjected = thor::dotProduct(axis, halfSize); rectHalfSizeProjected = thor::dotProduct(axis, halfSize);
else else
rectHalfSizeProjected = thor::dotProduct(axis, rectHalfSizeProjected = thor::dotProduct(axis,
sf::Vector2f(halfSize.x, -halfSize.y)); sf::Vector2f(halfSize.x, -halfSize.y));
Interval projectedCircle = Interval::IntervalFromRadius( Interval projectedCircle = Interval::IntervalFromRadius(
thor::dotProduct(axis, circleNewPos), thor::dotProduct(axis, circleRotatedPos),
circle.getRadius()); circle.getRadius());
Interval projectedRect = Interval::IntervalFromRadius( Interval projectedRect = Interval::IntervalFromRadius(
thor::dotProduct(axis, rectNewPos), thor::dotProduct(axis, rectNewPos),
rectHalfSizeProjected); rectHalfSizeProjected);
// using -5: works perfectly going between corner/side // using -5: works perfectly going between corner/side
// without -5 works perfectly on corner, but skips when going in between // without -5 works perfectly on corner, but skips when going in between tiles
float overlap = projectedCircle.getOverlap(projectedRect).getLength() - 5; float overlap = projectedCircle.getOverlap(projectedRect).getLength() - 5;
if (overlap > 0) if (overlap > 0)
offsetFirst -= overlap * axis; offsetFirst -= thor::rotatedVector(overlap * axis, rect.mShape.getRotation());
return overlap > 0; return overlap > 0;
} }
} }

View file

@ -64,6 +64,7 @@ protected:
void setPosition(const sf::Vector2f& position); void setPosition(const sf::Vector2f& position);
private: private:
friend class CollisionModel;
friend class World; friend class World;
sf::RectangleShape mShape; sf::RectangleShape mShape;