-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a very basic and naive collision detection system. However for the first game project in this engine it should be sufficient and for more complex cases we can replace it with a more robust physics system later.
- Loading branch information
1 parent
619b12e
commit 4077fe2
Showing
6 changed files
with
366 additions
and
227 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#ifndef GL_ADAGIO_COLLISIONRADIUS_H | ||
#define GL_ADAGIO_COLLISIONRADIUS_H | ||
|
||
#include "../../math/Vector2.h" | ||
|
||
struct CollisionRadius { | ||
Adagio::Vector2d offset{0, 0}; | ||
double radius{0}; | ||
}; | ||
|
||
#endif //GL_ADAGIO_COLLISIONRADIUS_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#ifndef GL_ADAGIO_DETECTCOLLISION_H | ||
#define GL_ADAGIO_DETECTCOLLISION_H | ||
|
||
#include "../../event/MessageDispatchService.h" | ||
#include "../../state/GameStats.h" | ||
#include "../../state/StateMachine.h" | ||
#include "../components/CollisionRadius.h" | ||
#include "entt/entt.hpp" | ||
|
||
template<class Target, class Filter> | ||
void DetectCollision(entt::registry ®istry, Adagio::GameStats &stats, | ||
Adagio::StateMachine *state) { | ||
auto getCollisionCenter = [®istry](const CollisionRadius &collision, entt::entity entity) { | ||
Adagio::Vector2d collisionCenter = collision.offset; | ||
Position *pos = registry.try_get<Position>(entity); | ||
if (pos) { | ||
collisionCenter += pos->position; | ||
} | ||
return collisionCenter; | ||
}; | ||
|
||
auto view = registry.view<CollisionRadius, Target>(); | ||
auto filterView = registry.view<CollisionRadius, Filter>(); | ||
for (auto entity: view) { | ||
const CollisionRadius &collision = registry.get<CollisionRadius>(entity); | ||
const Adagio::Vector2d collisionCenter = getCollisionCenter(collision, entity); | ||
for (auto id: filterView) { | ||
const CollisionRadius &otherCollision = registry.get<CollisionRadius>(id); | ||
const Adagio::Vector2d otherCollisionCenter = getCollisionCenter(otherCollision, id) - collisionCenter; | ||
const double minDistance = collision.radius + otherCollision.radius; | ||
if (otherCollisionCenter.magnitudeSquared() <= minDistance * minDistance) { | ||
registry.ctx().get<Adagio::MessageDispatchService *>()->dispatch(entity, id, "COLLISION"_hs); | ||
} | ||
} | ||
} | ||
} | ||
|
||
#endif //GL_ADAGIO_DETECTCOLLISION_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,101 +1,116 @@ | ||
namespace Adagio { | ||
template <typename T> Vector2<T>::Vector2() { | ||
this->x = 0; | ||
this->y = 0; | ||
} | ||
|
||
template <typename T> Vector2<T>::Vector2(T x, T y) { | ||
this->x = x; | ||
this->y = y; | ||
} | ||
|
||
template <typename T> | ||
template <typename U> | ||
Vector2<T>::Vector2(const Vector2<U> &v) { | ||
this->x = static_cast<T>(v.x); | ||
this->y = static_cast<T>(v.y); | ||
} | ||
|
||
template <typename T> double Vector2<T>::magnitude() const { | ||
return sqrt(x * x + y * y); | ||
} | ||
|
||
template <typename T> std::string Vector2<T>::to_string() const { | ||
std::stringstream sstream; | ||
sstream << *this; | ||
std::string str = sstream.str(); | ||
return str; | ||
} | ||
|
||
template <typename T> Vector2<T> Vector2<T>::normalized() const { | ||
double length = magnitude(); | ||
return Vector2<T>{x / length, y / length}; | ||
} | ||
|
||
template <typename T> Vector2<T> Vector2<T>::floor() const { | ||
return Vector2<T>{std::floor(x), std::floor(y)}; | ||
} | ||
|
||
template <typename T> | ||
void Vector2<T>::clamp(const Vector2<T> &lower, const Vector2<T> &upper) { | ||
x = std::max(std::min(upper.x, x), lower.x); | ||
y = std::max(std::min(upper.y, y), lower.y); | ||
} | ||
|
||
template <typename T> | ||
Vector2<T> operator+(const Vector2<T> &left, Vector2<T> right) { | ||
return Vector2<T>{left.x + right.x, left.y + right.y}; | ||
} | ||
|
||
template <typename T> | ||
Vector2<T> operator-(const Vector2<T> &left, Vector2<T> right) { | ||
return Vector2<T>{left.x - right.x, left.y - right.y}; | ||
} | ||
|
||
template <typename T> | ||
Vector2<T> operator+=(Vector2<T> &left, Vector2<T> right) { | ||
left.x += right.x; | ||
left.y += right.y; | ||
|
||
return left; | ||
} | ||
|
||
template <typename T> | ||
Vector2<T> operator-=(Vector2<T> &left, Vector2<T> right) { | ||
left.x -= right.x; | ||
left.y -= right.y; | ||
|
||
return left; | ||
} | ||
|
||
template <typename T> Vector2<T> operator*(const Vector2<T> &left, T right) { | ||
return Vector2<T>{left.x * right, left.y * right}; | ||
} | ||
|
||
template <typename T> Vector2<T> operator*(T left, const Vector2<T> &right) { | ||
return Vector2<T>{left * right.x, left * right.y}; | ||
} | ||
|
||
template <typename T> Vector2<T> operator/(const Vector2<T> &left, T right) { | ||
return Vector2<T>{left.x / right, left.y / right}; | ||
} | ||
|
||
template <typename T> Vector2<T> operator/(T left, const Vector2<T> &right) { | ||
return Vector2<T>{left / right.x, left / right.y}; | ||
} | ||
|
||
template <typename T> | ||
bool operator==(const Vector2<T> &left, const Vector2<T> &right) { | ||
return left.x == right.x && left.y == right.y; | ||
} | ||
|
||
template <typename T> | ||
bool operator!=(const Vector2<T> &left, const Vector2<T> &right) { | ||
return left.x != right.x && left.y != right.y; | ||
} | ||
|
||
template <typename T> | ||
inline std::ostream &operator<<(std::ostream &left, const Vector2<T> &right) { | ||
return left << "Vector2(" << right.x << "," << right.y << ")"; | ||
} | ||
template<typename T> | ||
Vector2<T>::Vector2() { | ||
this->x = 0; | ||
this->y = 0; | ||
} | ||
|
||
template<typename T> | ||
Vector2<T>::Vector2(T x, T y) { | ||
this->x = x; | ||
this->y = y; | ||
} | ||
|
||
template<typename T> | ||
template<typename U> | ||
Vector2<T>::Vector2(const Vector2<U> &v) { | ||
this->x = static_cast<T>(v.x); | ||
this->y = static_cast<T>(v.y); | ||
} | ||
|
||
template<typename T> | ||
double Vector2<T>::magnitude() const { | ||
return sqrt(x * x + y * y); | ||
} | ||
|
||
template<typename T> | ||
double Vector2<T>::magnitudeSquared() const { | ||
return x * x + y * y; | ||
} | ||
|
||
template<typename T> | ||
std::string Vector2<T>::to_string() const { | ||
std::stringstream sstream; | ||
sstream << *this; | ||
std::string str = sstream.str(); | ||
return str; | ||
} | ||
|
||
template<typename T> | ||
Vector2<T> Vector2<T>::normalized() const { | ||
double length = magnitude(); | ||
return Vector2<T>{x / length, y / length}; | ||
} | ||
|
||
template<typename T> | ||
Vector2<T> Vector2<T>::floor() const { | ||
return Vector2<T>{std::floor(x), std::floor(y)}; | ||
} | ||
|
||
template<typename T> | ||
void Vector2<T>::clamp(const Vector2<T> &lower, const Vector2<T> &upper) { | ||
x = std::max(std::min(upper.x, x), lower.x); | ||
y = std::max(std::min(upper.y, y), lower.y); | ||
} | ||
|
||
template<typename T> | ||
Vector2<T> operator+(const Vector2<T> &left, Vector2<T> right) { | ||
return Vector2<T>{left.x + right.x, left.y + right.y}; | ||
} | ||
|
||
template<typename T> | ||
Vector2<T> operator-(const Vector2<T> &left, Vector2<T> right) { | ||
return Vector2<T>{left.x - right.x, left.y - right.y}; | ||
} | ||
|
||
template<typename T> | ||
Vector2<T> operator+=(Vector2<T> &left, Vector2<T> right) { | ||
left.x += right.x; | ||
left.y += right.y; | ||
|
||
return left; | ||
} | ||
|
||
template<typename T> | ||
Vector2<T> operator-=(Vector2<T> &left, Vector2<T> right) { | ||
left.x -= right.x; | ||
left.y -= right.y; | ||
|
||
return left; | ||
} | ||
|
||
template<typename T> | ||
Vector2<T> operator*(const Vector2<T> &left, T right) { | ||
return Vector2<T>{left.x * right, left.y * right}; | ||
} | ||
|
||
template<typename T> | ||
Vector2<T> operator*(T left, const Vector2<T> &right) { | ||
return Vector2<T>{left * right.x, left * right.y}; | ||
} | ||
|
||
template<typename T> | ||
Vector2<T> operator/(const Vector2<T> &left, T right) { | ||
return Vector2<T>{left.x / right, left.y / right}; | ||
} | ||
|
||
template<typename T> | ||
Vector2<T> operator/(T left, const Vector2<T> &right) { | ||
return Vector2<T>{left / right.x, left / right.y}; | ||
} | ||
|
||
template<typename T> | ||
bool operator==(const Vector2<T> &left, const Vector2<T> &right) { | ||
return left.x == right.x && left.y == right.y; | ||
} | ||
|
||
template<typename T> | ||
bool operator!=(const Vector2<T> &left, const Vector2<T> &right) { | ||
return left.x != right.x && left.y != right.y; | ||
} | ||
|
||
template<typename T> | ||
inline std::ostream &operator<<(std::ostream &left, const Vector2<T> &right) { | ||
return left << "Vector2(" << right.x << "," << right.y << ")"; | ||
} | ||
} // namespace Adagio |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
#include "../../../src/game/components/events/hooks/MessageInboxHooks.h" | ||
#include "../../../src/game/components/events/MessageInbox.h" | ||
#include "../../../src/game/components/CollisionRadius.h" | ||
#include "../../../src/game/components/Position.h" | ||
#include "../../../src/game/systems/DetectCollision.h" | ||
#include "../EcsTestingHarness.h" | ||
#include <catch2/catch.hpp> | ||
|
||
static EcsTestingHarness harness; | ||
|
||
struct TagA { | ||
}; | ||
struct TagB { | ||
}; | ||
struct TestStruct { | ||
bool importantData{false}; | ||
}; | ||
|
||
TEST_CASE("DetectCollision: No components defined", "[system][Collision]") { | ||
harness.reset(); | ||
REQUIRE_NOTHROW(DetectCollision<TagA, TagB>(harness.registry, harness.stats, | ||
harness.stateMachine)); | ||
REQUIRE_NOTHROW(DetectCollision<TagA, TestStruct>(harness.registry, harness.stats, | ||
harness.stateMachine)); | ||
} | ||
|
||
TEST_CASE("Detect Collision: Fires if components fully intersect", "[system][Collision]") { | ||
harness.reset(); | ||
harness.registry.on_construct<MessageInbox>().connect<&RegisterInboxWithMessageService>(); | ||
harness.registry.on_destroy<MessageInbox>().connect<&UnregisterInboxWithMessageService>(); | ||
auto entityA = harness.registry.create(); | ||
auto entityB = harness.registry.create(); | ||
harness.registry.emplace<Position>(entityA, Adagio::Vector2d{0, 0}); | ||
auto &positionB = harness.registry.emplace<Position>(entityB, Adagio::Vector2d{0, 0}); | ||
harness.registry.emplace<CollisionRadius>(entityA, Adagio::Vector2d{0, 0}, 1); | ||
harness.registry.emplace<CollisionRadius>(entityB, Adagio::Vector2d{0, 0}, 1); | ||
harness.registry.emplace<TagA>(entityA); | ||
harness.registry.emplace<TagB>(entityB); | ||
auto &inbox = harness.registry.emplace<MessageInbox>(entityA); | ||
|
||
harness.testSystemFrame(DetectCollision<TagA, TagB>); | ||
|
||
REQUIRE(inbox.messages.size() == 1); | ||
|
||
SECTION("Include both radii in the equation") { | ||
inbox.messages.shift(); | ||
positionB.position.x = 1.5; | ||
|
||
harness.testSystemFrame(DetectCollision<TagA, TagB>); | ||
|
||
REQUIRE(inbox.messages.size() == 1); | ||
} | ||
|
||
SECTION("Do not fire once an entity is too far away") { | ||
inbox.messages.shift(); | ||
positionB.position.x = 2.5; | ||
|
||
harness.testSystemFrame(DetectCollision<TagA, TagB>); | ||
|
||
REQUIRE(inbox.messages.empty()); | ||
} | ||
} |
Oops, something went wrong.