parent
81f32653af
commit
e01edd3c83
|
@ -1,5 +1,6 @@
|
|||
#include "collidable_node.hpp"
|
||||
#include "collision_shape.hpp"
|
||||
#include <SDL2/SDL_log.h>
|
||||
|
||||
namespace ce {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if(this->overlapped_nodes.count(other_shape->get_owner()) != 0) {
|
||||
this->overlap_enter.invoke(shape, other_shape->get_owner(), other_shape);
|
||||
}
|
||||
this->shape_overlaps.insert(other_shape);
|
||||
++this->overlapped_nodes[other_shape->get_owner()];
|
||||
this->overlap_enter.invoke(shape, other_shape->get_owner(), other_shape);
|
||||
}
|
||||
|
||||
void CollidableNode::set_mask(CollisionMask mask) {
|
||||
|
|
|
@ -11,17 +11,13 @@ typedef uint64_t CollisionMask;
|
|||
class CollisionShape;
|
||||
|
||||
class CollidableNode : public Node2D {
|
||||
std::set<CollisionShape*> shape_overlaps{};
|
||||
std::map<CollidableNode*, unsigned> overlapped_nodes{};
|
||||
CollisionMask mask{~0x0u /* all layers by default */};
|
||||
CollisionMask layers{0x1u /* only the first layer is enabled by default */};
|
||||
public:
|
||||
// collision(local_shape, other_node, other_shape)
|
||||
Signal<CollisionShape*, CollidableNode*, CollisionShape*> overlap_enter{};
|
||||
Signal<CollidableNode*> overlap_exit{};
|
||||
public:
|
||||
CollidableNode(std::string const &name, CollisionMask layers, CollisionMask mask);
|
||||
virtual void _tick(double const &delta) override;
|
||||
void add_overlap(CollisionShape *shape, CollisionShape *other);
|
||||
|
||||
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");
|
||||
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)};
|
||||
if(found != this->shapes.end()) {
|
||||
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) {
|
||||
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)};
|
||||
if(found == this->shapes.end()) {
|
||||
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() {
|
||||
for(std::vector<CollisionShape*>::iterator iter{this->shapes.begin()}; iter != this->shapes.end();) {
|
||||
CollisionShape *shape{*iter};
|
||||
this->check_collisions_for(shape, ++iter);
|
||||
for(int i{0}; i < this->shapes.size(); ++i) {
|
||||
CollisionShape *shape{this->shapes[i]};
|
||||
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.
|
||||
// (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) {
|
||||
CollisionShape *other{*iter};
|
||||
for(size_t i{begin+1}; i < this->shapes.size(); ++i) {
|
||||
CollisionShape *other{this->shapes[i]};
|
||||
if(other != shape && other->get_owner() != shape->get_owner() && CollisionShape::shapes_overlap(shape, other)) {
|
||||
if((shape->get_mask() & other->get_layers()) != 0x0u)
|
||||
shape->get_owner()->add_overlap(shape, other);
|
||||
|
|
|
@ -13,7 +13,7 @@ public:
|
|||
void remove_collision_shape(CollisionShape *shape);
|
||||
void check_collisions();
|
||||
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 "core/canvas_engine.hpp"
|
||||
#include <SDL2/SDL_log.h>
|
||||
#include <algorithm>
|
||||
#include <SDL2/SDL_render.h>
|
||||
|
||||
namespace ce {
|
||||
Shape Shape::make_circle(float radius) {
|
||||
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)
|
||||
CollisionShape::CollisionShape(std::string const &name, float radius, float bounce)
|
||||
: Node2D(name)
|
||||
, world{CanvasEngine::get_singleton()->get_collision_world()}
|
||||
, shape{shape} {}
|
||||
, radius{radius}
|
||||
, bounce{bounce} {}
|
||||
|
||||
void CollisionShape::_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) {
|
||||
return lhs->owner != nullptr && rhs->owner != nullptr
|
||||
&& 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) {
|
||||
if(!can_collide(lhs, rhs))
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
float const rad_sum{lhs->radius + rhs->radius};
|
||||
return (lhs->get_global_transform().position - rhs->get_global_transform().position).sqr_magnitude() < rad_sum * rad_sum;
|
||||
}
|
||||
|
||||
bool CollisionShape::overlap_circle_circle(ShapeCircle lhs, Transform lhst, ShapeCircle rhs, Transform rhst) {
|
||||
float const min_dist{lhs.radius + rhs.radius};
|
||||
return Vecf::sqr_distance(lhst.position, rhst.position) <= min_dist * min_dist;
|
||||
}
|
||||
|
||||
bool CollisionShape::overlap_circle_aabb(ShapeCircle lhs, Transform lhst, ShapeAABB rhs, Transform rhst) {
|
||||
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);
|
||||
Vecf CollisionShape::get_escape_vector(CollisionShape const *lhs, CollisionShape const *rhs) {
|
||||
Vecf const difference{lhs->get_global_transform().position - rhs->get_global_transform().position};
|
||||
float const diff_mag{difference.magnitude()};
|
||||
return diff_mag < (lhs->radius + rhs->radius)
|
||||
? (difference / diff_mag) * (lhs->radius + rhs->radius - diff_mag)
|
||||
: Vecf::ZERO;
|
||||
}
|
||||
|
||||
CollidableNode *CollisionShape::get_owner() const {
|
||||
return this->owner;
|
||||
}
|
||||
|
||||
void CollisionShape::set_shape(Shape const &shape) {
|
||||
this->shape = shape;
|
||||
}
|
||||
|
||||
Shape const &CollisionShape::get_shape() const {
|
||||
return this->shape;
|
||||
float CollisionShape::get_radius() const {
|
||||
return this->radius;
|
||||
}
|
||||
|
||||
CollisionMask CollisionShape::get_layers() const {
|
||||
|
@ -127,6 +91,10 @@ CollisionMask CollisionShape::get_mask() const {
|
|||
return this->owner ? this->owner->get_mask() : 0x0u;
|
||||
}
|
||||
|
||||
float CollisionShape::get_bounce() const {
|
||||
return this->bounce;
|
||||
}
|
||||
|
||||
void CollisionShape::register_with_world() {
|
||||
this->world.add_collision_shape(this);
|
||||
this->is_registered = true;
|
||||
|
|
|
@ -8,43 +8,30 @@
|
|||
namespace ce {
|
||||
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 {
|
||||
CollidableNode *owner{nullptr};
|
||||
Shape shape{.shape=Shape::CIRCLE, .circle={.radius=1.f}};
|
||||
float radius;
|
||||
CollisionWorld &world;
|
||||
bool is_registered{false};
|
||||
float bounce{0.5f};
|
||||
public:
|
||||
CollisionShape(std::string const &name, Shape shape);
|
||||
CollisionShape(std::string const &name, float radius, float bounce);
|
||||
|
||||
virtual void _added() 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 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 bool overlap_aabb_aabb(ShapeAABB lhs, Transform lhst, ShapeAABB rhs, Transform rhst);
|
||||
|
||||
static Vecf get_escape_vector(CollisionShape const *lhs, CollisionShape const *rhs);
|
||||
|
||||
|
||||
CollidableNode *get_owner() const;
|
||||
void set_shape(Shape const &shape);
|
||||
Shape const &get_shape() const;
|
||||
float get_radius() const;
|
||||
CollisionMask get_layers() const;
|
||||
CollisionMask get_mask() const;
|
||||
float get_bounce() const;
|
||||
private:
|
||||
void register_with_world();
|
||||
void deregister_with_world();
|
||||
|
|
Loading…
Reference in New Issue