Improved collision detection by taking movement into account. Fixed long standing...
authorMatthias Braun <matze@braunis.de>
Wed, 24 Nov 2004 03:42:58 +0000 (03:42 +0000)
committerMatthias Braun <matze@braunis.de>
Wed, 24 Nov 2004 03:42:58 +0000 (03:42 +0000)
SVN-Revision: 2166

TODO
lib/gui/menu.cpp
lib/special/collision.cpp
lib/special/collision.h
src/gameloop.cpp
src/object/block.cpp
src/player.cpp
src/sector.cpp
src/trigger/secretarea_trigger.cpp
src/trigger/secretarea_trigger.h

diff --git a/TODO b/TODO
index b979364..0b791af 100644 (file)
--- 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
index e2830d6..3e65955 100644 (file)
@@ -347,6 +347,7 @@ void
 Menu::clear()
 {
   item.clear();
+  active_item = 0;
 }
 
 /* Process actions done on the menu */
index 5219770..b0cb229 100644 (file)
 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;
index 21dc52f..867888c 100644 (file)
@@ -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);                                            
 };
 
 }
index bd8812d..1589565 100644 (file)
@@ -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;
index 3404883..cc9c400 100644 (file)
@@ -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<Player*> (&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;
index 96ac4b5..6ad42c2 100644 (file)
@@ -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?
index bd4f16d..0dd1588 100644 (file)
@@ -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<MovingObject*> (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);
   }
index 855d1e3..9fd16bf 100644 (file)
@@ -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);
index 61ce61c..d16377c 100644 (file)
@@ -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);