Skip to content

Commit

Permalink
Unisolid hitboxes for objects (#2647)
Browse files Browse the repository at this point in the history
CollisionObject now contains a unisolid property. If it's true, only bottom constraints to the top of the bounding box would be applied to objects, colliding with that object.

The property is also settable from sprite file actions with the name unisolid. Default is false.

Fixes #2644.
  • Loading branch information
Vankata453 authored Oct 7, 2023
1 parent d55e6f5 commit 3aea310
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/collision/collision_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ CollisionObject::CollisionObject(CollisionGroup group, CollisionListener& listen
m_group(group),
m_movement(0.0f, 0.0f),
m_dest(),
m_unisolid(false),
m_objects_hit_bottom(),
m_ground_movement_manager(nullptr)
{
Expand Down
9 changes: 9 additions & 0 deletions src/collision/collision_object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class CollisionObject

void clear_bottom_collision_list();

bool is_unisolid() const { return m_unisolid; }
void set_unisolid(bool unisolid) { m_unisolid = unisolid; }

/** returns the bounding box of the Object */
const Rectf& get_bbox() const
{
Expand Down Expand Up @@ -155,6 +158,12 @@ class CollisionObject
during collision detection */
Rectf m_dest;

/** Determines whether the object is unisolid.
Only bottom constraints to the top of the bounding box would be applied for objects,
colliding with unisolid objects. */
bool m_unisolid;

/** Objects that were touching the top of this object at the last frame,
if this object was static or moving static. */
std::unordered_set<CollisionObject*> m_objects_hit_bottom;
Expand Down
52 changes: 36 additions & 16 deletions src/collision/collision_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ CollisionSystem::draw(DrawingContext& context)
}
const Rectf& rect = object->get_bbox();
context.color().draw_filled_rect(rect, color, LAYER_FOREGROUND1 + 10);

// If unisolid, draw a line on top of the rectangle.
if (object->is_unisolid())
context.color().draw_line(rect.p1(), Vector(rect.get_right(), rect.get_top()),
Color::YELLOW, LAYER_FOREGROUND1 + 11);
}
}

Expand Down Expand Up @@ -131,22 +136,25 @@ collision::Constraints check_collisions(const Vector& obj_movement, const Rectf&

bool shiftout = false;

if (fabsf(obj_movement.y) > fabsf(obj_movement.x)) {
if (ileft < SHIFT_DELTA) {
constraints.constrain_right(grown_other_obj_rect.get_left());
shiftout = true;
} else if (iright < SHIFT_DELTA) {
constraints.constrain_left(grown_other_obj_rect.get_right());
shiftout = true;
}
} else {
// Shiftout bottom/top.
if (itop < SHIFT_DELTA) {
constraints.constrain_bottom(grown_other_obj_rect.get_top());
shiftout = true;
} else if (ibottom < SHIFT_DELTA) {
constraints.constrain_top(grown_other_obj_rect.get_bottom());
shiftout = true;
if (!other_object || !other_object->is_unisolid())
{
if (fabsf(obj_movement.y) > fabsf(obj_movement.x)) {
if (ileft < SHIFT_DELTA) {
constraints.constrain_right(grown_other_obj_rect.get_left());
shiftout = true;
} else if (iright < SHIFT_DELTA) {
constraints.constrain_left(grown_other_obj_rect.get_right());
shiftout = true;
}
} else {
// Shiftout bottom/top.
if (itop < SHIFT_DELTA) {
constraints.constrain_bottom(grown_other_obj_rect.get_top());
shiftout = true;
} else if (ibottom < SHIFT_DELTA) {
constraints.constrain_top(grown_other_obj_rect.get_bottom());
shiftout = true;
}
}
}

Expand All @@ -158,6 +166,18 @@ collision::Constraints check_collisions(const Vector& obj_movement, const Rectf&
return constraints;
}

if (other_object && other_object->is_unisolid())
{
// Constrain only on fall on top of the unisolid object.
if (moving_obj_rect.get_bottom() - obj_movement.y <= grown_other_obj_rect.get_top())
{
constraints.constrain_bottom(grown_other_obj_rect.get_top());
constraints.hit.bottom = true;
}

return constraints;
}

const float vert_penetration = std::min(itop, ibottom);
const float horiz_penetration = std::min(ileft, iright);

Expand Down
2 changes: 2 additions & 0 deletions src/object/conveyor_belt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ ConveyorBelt::draw(DrawingContext &context)
void
ConveyorBelt::update_hitbox()
{
MovingSprite::update_hitbox();

m_col.m_bbox.set_size(m_sprite->get_current_hitbox_width() * static_cast<float>(m_length),
m_sprite->get_current_hitbox_height());
}
Expand Down
1 change: 1 addition & 0 deletions src/object/moving_sprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ void
MovingSprite::update_hitbox()
{
m_col.set_size(m_sprite->get_current_hitbox_width(), m_sprite->get_current_hitbox_height());
m_col.set_unisolid(m_sprite->is_current_hitbox_unisolid());
}

void
Expand Down
6 changes: 6 additions & 0 deletions src/sprite/sprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ Sprite::get_height() const
return static_cast<int>(m_action->surfaces[m_frameidx]->get_height());
}

bool
Sprite::is_current_hitbox_unisolid() const
{
return m_action->hitbox_unisolid;
}

float
Sprite::get_current_hitbox_x_offset() const
{
Expand Down
2 changes: 2 additions & 0 deletions src/sprite/sprite.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class Sprite final
int get_width() const;
int get_height() const;

/** Return the "unisolid" property for the current action's hitbox. */
bool is_current_hitbox_unisolid() const;
/** return x-offset of current action's hitbox, relative to start of image */
float get_current_hitbox_x_offset() const;
/** return y-offset of current action's hitbox, relative to start of image */
Expand Down
2 changes: 2 additions & 0 deletions src/sprite/sprite_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ SpriteData::Action::Action() :
y_offset(0),
hitbox_w(0),
hitbox_h(0),
hitbox_unisolid(false),
fps(10),
loops(-1),
loop_frame(1),
Expand Down Expand Up @@ -127,6 +128,7 @@ SpriteData::parse_action(const ReaderMapping& mapping)
throw std::runtime_error("hitbox should specify 2/4 coordinates");
}
}
mapping.get("unisolid", action->hitbox_unisolid);
mapping.get("fps", action->fps);
if (mapping.get("loops", action->loops))
{
Expand Down
3 changes: 3 additions & 0 deletions src/sprite/sprite_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ class SpriteData final
/** Hitbox height */
float hitbox_h;

/** Whether the hitbox is unisolid */
bool hitbox_unisolid;

/** Frames per second */
float fps;

Expand Down

0 comments on commit 3aea310

Please sign in to comment.