From 22e7accd3e335951d82850ec2e54563e67e1cf97 Mon Sep 17 00:00:00 2001 From: Matthias Braun Date: Wed, 24 Nov 2004 03:42:58 +0000 Subject: [PATCH] Improved collision detection by taking movement into account. Fixed long standing bug where returning from bonuslevel to mainmenu and going back to bonuslevels makes supertux crash SVN-Revision: 2166 --- TODO | 5 +++- lib/gui/menu.cpp | 1 + lib/special/collision.cpp | 57 +++++++++++++++++++++++--------------- lib/special/collision.h | 5 ++-- src/gameloop.cpp | 4 +-- src/object/block.cpp | 22 ++++++--------- src/player.cpp | 10 +++---- src/sector.cpp | 44 +++++++++++++++++------------ src/trigger/secretarea_trigger.cpp | 3 +- src/trigger/secretarea_trigger.h | 2 +- 10 files changed, 88 insertions(+), 65 deletions(-) diff --git a/TODO b/TODO index b979364d8..0b791afec 100644 --- a/TODO +++ b/TODO @@ -41,7 +41,7 @@ L: low priority --Collision Detection Rewrite (all [H])-- * make blocks bounce again - ok * bonusblocks don't always bounce back to their original position (but stay a - few pixels higher) + few pixels higher) - hopefully ok * it's impossible to go into passages that have exactly the size of tux, either reduce collision rectangle by DELTA or round collision coordinates to integers... @@ -73,6 +73,9 @@ L: low priority * rework collision detection to take movement into account - this should fix the egg suddenly turning directions and the somtimes strange behaviour when hitting a block from the side when falling. + - done for rectangles, fixes the issues with blocks and enemies hitting + when they should get squished, still it's not optimal as when hitting 2 + blocks now only 1 gets cleared... * rethink slopes collision feedback... tux becomes too slow when walking up and starts jumping when walking down * think about an attachement mechanism for moving platforms diff --git a/lib/gui/menu.cpp b/lib/gui/menu.cpp index e2830d6fd..3e65955c6 100644 --- a/lib/gui/menu.cpp +++ b/lib/gui/menu.cpp @@ -347,6 +347,7 @@ void Menu::clear() { item.clear(); + active_item = 0; } /* Process actions done on the menu */ diff --git a/lib/special/collision.cpp b/lib/special/collision.cpp index 521977012..b0cb2295d 100644 --- a/lib/special/collision.cpp +++ b/lib/special/collision.cpp @@ -15,38 +15,51 @@ namespace SuperTux { +static const float DELTA = .0001; + bool Collision::rectangle_rectangle(CollisionHit& hit, const Rectangle& r1, - const Rectangle& r2) + const Vector& movement, const Rectangle& r2) { if(r1.p2.x < r2.p1.x || r1.p1.x > r2.p2.x) return false; if(r1.p2.y < r2.p1.y || r1.p1.y > r2.p2.y) return false; - - hit.depth = r1.p2.x - r2.p1.x; - hit.normal.x = -1; - hit.normal.y = 0; - float px2 = r2.p2.x - r1.p1.x; - if(px2 < hit.depth) { - hit.depth = px2; + float xr; + if(movement.x > DELTA) { + hit.depth = r1.p2.x - r2.p1.x; + hit.normal.x = -1; + hit.normal.y = 0; + xr = hit.depth / movement.x; + } else if(movement.x < -DELTA) { + hit.depth = r2.p2.x - r1.p1.x; hit.normal.x = 1; hit.normal.y = 0; + xr = hit.depth / -movement.x; + } else { + xr = FLT_MAX; + if(movement.y > -DELTA && movement.y < DELTA) { + return false; + } } - float py1 = r1.p2.y - r2.p1.y; - if(py1 < hit.depth) { - hit.depth = py1; - hit.normal.x = 0; - hit.normal.y = -1; - } - - float py2 = r2.p2.y - r1.p1.y; - if(py2 < hit.depth) { - hit.depth = py2; - hit.normal.x = 0; - hit.normal.y = 1; + if(movement.y > DELTA) { + float ydepth = r1.p2.y - r2.p1.y; + float yr = ydepth / movement.y; + if(yr < xr) { + hit.depth = ydepth; + hit.normal.x = 0; + hit.normal.y = -1; + } + } else if(movement.y < -DELTA) { + float ydepth = r2.p2.y - r1.p1.y; + float yr = ydepth / -movement.y; + if(yr < xr) { + hit.depth = ydepth; + hit.normal.x = 0; + hit.normal.y = 1; + } } return true; @@ -65,9 +78,9 @@ static void makePlane(const Vector& p1, const Vector& p2, Vector& n, float& c) bool Collision::rectangle_aatriangle(CollisionHit& hit, const Rectangle& rect, - const AATriangle& triangle) + const Vector& movement, const AATriangle& triangle) { - if(!rectangle_rectangle(hit, rect, (const Rectangle&) triangle)) + if(!rectangle_rectangle(hit, rect, movement, (const Rectangle&) triangle)) return false; Vector normal; diff --git a/lib/special/collision.h b/lib/special/collision.h index 21dc52fae..867888c5d 100644 --- a/lib/special/collision.h +++ b/lib/special/collision.h @@ -4,6 +4,7 @@ namespace SuperTux { +class Vector; class Rectangle; class CollisionHit; class AATriangle; @@ -15,13 +16,13 @@ public: * collision and fills in the hit structure then. */ static bool rectangle_rectangle(CollisionHit& hit, const Rectangle& r1, - const Rectangle& r2); + const Vector& movement, const Rectangle& r2); /** does collision detection between a rectangle and an axis aligned triangle * Returns true in case of a collision and fills in the hit structure then. */ static bool rectangle_aatriangle(CollisionHit& hit, const Rectangle& rect, - const AATriangle& triangle); + const Vector& movement, const AATriangle& triangle); }; } diff --git a/src/gameloop.cpp b/src/gameloop.cpp index bd8812d35..15895653e 100644 --- a/src/gameloop.cpp +++ b/src/gameloop.cpp @@ -724,8 +724,8 @@ GameSession::run() global_time += elapsed_time; lastticks = ticks; // 40fps is minimum - if(elapsed_time > .05) - elapsed_time = .05; + if(elapsed_time > .025) + elapsed_time = .025; /* Handle events: */ currentsector->player->input.old_fire = currentsector->player->input.fire; diff --git a/src/object/block.cpp b/src/object/block.cpp index 340488313..cc9c40001 100644 --- a/src/object/block.cpp +++ b/src/object/block.cpp @@ -35,22 +35,18 @@ Block::~Block() HitResponse Block::collision(GameObject& other, const CollisionHit& hitdata) { - if(bouncing) - return FORCE_MOVE; - // TODO kill badguys when bumping them... Player* player = dynamic_cast (&other); - if(!player) - return ABORT_MOVE; - - // collided from below? - if(hitdata.normal.x == 0 && hitdata.normal.y < 0 - && player->get_movement().y < 0) { - hit(*player); + if(player) { + // collided from below? + if(hitdata.normal.x == 0 && hitdata.normal.y < 0 + && player->get_movement().y < 0) { + hit(*player); + } } - return ABORT_MOVE; + return FORCE_MOVE; } void @@ -61,9 +57,9 @@ Block::action(float elapsed_time) float offset = original_y - get_pos().y; if(offset > BOUNCY_BRICK_MAX_OFFSET) { - bounce_dir *= -1; + bounce_dir = BOUNCY_BRICK_SPEED; movement = Vector(0, bounce_dir * elapsed_time); - } else if(offset < -EPSILON) { + } else if(offset < BOUNCY_BRICK_SPEED * elapsed_time && bounce_dir > 0) { movement = Vector(0, offset); bounce_dir = 0; bouncing = false; diff --git a/src/player.cpp b/src/player.cpp index 96ac4b5b3..6ad42c298 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -728,7 +728,7 @@ Player::draw(DrawingContext& context) else tux_body = big_tux; - int layer = LAYER_OBJECTS - 1; + int layer = LAYER_OBJECTS + 10; /* Set Tux sprite action */ if (duck && size == BIG) @@ -858,15 +858,15 @@ Player::draw(DrawingContext& context) && !dying) { if (size == SMALL || duck) - smalltux_star->draw(context, get_pos(), LAYER_OBJECTS + 2); + smalltux_star->draw(context, get_pos(), layer + 5); else - bigtux_star->draw(context, get_pos(), LAYER_OBJECTS + 2); + bigtux_star->draw(context, get_pos(), layer + 5); } if (debug_mode) context.draw_filled_rect(get_pos(), Vector(bbox.get_width(), bbox.get_height()), - Color(75,75,75, 150), LAYER_OBJECTS+1); + Color(75,75,75, 150), LAYER_OBJECTS+20); } HitResponse @@ -882,7 +882,7 @@ Player::collision(GameObject& other, const CollisionHit& hit) physic.set_velocity_y(0); on_ground_flag = true; } else if(hit.normal.y > 0) { // bumped against the roof - physic.set_velocity_y(0); + physic.set_velocity_y(.1); } if(hit.normal.x != 0) { // hit on the side? diff --git a/src/sector.cpp b/src/sector.cpp index bd4f16d2c..0dd15884b 100644 --- a/src/sector.cpp +++ b/src/sector.cpp @@ -668,13 +668,15 @@ Sector::collision_tilemap(MovingObject* object, int depth) break; } - if(Collision::rectangle_aatriangle(temphit, dest, triangle)) { + if(Collision::rectangle_aatriangle(temphit, dest, object->movement, + triangle)) { if(temphit.depth > hit.depth) hit = temphit; } } else { // normal rectangular tile Rectangle rect(x*32, y*32, (x+1)*32, (y+1)*32); - if(Collision::rectangle_rectangle(temphit, dest, rect)) { + if(Collision::rectangle_rectangle(temphit, dest, + object->movement, rect)) { if(temphit.depth > hit.depth) hit = temphit; } @@ -683,7 +685,7 @@ Sector::collision_tilemap(MovingObject* object, int depth) } // did we collide at all? - if(hit.depth == -1) + if(hit.depth < 0) return; // call collision function @@ -708,19 +710,27 @@ Sector::collision_object(MovingObject* object1, MovingObject* object2) dest1.move(object1->get_movement()); Rectangle dest2 = object2->get_bbox(); dest2.move(object2->get_movement()); - if(Collision::rectangle_rectangle(hit, dest1, dest2)) { - HitResponse response = object1->collision(*object2, hit); - if(response == ABORT_MOVE) { - object1->movement = Vector(0, 0); - } else if(response == CONTINUE) { - object1->movement += hit.normal * (hit.depth/2 + .001); - } + + Vector movement = object1->get_movement() - object2->get_movement(); + if(Collision::rectangle_rectangle(hit, dest1, movement, dest2)) { + HitResponse response1 = object1->collision(*object2, hit); + Vector hitnormal1 = hit.normal; hit.normal *= -1; - response = object2->collision(*object1, hit); - if(response == ABORT_MOVE) { - object2->movement = Vector(0, 0); - } else if(response == CONTINUE) { - object2->movement += hit.normal * (hit.depth/2 + .001); + HitResponse response2 = object2->collision(*object1, hit); + + if(response1 != CONTINUE) { + if(response1 == ABORT_MOVE) + object1->movement = Vector(0, 0); + if(response2 == CONTINUE) + object2->movement += hit.normal * (hit.depth + .1); + } else if(response2 != CONTINUE) { + if(response2 == ABORT_MOVE) + object2->movement = Vector(0, 0); + if(response1 == CONTINUE) + object1->movement += hitnormal1 * (hit.depth + .1); + } else { + object1->movement += hitnormal1 * (hit.depth/2 + 1); + object2->movement += hit.normal * (hit.depth/2 + 1); } } } @@ -737,7 +747,7 @@ Sector::collision_handler() MovingObject* movingobject = dynamic_cast (gameobject); if(!movingobject) continue; - + // collision with tilemap if(! (movingobject->movement == Vector(0, 0))) collision_tilemap(movingobject, 0); @@ -755,7 +765,7 @@ Sector::collision_handler() collision_object(movingobject, movingobject2); } - + movingobject->bbox.move(movingobject->get_movement()); movingobject->movement = Vector(0, 0); } diff --git a/src/trigger/secretarea_trigger.cpp b/src/trigger/secretarea_trigger.cpp index 855d1e34e..9fd16bfcd 100644 --- a/src/trigger/secretarea_trigger.cpp +++ b/src/trigger/secretarea_trigger.cpp @@ -13,8 +13,7 @@ SecretAreaTrigger::SecretAreaTrigger(LispReader& reader) reader.read_string("message", message); } -SecretAreaTrigger::SecretAreaTrigger(const Vector& pos, - const std::string& secretarea) +SecretAreaTrigger::SecretAreaTrigger(const Vector& pos) { bbox.set_pos(pos); bbox.set_size(32, 32); diff --git a/src/trigger/secretarea_trigger.h b/src/trigger/secretarea_trigger.h index 61ce61ccf..d16377c29 100644 --- a/src/trigger/secretarea_trigger.h +++ b/src/trigger/secretarea_trigger.h @@ -14,7 +14,7 @@ class SecretAreaTrigger : public TriggerBase, public Serializable { public: SecretAreaTrigger(LispReader& reader); - SecretAreaTrigger(const Vector& pos, const std::string& sequence); + SecretAreaTrigger(const Vector& pos); ~SecretAreaTrigger(); void write(LispWriter& writer); -- 2.11.0