parent
81f32653af
commit
e01edd3c83
|
@ -1,5 +1,6 @@
|
||||||
#include "collidable_node.hpp"
|
#include "collidable_node.hpp"
|
||||||
#include "collision_shape.hpp"
|
#include "collision_shape.hpp"
|
||||||
|
#include <SDL2/SDL_log.h>
|
||||||
|
|
||||||
namespace ce {
|
namespace ce {
|
||||||
CollidableNode::CollidableNode(std::string const &name, CollisionMask layers, CollisionMask mask)
|
CollidableNode::CollidableNode(std::string const &name, CollisionMask layers, CollisionMask mask)
|
||||||
|
@ -8,26 +9,8 @@ CollidableNode::CollidableNode(std::string const &name, CollisionMask layers, Co
|
||||||
this->mask = mask;
|
this->mask = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollidableNode::_tick(double const &delta) {
|
|
||||||
// find nodes that were overlapping last frame but not this frame
|
|
||||||
std::map<CollidableNode*, unsigned>::iterator iterator{this->overlapped_nodes.begin()};
|
|
||||||
while(iterator != this->overlapped_nodes.end()) {
|
|
||||||
std::pair<CollidableNode*, unsigned> pair{*iterator};
|
|
||||||
if(pair.second == 0) {
|
|
||||||
this->overlap_exit.invoke(pair.first);
|
|
||||||
this->overlapped_nodes.erase(iterator);
|
|
||||||
iterator = this->overlapped_nodes.begin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this->shape_overlaps.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollidableNode::add_overlap(CollisionShape *shape, CollisionShape *other_shape) {
|
void CollidableNode::add_overlap(CollisionShape *shape, CollisionShape *other_shape) {
|
||||||
if(this->overlapped_nodes.count(other_shape->get_owner()) != 0) {
|
|
||||||
this->overlap_enter.invoke(shape, other_shape->get_owner(), other_shape);
|
this->overlap_enter.invoke(shape, other_shape->get_owner(), other_shape);
|
||||||
}
|
|
||||||
this->shape_overlaps.insert(other_shape);
|
|
||||||
++this->overlapped_nodes[other_shape->get_owner()];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollidableNode::set_mask(CollisionMask mask) {
|
void CollidableNode::set_mask(CollisionMask mask) {
|
||||||
|
|
|
@ -11,17 +11,13 @@ typedef uint64_t CollisionMask;
|
||||||
class CollisionShape;
|
class CollisionShape;
|
||||||
|
|
||||||
class CollidableNode : public Node2D {
|
class CollidableNode : public Node2D {
|
||||||
std::set<CollisionShape*> shape_overlaps{};
|
|
||||||
std::map<CollidableNode*, unsigned> overlapped_nodes{};
|
|
||||||
CollisionMask mask{~0x0u /* all layers by default */};
|
CollisionMask mask{~0x0u /* all layers by default */};
|
||||||
CollisionMask layers{0x1u /* only the first layer is enabled by default */};
|
CollisionMask layers{0x1u /* only the first layer is enabled by default */};
|
||||||
public:
|
public:
|
||||||
// collision(local_shape, other_node, other_shape)
|
// collision(local_shape, other_node, other_shape)
|
||||||
Signal<CollisionShape*, CollidableNode*, CollisionShape*> overlap_enter{};
|
Signal<CollisionShape*, CollidableNode*, CollisionShape*> overlap_enter{};
|
||||||
Signal<CollidableNode*> overlap_exit{};
|
|
||||||
public:
|
public:
|
||||||
CollidableNode(std::string const &name, CollisionMask layers, CollisionMask mask);
|
CollidableNode(std::string const &name, CollisionMask layers, CollisionMask mask);
|
||||||
virtual void _tick(double const &delta) override;
|
|
||||||
void add_overlap(CollisionShape *shape, CollisionShape *other);
|
void add_overlap(CollisionShape *shape, CollisionShape *other);
|
||||||
|
|
||||||
void set_mask(CollisionMask mask);
|
void set_mask(CollisionMask mask);
|
||||||
|
|
|
@ -10,6 +10,7 @@ void CollisionWorld::add_collision_shape(CollisionShape *shape) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Invalid attempt to add nullptr as collision shape to collision system");
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Invalid attempt to add nullptr as collision shape to collision system");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "adding collision shape %s", shape->get_name().c_str());
|
||||||
std::vector<CollisionShape*>::iterator found{std::find(this->shapes.begin(), this->shapes.end(), shape)};
|
std::vector<CollisionShape*>::iterator found{std::find(this->shapes.begin(), this->shapes.end(), shape)};
|
||||||
if(found != this->shapes.end()) {
|
if(found != this->shapes.end()) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Attempted to add shape that is already in collision system.");
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Attempted to add shape that is already in collision system.");
|
||||||
|
@ -19,6 +20,7 @@ void CollisionWorld::add_collision_shape(CollisionShape *shape) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollisionWorld::remove_collision_shape(CollisionShape *shape) {
|
void CollisionWorld::remove_collision_shape(CollisionShape *shape) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "removing collision shape %s", shape->get_name().c_str());
|
||||||
std::vector<CollisionShape*>::iterator found{std::find(this->shapes.begin(), this->shapes.end(), shape)};
|
std::vector<CollisionShape*>::iterator found{std::find(this->shapes.begin(), this->shapes.end(), shape)};
|
||||||
if(found == this->shapes.end()) {
|
if(found == this->shapes.end()) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Attempted to remove shape that is not in collision system");
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Attempted to remove shape that is not in collision system");
|
||||||
|
@ -28,17 +30,17 @@ void CollisionWorld::remove_collision_shape(CollisionShape *shape) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollisionWorld::check_collisions() {
|
void CollisionWorld::check_collisions() {
|
||||||
for(std::vector<CollisionShape*>::iterator iter{this->shapes.begin()}; iter != this->shapes.end();) {
|
for(int i{0}; i < this->shapes.size(); ++i) {
|
||||||
CollisionShape *shape{*iter};
|
CollisionShape *shape{this->shapes[i]};
|
||||||
this->check_collisions_for(shape, ++iter);
|
this->check_collisions_for(shape, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollisionWorld::check_collisions_for(CollisionShape *shape, std::vector<CollisionShape*>::iterator begin) {
|
void CollisionWorld::check_collisions_for(CollisionShape *shape, size_t begin) {
|
||||||
// check all shapes *after* this one in the shapes list.
|
// check all shapes *after* this one in the shapes list.
|
||||||
// (As the shapes *before* have already been checked, guaranteeing that each pair will only be checked once)
|
// (As the shapes *before* have already been checked, guaranteeing that each pair will only be checked once)
|
||||||
for(std::vector<CollisionShape*>::iterator iter{begin}; iter != this->shapes.end(); ++iter) {
|
for(size_t i{begin+1}; i < this->shapes.size(); ++i) {
|
||||||
CollisionShape *other{*iter};
|
CollisionShape *other{this->shapes[i]};
|
||||||
if(other != shape && other->get_owner() != shape->get_owner() && CollisionShape::shapes_overlap(shape, other)) {
|
if(other != shape && other->get_owner() != shape->get_owner() && CollisionShape::shapes_overlap(shape, other)) {
|
||||||
if((shape->get_mask() & other->get_layers()) != 0x0u)
|
if((shape->get_mask() & other->get_layers()) != 0x0u)
|
||||||
shape->get_owner()->add_overlap(shape, other);
|
shape->get_owner()->add_overlap(shape, other);
|
||||||
|
|
|
@ -13,7 +13,7 @@ public:
|
||||||
void remove_collision_shape(CollisionShape *shape);
|
void remove_collision_shape(CollisionShape *shape);
|
||||||
void check_collisions();
|
void check_collisions();
|
||||||
private:
|
private:
|
||||||
void check_collisions_for(CollisionShape *shape, std::vector<CollisionShape*>::iterator begin);
|
void check_collisions_for(CollisionShape *shape, size_t begin);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,32 +2,14 @@
|
||||||
#include "collidable_node.hpp"
|
#include "collidable_node.hpp"
|
||||||
#include "core/canvas_engine.hpp"
|
#include "core/canvas_engine.hpp"
|
||||||
#include <SDL2/SDL_log.h>
|
#include <SDL2/SDL_log.h>
|
||||||
#include <algorithm>
|
#include <SDL2/SDL_render.h>
|
||||||
|
|
||||||
namespace ce {
|
namespace ce {
|
||||||
Shape Shape::make_circle(float radius) {
|
CollisionShape::CollisionShape(std::string const &name, float radius, float bounce)
|
||||||
return Shape {
|
|
||||||
.shape=Shape::CIRCLE,
|
|
||||||
.circle={
|
|
||||||
.radius=radius
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Shape Shape::make_box(float h_extent, float v_extent) {
|
|
||||||
return Shape {
|
|
||||||
.shape=Shape::AABB,
|
|
||||||
.box={
|
|
||||||
.h_extent=h_extent,
|
|
||||||
.v_extent=v_extent
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
CollisionShape::CollisionShape(std::string const &name, Shape shape)
|
|
||||||
: Node2D(name)
|
: Node2D(name)
|
||||||
, world{CanvasEngine::get_singleton()->get_collision_world()}
|
, world{CanvasEngine::get_singleton()->get_collision_world()}
|
||||||
, shape{shape} {}
|
, radius{radius}
|
||||||
|
, bounce{bounce} {}
|
||||||
|
|
||||||
void CollisionShape::_added() {
|
void CollisionShape::_added() {
|
||||||
this->ce::Node2D::_added();
|
this->ce::Node2D::_added();
|
||||||
|
@ -57,6 +39,22 @@ void CollisionShape::_removed() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollisionShape::_draw(SDL_Renderer *render, ce::Transform const &view_transform) {
|
||||||
|
static const size_t points_c{17};
|
||||||
|
SDL_FPoint points[points_c];
|
||||||
|
Vecf point{this->radius, 0.f};
|
||||||
|
for(size_t i{0}; i < points_c-1; ++i) {
|
||||||
|
points[i] = ((point
|
||||||
|
.rotated(float(i)/float(points_c-1) * M_PI * 2.f)
|
||||||
|
+ this->get_global_transform().position
|
||||||
|
).scaled(view_transform.scale)
|
||||||
|
+ view_transform.position
|
||||||
|
).operator SDL_FPoint();
|
||||||
|
}
|
||||||
|
points[points_c-1] = points[0];
|
||||||
|
SDL_RenderDrawLinesF(render, points, points_c);
|
||||||
|
}
|
||||||
|
|
||||||
bool CollisionShape::can_collide(CollisionShape const *lhs, CollisionShape const *rhs) {
|
bool CollisionShape::can_collide(CollisionShape const *lhs, CollisionShape const *rhs) {
|
||||||
return lhs->owner != nullptr && rhs->owner != nullptr
|
return lhs->owner != nullptr && rhs->owner != nullptr
|
||||||
&& lhs->owner != rhs->owner
|
&& lhs->owner != rhs->owner
|
||||||
|
@ -65,58 +63,24 @@ bool CollisionShape::can_collide(CollisionShape const *lhs, CollisionShape const
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CollisionShape::shapes_overlap(CollisionShape const *lhs, CollisionShape const *rhs) {
|
bool CollisionShape::shapes_overlap(CollisionShape const *lhs, CollisionShape const *rhs) {
|
||||||
if(!can_collide(lhs, rhs))
|
float const rad_sum{lhs->radius + rhs->radius};
|
||||||
return false;
|
return (lhs->get_global_transform().position - rhs->get_global_transform().position).sqr_magnitude() < rad_sum * rad_sum;
|
||||||
Shape const &lshape{lhs->get_shape()};
|
|
||||||
Shape const &rshape{rhs->get_shape()};
|
|
||||||
Transform const &lhst{lhs->get_global_transform()};
|
|
||||||
Transform const &rhst{rhs->get_global_transform()};
|
|
||||||
if(lhs->shape.shape == Shape::CIRCLE) {
|
|
||||||
return rhs->shape.shape == Shape::CIRCLE
|
|
||||||
? overlap_circle_circle(lshape.circle, lhst, rshape.circle, rhst)
|
|
||||||
: overlap_circle_aabb(lshape.circle, lhst, rshape.box, rhst);
|
|
||||||
} else if(lhs->shape.shape == Shape::AABB) {
|
|
||||||
return rhs->shape.shape == Shape::AABB
|
|
||||||
? overlap_aabb_aabb(lshape.box, lhst, rshape.box, rhst)
|
|
||||||
: overlap_circle_aabb(lshape.circle, lhst, rshape.box, rhst);
|
|
||||||
} else {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Attempt to compare shapes with a shape that is not of a valid shape. This should never happen.");
|
|
||||||
#ifdef DEBUG
|
|
||||||
abort();
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CollisionShape::overlap_circle_circle(ShapeCircle lhs, Transform lhst, ShapeCircle rhs, Transform rhst) {
|
Vecf CollisionShape::get_escape_vector(CollisionShape const *lhs, CollisionShape const *rhs) {
|
||||||
float const min_dist{lhs.radius + rhs.radius};
|
Vecf const difference{lhs->get_global_transform().position - rhs->get_global_transform().position};
|
||||||
return Vecf::sqr_distance(lhst.position, rhst.position) <= min_dist * min_dist;
|
float const diff_mag{difference.magnitude()};
|
||||||
}
|
return diff_mag < (lhs->radius + rhs->radius)
|
||||||
|
? (difference / diff_mag) * (lhs->radius + rhs->radius - diff_mag)
|
||||||
bool CollisionShape::overlap_circle_aabb(ShapeCircle lhs, Transform lhst, ShapeAABB rhs, Transform rhst) {
|
: Vecf::ZERO;
|
||||||
Vecf const closest{
|
|
||||||
std::clamp(lhst.position.x, rhst.position.x - rhs.h_extent, rhst.position.x + rhs.h_extent),
|
|
||||||
std::clamp(lhst.position.y, rhst.position.y - rhs.v_extent, rhst.position.y + rhs.v_extent)
|
|
||||||
};
|
|
||||||
return Vecf::sqr_distance(lhst.position, closest) <= lhs.radius * lhs.radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CollisionShape::overlap_aabb_aabb(ShapeAABB lhs, Transform lhst, ShapeAABB rhs, Transform rhst) {
|
|
||||||
ce::Vecf const diff{lhst.position - rhst.position};
|
|
||||||
return std::abs(diff.x) <= (lhs.h_extent + rhs.h_extent)
|
|
||||||
&& std::abs(diff.y) <= (lhs.v_extent + rhs.v_extent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CollidableNode *CollisionShape::get_owner() const {
|
CollidableNode *CollisionShape::get_owner() const {
|
||||||
return this->owner;
|
return this->owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollisionShape::set_shape(Shape const &shape) {
|
float CollisionShape::get_radius() const {
|
||||||
this->shape = shape;
|
return this->radius;
|
||||||
}
|
|
||||||
|
|
||||||
Shape const &CollisionShape::get_shape() const {
|
|
||||||
return this->shape;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CollisionMask CollisionShape::get_layers() const {
|
CollisionMask CollisionShape::get_layers() const {
|
||||||
|
@ -127,6 +91,10 @@ CollisionMask CollisionShape::get_mask() const {
|
||||||
return this->owner ? this->owner->get_mask() : 0x0u;
|
return this->owner ? this->owner->get_mask() : 0x0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float CollisionShape::get_bounce() const {
|
||||||
|
return this->bounce;
|
||||||
|
}
|
||||||
|
|
||||||
void CollisionShape::register_with_world() {
|
void CollisionShape::register_with_world() {
|
||||||
this->world.add_collision_shape(this);
|
this->world.add_collision_shape(this);
|
||||||
this->is_registered = true;
|
this->is_registered = true;
|
||||||
|
|
|
@ -8,43 +8,30 @@
|
||||||
namespace ce {
|
namespace ce {
|
||||||
class CollisionWorld;
|
class CollisionWorld;
|
||||||
|
|
||||||
struct ShapeCircle { float radius; };
|
|
||||||
struct ShapeAABB { float h_extent, v_extent; };
|
|
||||||
|
|
||||||
struct Shape {
|
|
||||||
enum ShapeType { CIRCLE, AABB };
|
|
||||||
static Shape make_circle(float radius);
|
|
||||||
static Shape make_box(float h_extent, float v_extent);
|
|
||||||
|
|
||||||
ShapeType shape;
|
|
||||||
union {
|
|
||||||
ShapeCircle circle;
|
|
||||||
ShapeAABB box;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class CollisionShape : public Node2D {
|
class CollisionShape : public Node2D {
|
||||||
CollidableNode *owner{nullptr};
|
CollidableNode *owner{nullptr};
|
||||||
Shape shape{.shape=Shape::CIRCLE, .circle={.radius=1.f}};
|
float radius;
|
||||||
CollisionWorld &world;
|
CollisionWorld &world;
|
||||||
bool is_registered{false};
|
bool is_registered{false};
|
||||||
|
float bounce{0.5f};
|
||||||
public:
|
public:
|
||||||
CollisionShape(std::string const &name, Shape shape);
|
CollisionShape(std::string const &name, float radius, float bounce);
|
||||||
|
|
||||||
virtual void _added() override;
|
virtual void _added() override;
|
||||||
virtual void _removed() override;
|
virtual void _removed() override;
|
||||||
|
virtual void _draw(SDL_Renderer *render, ce::Transform const &view_transform) override;
|
||||||
|
|
||||||
static bool can_collide(CollisionShape const *lhs, CollisionShape const *rhs);
|
static bool can_collide(CollisionShape const *lhs, CollisionShape const *rhs);
|
||||||
static bool shapes_overlap(CollisionShape const *lhs, CollisionShape const *rhs);
|
static bool shapes_overlap(CollisionShape const *lhs, CollisionShape const *rhs);
|
||||||
static bool overlap_circle_circle(ShapeCircle lhs, Transform lhst, ShapeCircle rhs, Transform rhst);
|
|
||||||
static bool overlap_circle_aabb(ShapeCircle lhs, Transform lhst, ShapeAABB rhs, Transform rhst);
|
static Vecf get_escape_vector(CollisionShape const *lhs, CollisionShape const *rhs);
|
||||||
static bool overlap_aabb_aabb(ShapeAABB lhs, Transform lhst, ShapeAABB rhs, Transform rhst);
|
|
||||||
|
|
||||||
CollidableNode *get_owner() const;
|
CollidableNode *get_owner() const;
|
||||||
void set_shape(Shape const &shape);
|
float get_radius() const;
|
||||||
Shape const &get_shape() const;
|
|
||||||
CollisionMask get_layers() const;
|
CollisionMask get_layers() const;
|
||||||
CollisionMask get_mask() const;
|
CollisionMask get_mask() const;
|
||||||
|
float get_bounce() const;
|
||||||
private:
|
private:
|
||||||
void register_with_world();
|
void register_with_world();
|
||||||
void deregister_with_world();
|
void deregister_with_world();
|
||||||
|
|
Loading…
Reference in New Issue