diff skaapsteker/physics.py @ 627:35919d12b792

Path-based collision minimisation and axis-projection backout.
author Jeremy Thurgood <firxen@gmail.com>
date Sat, 07 May 2011 20:28:06 +0200
parents 1abb53ae1a6a
children 59556235dec7
line wrap: on
line diff
--- a/skaapsteker/physics.py	Sat May 07 17:29:10 2011 +0200
+++ b/skaapsteker/physics.py	Sat May 07 20:28:06 2011 +0200
@@ -12,7 +12,8 @@
 
 from . import options
 from .constants import EPSILON
-from .utils import cadd, csub, cmul, cdiv, cclamp, cint, cneg, cabs
+from .utils import (cadd, csub, cmul, cdiv, cclamp, cabsmax, cint, cneg, cabs,
+                    rect_projection)
 
 class Sprite(pygame.sprite.Sprite):
 
@@ -252,29 +253,87 @@
         self._last_time = now
         return dt
 
+
+    # def collide_sprite(self, dt, sprite):
+    #     sprite.apply_velocity(dt)
+    #     sprite_collides = sprite.collide_rect.colliderect
+    #     collisions = []
+    #     for other in self._collision_groups[sprite.collision_layer]:
+    #         if sprite_collides(other.collide_rect) \
+    #                 and sprite.check_collides(other):
+    #             collisions.append(other)
+    #     if collisions:
+    #         self._backout_collisions(sprite, collisions, dt)
+    #     contact_rect = pygame.Rect(
+    #         (sprite.collide_rect.left, sprite.collide_rect.bottom),
+    #         (sprite.collide_rect.width, 1))
+    #     return contact_rect.colliderect
+
+
+    def get_sprite_collisions(self, dt, sprite):
+        sprite.apply_velocity(dt)
+        sprite_collides = sprite.collide_rect.colliderect
+        collisions = []
+        for other in self._collision_groups[sprite.collision_layer]:
+            if (sprite_collides(other.collide_rect)
+                and sprite.check_collides(other)):
+                collisions.append(other)
+        return collisions
+
+
+    def path_collide(self, dt, sprite):
+        dts = [dt/10] * 9
+        dts.append(dt - sum(dts))
+        dtf_acc = 0
+        collisions = []
+        for dtf in dts:
+            dtf_acc += dtf
+            collisions = self.get_sprite_collisions(dtf, sprite)
+            for col in collisions:
+                if sprite.block and (col.floor or col.block):
+                    return collisions, dtf_acc
+        return collisions, dt
+
+
+    def collide_sprite(self, dt, sprite):
+        initial_pos = sprite._float_pos
+        collisions = self.get_sprite_collisions(dt, sprite)
+        escape_vector = (0, 0)
+        if collisions:
+            # If we've collided, reset and try again with smaller time increments
+            sprite.update_position(initial_pos)
+            collisions, dtf = self.path_collide(dt, sprite)
+            for col in collisions:
+                if sprite.block and (col.floor or col.block):
+                    escape_vector = cabsmax(escape_vector, rect_projection(sprite.collide_rect, col.collide_rect))
+                sprite.collided(col)
+            sprite.update_position(cadd(sprite._float_pos, escape_vector))
+            # if escape_vector[0] != 0:
+            #     sprite.velocity = (0, sprite.velocity[1])
+            # if escape_vector[1] != 0:
+            #     sprite.velocity = (sprite.velocity[0], 1)
+            # self._backout_collisions(sprite, collisions, dtf)
+        contact_rect = pygame.Rect(
+            cadd(sprite.collide_rect.bottomleft, (1, 0)),
+            (sprite.collide_rect.width, 1))
+        return contact_rect.colliderect
+
+
     def update_sprite_positions(self, dt):
         # position update and collision check (do last)
         for sprite in self._mobiles:
-            sprite.apply_velocity(dt)
-            sprite_collides = sprite.collide_rect.colliderect
-            collisions = []
-            for other in self._collision_groups[sprite.collision_layer]:
-                if sprite_collides(other.collide_rect) \
-                        and sprite.check_collides(other):
-                    collisions.append(other)
-            if collisions:
-                self._backout_collisions(sprite, collisions, dt)
-            contact_rect = pygame.Rect(
-                    (sprite.collide_rect.left, sprite.collide_rect.bottom),
-                    (sprite.collide_rect.width, 1))
-            collides = contact_rect.colliderect
-            floors = []
+            collides = self.collide_sprite(dt, sprite)
 
             # Are we currently in contact with the ground?
+            if not sprite.block:
+                continue
+            floors = []
             sprite.on_solid = False
             for other in self._collision_groups[sprite.collision_layer]:
                 if (other.floor or other.block) and collides(other.floor_rect):
                     sprite.on_solid = True
+                    if sprite.velocity[1] > 0:
+                        sprite.velocity = (sprite.velocity[0], 0)
                     floors.append(other)
             sprite.check_floors(floors)