This repository has been archived on 2019-12-07. You can view files and clone it, but cannot push or open issues or pull requests.
dungeon-gunner/source/abstract/CollisionModel.cpp
Felix Ableitner aa6df65e97 Changed sprites to always render as rectangle.
Also created Rectangle/Circle subclasses for sprite to handle
collision detection and moved collision detection from World to
CollisionModel (static methods).
2013-05-08 00:42:26 +02:00

127 lines
4.7 KiB
C++

/*
* CollisionModel.cpp
*
* Created on: 07.05.2013
* Author: Felix
*/
#include "CollisionModel.h"
#include <SFML/System.hpp>
#include <Thor/Vectors.hpp>
#include "Circle.h"
#include "Rectangle.h"
#include "../util/Interval.h"
CollisionModel::~CollisionModel() {
}
/**
* Tests for collision between rectangle and circle.
*
* @return True if a collision occured.
*/
bool
CollisionModel::testCollision(const Circle& circle, const Rectangle& rect,
int elapsed) {
sf::Vector2f halfsize = rect.getSize() / 2.0f;
sf::Vector2f circlePos = circle.getPosition();
sf::Vector2f rectPos = rect.getPosition();
// Only circle movement as rectangles don't move.
sf::Vector2f circleMovement = circle.getSpeed() * (elapsed / 1000.0f);
// We assume that rectangles are always axis aligned.
float overlapNoMovementX = Interval::IntervalFromRadius(circlePos.x, circle.getRadius())
.getOverlap(Interval::IntervalFromRadius(rectPos.x, halfsize.x)).getLength();
float overlapMovementX = Interval::IntervalFromRadius(circlePos.x + circleMovement.x, circle.getRadius())
.getOverlap(Interval::IntervalFromRadius(rectPos.x, halfsize.x)).getLength();
float overlapNoMovementY = Interval::IntervalFromRadius(circlePos.y, circle.getRadius())
.getOverlap(Interval::IntervalFromRadius(rectPos.y, halfsize.y)).getLength();
float overlapMovementY = Interval::IntervalFromRadius(circlePos.y + circleMovement.y, circle.getRadius())
.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::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.
else if (xyCollisionResult) {
// This is the same as circle-circle collision.
sf::Vector2f axis = circlePos - rectPos;
// If both objects are at the exact same position, allow any
// movement for unstucking.
if (axis == sf::Vector2f())
return true;
axis = thor::unitVector(axis);
float circlePosProjected = thor::dotProduct(axis, circlePos);
float movementProjected = thor::dotProduct(axis, circleMovement);
float rectPosProjected = thor::dotProduct(axis, rectPos);
// 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))));
// Allow movement if sprites are moving apart.
return Interval::IntervalFromRadius(circlePosProjected, circle.getRadius())
.getOverlap(Interval::IntervalFromRadius(rectPosProjected,
rectHalfWidthProjected))
.getLength() <
Interval::IntervalFromRadius(circlePosProjected + movementProjected, circle.getRadius())
.getOverlap(Interval::IntervalFromRadius(rectPosProjected,
rectHalfWidthProjected))
.getLength();
}
// If there is no collision on x and y axis, there can't be one at all.
else {
return false;
}
}
/**
* Tests for collision between two circles.
*
* @return True if a collision occured.
*/
bool
CollisionModel::testCollision(const Circle& first, const Circle& second,
int elapsed) {
sf::Vector2f axis = first.getPosition() - second.getPosition();
// If both objects are at the exact same position, allow any movement for unstucking.
if (axis == sf::Vector2f())
return true;
axis = thor::unitVector(axis);
float centerA = thor::dotProduct(axis, first.getPosition());
float radiusA = first.getRadius();
float movementA = thor::dotProduct(axis, first.getSpeed() * (elapsed / 1000.0f));
float centerB = thor::dotProduct(axis, second.getPosition());
float radiusB = second.getRadius();
float movementB = thor::dotProduct(axis, second.getSpeed() * (elapsed / 1000.0f));
// Allow movement if sprites are moving apart.
return Interval::IntervalFromRadius(centerA, radiusA).getOverlap(
Interval::IntervalFromRadius(centerB, radiusB)).getLength() <
Interval::IntervalFromRadius(centerA + movementA, radiusA).getOverlap(
Interval::IntervalFromRadius(centerB + movementB, radiusB)).getLength();
}
/**
* Tests for collision between two rectangles. Not implemented as these can't
* occur (rectangles can't move).
*
* @return True if a collision occured.
*/
bool
CollisionModel::testCollision(const Rectangle& first, const Rectangle& second,
int elapsed) {
return false;
}