feat: removed box collision

too much work for this project
main
Sara 2025-01-27 00:39:53 +01:00
parent 81f32653af
commit e01edd3c83
6 changed files with 54 additions and 118 deletions

View File

@ -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,27 +9,9 @@ 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()];
}
void CollidableNode::set_mask(CollisionMask mask) {
this->mask = mask;

View File

@ -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);

View File

@ -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);

View File

@ -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);
};
}

View File

@ -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;

View File

@ -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();