started work on a collision grid class to speedup collision detection. Doesn't work...
authorMatthias Braun <matze@braunis.de>
Mon, 29 Nov 2004 16:03:33 +0000 (16:03 +0000)
committerMatthias Braun <matze@braunis.de>
Mon, 29 Nov 2004 16:03:33 +0000 (16:03 +0000)
SVN-Revision: 2227

TODO
lib/special/moving_object.h
src/collision_grid.cpp [new file with mode: 0644]
src/collision_grid.h [new file with mode: 0644]
src/object/oneup.cpp
src/sector.cpp
src/sector.h

diff --git a/TODO b/TODO
index 49cd9f6..a6bc743 100644 (file)
--- a/TODO
+++ b/TODO
@@ -117,6 +117,8 @@ L: low priority
      - New forest tileset
      - Badguy sprites
      - Tux's buttjump animation
+[M] after picking up a star the salcon music isn't played anymore, but the
+    levelmusic restarts.
 
 [M] Save score on per-level basis to make high-score
 [M] Save time on per-level basis to make low-time-score
index d04c3ef..b5c4764 100644 (file)
@@ -26,6 +26,7 @@
 #include "math/rectangle.h"
 
 class Sector;
+class CollisionGrid;
 
 namespace SuperTux
   {
diff --git a/src/collision_grid.cpp b/src/collision_grid.cpp
new file mode 100644 (file)
index 0000000..9e11b60
--- /dev/null
@@ -0,0 +1,233 @@
+#include <config.h>
+
+#include <iostream>
+#include "collision_grid.h"
+#include "special/collision.h"
+#include "sector.h"
+
+static const float DELTA = .001;
+
+CollisionGrid::CollisionGrid(float newwidth, float newheight)
+  : width(newwidth), height(newheight), cell_width(128), cell_height(128)
+{
+  cells_x = size_t(width / cell_width) + 1;
+  cells_y = size_t(height / cell_height) + 1;
+  grid.resize(cells_x * cells_y);
+}
+
+CollisionGrid::~CollisionGrid()
+{
+  for(GridEntries::iterator i = grid.begin(); i != grid.end(); ++i) {
+    GridEntry* entry = *i;
+    while(entry) {
+      GridEntry* nextentry = entry->next;
+      delete entry;
+      entry = nextentry;
+    }
+  }
+}
+
+void
+CollisionGrid::add_object(MovingObject* object)
+{
+#ifdef DEBUG
+  // make sure the object isn't already in the grid
+  for(Objects::iterator i = objects.begin(); i != objects.end(); ++i) {
+    ObjectWrapper* wrapper = *i;
+    if(wrapper->object == object)
+      assert(false);
+  }
+  assert(object != 0);
+#endif
+  
+  ObjectWrapper* wrapper = new ObjectWrapper;
+  wrapper->object = object;
+  wrapper->timestamp = 0;
+  wrapper->dest = object->bbox;
+  objects.push_back(wrapper);
+  wrapper->id = objects.size()-1;
+  
+  const Rectangle& bbox = object->bbox;
+  for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
+    for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
+      int gridx = int(x / cell_width);
+      int gridy = int(y / cell_height);
+      if(gridx < 0 || gridy < 0 
+          || gridx >= int(cells_x) || gridy >= int(cells_y)) {
+        std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+        continue;
+      }
+      GridEntry* entry = new GridEntry;
+      entry->object_wrapper = wrapper;
+      entry->next = grid[gridy*cells_x + gridx];
+      grid[gridy*cells_x + gridx] = entry;
+    }
+  }
+}
+
+void
+CollisionGrid::remove_object(MovingObject* object)
+{
+  ObjectWrapper* wrapper = 0;
+  for(Objects::iterator i = objects.begin(); i != objects.end(); ++i) {
+    if((*i)->object == object) {
+      wrapper = *i;
+      objects.erase(i);
+      break;
+    }
+  }
+  assert(wrapper != 0);
+  
+  const Rectangle& bbox = wrapper->dest;
+  for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
+    for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
+      int gridx = int(x / cell_width);
+      int gridy = int(y / cell_height);
+      if(gridx < 0 || gridy < 0 
+          || gridx >= int(cells_x) || gridy >= int(cells_y)) {
+        std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+        continue;
+      }
+      remove_object_from_gridcell(gridy*cells_x + gridx, object);
+    }
+  }
+
+  delete wrapper;
+}
+
+void
+CollisionGrid::move_object(MovingObject* object)
+{
+  const Rectangle& bbox = object->bbox;
+  for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
+    for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
+      int gridx = int(x / cell_width);
+      int gridy = int(y / cell_height);
+      if(gridx < 0 || gridy < 0 
+          || gridx >= int(cells_x) || gridy >= int(cells_y)) {
+        std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+        continue;
+      }
+      // TODO
+    }
+  }
+}
+
+void
+CollisionGrid::check_collisions()
+{
+  for(Objects::iterator i = objects.begin(); i != objects.end(); ++i) {
+    ObjectWrapper* wrapper = *i;
+    MovingObject* object = wrapper->object;
+    if(!object->is_valid())
+      continue;
+    if(object->get_flags() & GameObject::FLAG_NO_COLLDET) {
+      object->bbox.move(object->movement);
+      object->movement = Vector(0, 0);
+      continue;
+    }
+
+    // hack for now...
+    Sector::current()->collision_tilemap(object, 0);
+    
+    collide_object(wrapper);
+
+    object->bbox.move(object->get_movement());
+    object->movement = Vector(0, 0);
+  }
+}
+
+void
+CollisionGrid::collide_object(ObjectWrapper* wrapper)
+{
+  static int timestamp = 0;
+  timestamp++;
+
+  const Rectangle& bbox = wrapper->object->bbox;
+  for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
+    for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
+      int gridx = int(x / cell_width);
+      int gridy = int(y / cell_height);
+      if(gridx < 0 || gridy < 0 
+          || gridx >= int(cells_x) || gridy >= int(cells_y)) {
+        std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+        continue;
+      }
+  
+      for(GridEntry* entry = grid[gridy*cells_x + gridx]; entry;
+          entry = entry->next) {
+        ObjectWrapper* wrapper2 = entry->object_wrapper;
+        // only check each object once (even if it is in multiple cells)
+        if(wrapper2->timestamp == timestamp)
+          continue;
+        // don't collide with objects we already collided with
+        if(wrapper2->id <= wrapper->id)
+          continue;
+
+        wrapper->timestamp = timestamp;
+        collide_object_object(wrapper, wrapper2);
+      }
+    }
+  }
+}
+
+void
+CollisionGrid::collide_object_object(ObjectWrapper* wrapper,
+    ObjectWrapper* wrapper2)
+{
+  CollisionHit hit;
+  MovingObject* object1 = wrapper->object;
+  MovingObject* object2 = wrapper2->object;
+  
+  Rectangle dest1 = object1->get_bbox();
+  dest1.move(object1->get_movement());
+  Rectangle dest2 = object2->get_bbox();
+  dest2.move(object2->get_movement());
+
+  Vector movement = object1->get_movement() - object2->get_movement();
+  if(Collision::rectangle_rectangle(hit, dest1, movement, dest2)) {
+    HitResponse response1 = object1->collision(*object2, hit);
+    hit.normal *= -1;
+    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 + DELTA);
+    } else if(response2 != CONTINUE) {
+      if(response2 == ABORT_MOVE)
+        object2->movement = Vector(0, 0);
+      if(response1 == CONTINUE)
+        object1->movement += -hit.normal * (hit.depth + DELTA);
+    } else {
+      object1->movement += -hit.normal * (hit.depth/2 + DELTA);
+      object2->movement += hit.normal * (hit.depth/2 + DELTA);
+    }
+  }
+}
+
+void
+CollisionGrid::remove_object_from_gridcell(int gridcell, MovingObject* object)
+{
+  GridEntry* lastentry = 0;
+  GridEntry* entry = grid[gridcell];
+
+  while(entry) {
+    if(entry->object_wrapper->object == object) {
+      if(lastentry == 0) {
+        grid[gridcell] = entry->next;
+      } else {
+        lastentry->next = entry->next;
+      }
+      delete entry;
+      return;
+    }
+
+    lastentry = entry;
+    entry = entry->next;
+  };
+
+  std::cerr << "Couldn't find object in cell.\n";
+}
+
diff --git a/src/collision_grid.h b/src/collision_grid.h
new file mode 100644 (file)
index 0000000..09cc87a
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef __COLLISION_GRID_H__
+#define __COLLISION_GRID_H__
+
+#include <vector>
+#include "special/moving_object.h"
+
+using namespace SuperTux;
+
+/**
+ * A rectangular grid to keep track of all moving game objects. It allows fast
+ * queries for all objects in a rectangular area.
+ */
+class CollisionGrid
+{
+public:
+  CollisionGrid(float width, float height);
+  ~CollisionGrid();
+
+  void add_object(MovingObject* object);
+  void remove_object(MovingObject* object);
+  void move_object(MovingObject* object);
+
+  void check_collisions();
+
+private:
+  struct ObjectWrapper
+  {
+    MovingObject* object;
+    Rectangle dest;
+    /** (pseudo) timestamp. When reading from the grid the timestamp is
+     * changed so that you can easily avoid reading an object multiple times
+     * when it is in several cells that you check.
+     */
+    int timestamp;
+    /// index in the objects vector
+    int id;
+  };
+  /** Element for the single linked list in each grid cell */
+  struct GridEntry
+  {
+    GridEntry* next;
+    ObjectWrapper* object_wrapper;
+  };
+
+  void remove_object_from_gridcell(int gridcell, MovingObject* object);
+  void collide_object(ObjectWrapper* wrapper);
+  void collide_object_object(ObjectWrapper* wrapper, ObjectWrapper* wrapper2);
+  
+  typedef std::vector<GridEntry*> GridEntries;
+  GridEntries grid;
+  typedef std::vector<ObjectWrapper*> Objects;
+  Objects objects;
+  size_t cells_x, cells_y;
+  float width;
+  float height;
+  float cell_width;
+  float cell_height;
+};
+
+extern CollisionGrid* bla;
+
+#endif
+
index 0f2a410..a576bcf 100644 (file)
@@ -4,6 +4,7 @@
 #include "resources.h"
 #include "player.h"
 #include "scene.h"
+#include "sector.h"
 #include "special/sprite_manager.h"
 #include "video/drawing_context.h"
 
@@ -23,6 +24,9 @@ OneUp::~OneUp()
 void
 OneUp::action(float elapsed_time)
 {
+  if(!Sector::current()->inside(bbox))
+    remove_me();
+
   movement = physic.get_movement(elapsed_time); 
 }
 
index 6b3abe1..2a56628 100644 (file)
@@ -42,6 +42,7 @@
 #include "gameloop.h"
 #include "resources.h"
 #include "statistics.h"
+#include "collision_grid.h"
 #include "special/collision.h"
 #include "math/rectangle.h"
 #include "math/aatriangle.h"
@@ -73,6 +74,8 @@ Sector::Sector()
   song_title = "Mortimers_chipdisko.mod";
   player = new Player();
   add_object(player);
+
+  grid = new CollisionGrid(32000, 32000);
 }
 
 Sector::~Sector()
@@ -80,6 +83,8 @@ Sector::~Sector()
   update_game_objects();
   assert(gameobjects_new.size() == 0);
 
+  delete grid;
+
   for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
       ++i) {
     delete *i;
@@ -485,29 +490,42 @@ Sector::update_game_objects()
   /** cleanup marked objects */
   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
       i != gameobjects.end(); /* nothing */) {
-    if((*i)->is_valid() == false) {
-      Bullet* bullet = dynamic_cast<Bullet*> (*i);
-      if(bullet) {
-        bullets.erase(
-            std::remove(bullets.begin(), bullets.end(), bullet),
-            bullets.end());
-      }
-      delete *i;
-      i = gameobjects.erase(i);
-    } else {
+    GameObject* object = *i;
+    
+    if(object->is_valid()) {
       ++i;
+      continue;
+    }
+    
+    Bullet* bullet = dynamic_cast<Bullet*> (object);
+    if(bullet) {
+      bullets.erase(
+          std::remove(bullets.begin(), bullets.end(), bullet),
+          bullets.end());
+    }
+    MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
+    if(movingobject) {
+      grid->remove_object(movingobject);
     }
+    delete *i;
+    i = gameobjects.erase(i);
   }
 
   /* add newly created objects */
   for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
       i != gameobjects_new.end(); ++i)
   {
-    Bullet* bullet = dynamic_cast<Bullet*> (*i);
+    GameObject* object = *i;
+    
+    Bullet* bullet = dynamic_cast<Bullet*> (object);
     if(bullet)
       bullets.push_back(bullet);
 
-    TileMap* tilemap = dynamic_cast<TileMap*> (*i);
+    MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
+    if(movingobject)
+      grid->add_object(movingobject);
+    
+    TileMap* tilemap = dynamic_cast<TileMap*> (object);
     if(tilemap && tilemap->is_solid()) {
       if(solids == 0) {
         solids = tilemap;
@@ -516,7 +534,7 @@ Sector::update_game_objects()
       }
     }
 
-    Camera* camera = dynamic_cast<Camera*> (*i);
+    Camera* camera = dynamic_cast<Camera*> (object);
     if(camera) {
       if(this->camera != 0) {
         std::cerr << "Warning: Multiple cameras added. Ignoring.";
@@ -525,7 +543,7 @@ Sector::update_game_objects()
       this->camera = camera;
     }
 
-    gameobjects.push_back(*i);
+    gameobjects.push_back(object);
   }
   gameobjects_new.clear();
 }
@@ -674,6 +692,9 @@ Sector::collision_object(MovingObject* object1, MovingObject* object2)
 void
 Sector::collision_handler()
 {
+#if 0
+  grid->check_collisions();
+#else
   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
       i != gameobjects.end(); ++i) {
     GameObject* gameobject = *i;
@@ -709,6 +730,7 @@ Sector::collision_handler()
     movingobject->bbox.move(movingobject->get_movement());
     movingobject->movement = Vector(0, 0);
   }
+#endif
 }
 
 bool
index ee08514..5fb49f6 100644 (file)
@@ -40,19 +40,11 @@ class Lisp;
 class Writer;
 }
 
-class InteractiveObject;
-class Background;
 class Player;
 class Camera;
-class Trampoline;
-class FlyingPlatform;
 class TileMap;
-class Upgrade;
 class Bullet;
-class SmokeCloud;
-class Particles;
-class BadGuy;
-class Tile;
+class CollisionGrid;
 
 struct SpawnPoint
 {
@@ -115,8 +107,10 @@ public:
   /** Get total number of badguys */
   int get_total_badguys();
 
-private:
+  // make this private again soon
   void collision_tilemap(MovingObject* object, int depth);
+
+private:
   void collision_object(MovingObject* object1, MovingObject* object2);
   
   void load_music();
@@ -142,8 +136,6 @@ private:
   std::vector<Bullet*> bullets;
 
 public: // TODO make this private again
-  typedef std::vector<InteractiveObject*> InteractiveObjects;
-  InteractiveObjects interactive_objects;
   typedef std::vector<GameObject*> GameObjects;
   GameObjects gameobjects;
 
@@ -157,6 +149,8 @@ private:
   SpawnPoints spawnpoints;
 
   int currentmusic;
+
+  CollisionGrid* grid;
 };
 
 #endif