changeset 144:6b488e1351a5

Buggy ground implementation. Make the world less bouncy
author Neil Muller <drnlmuller@gmail.com>
date Tue, 05 Apr 2011 15:46:13 +0200
parents d10b841c452e
children fec9ed36e9a2
files skaapsteker/physics.py skaapsteker/sprites/base.py skaapsteker/sprites/player.py
diffstat 3 files changed, 48 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/skaapsteker/physics.py	Tue Apr 05 15:00:31 2011 +0200
+++ b/skaapsteker/physics.py	Tue Apr 05 15:46:13 2011 +0200
@@ -17,7 +17,7 @@
     # physics attributes
     mobile = True # whether the velocity may be non-zero
     gravitates = True # whether gravity applies to the sprite
-    terminal_velocity = (300.0, 300.0) # maximum horizontal and vertial speeds (pixels / s)
+    terminal_velocity = (450.0, 450.0) # maximum horizontal and vertial speeds (pixels / s)
     bounce_factor = (0.95, 0.95) # bounce factor
     mass = 1.0 # used for shared collisions and applying forces
     friction_coeff = (0.99, 0.99) # friction factor
@@ -34,8 +34,11 @@
 
     debug_color = (240, 0, 0)
 
+    is_ground = False  # We special case collisions with ground objects
+
     def __init__(self, *args, **kwargs):
         super(Sprite, self).__init__(*args, **kwargs)
+        self.on_ground = False
         self.velocity = (0.0, 0.0)
         self.rect = pygame.Rect(0, 0, 10, 10) # sub-classes should override
         self.collide_rect = pygame.Rect(0, 0, 10, 10) # rectangle we use for collisions
@@ -100,6 +103,11 @@
         dv_x = - normal[0] * b_x * v_x
         dv_y = - normal[1] * b_y * v_y
 
+        if normal == (0, 1) and other.is_ground and v_y > 0 and self.collide_rect.top < other.collide_rect.top:
+            # Colliding with the ground from above is special
+            self.on_ground = True
+            dv_y = -v_y
+
         if other.mobile:
             total_mass = self.mass + other.mass
             f_self = self.mass / total_mass
@@ -186,7 +194,10 @@
         # gravity
         dv = self.GRAVITY[0] * dt, self.GRAVITY[1] * dt
         for sprite in self._gravitators:
-            sprite.deltav(dv)
+            if sprite.on_ground:
+                sprite.deltav((dv[0], 0.0))
+            else:
+                sprite.deltav(dv)
 
         # friction
         for sprite in self._mobiles:
@@ -198,10 +209,32 @@
             sprite_collides = sprite.collide_rect.colliderect
             collisions = []
             for other in self._collision_groups[sprite.collision_layer]:
-                if sprite_collides(other) and sprite.check_collides(other):
+                if sprite_collides(other.collide_rect) \
+                        and sprite.check_collides(other):
                     collisions.append(other)
             if collisions:
                 self._backout_collisions(sprite, collisions, dt)
+            if sprite.on_ground:
+                # Check if we are still in contact with the ground
+                contact_rect = sprite.collide_rect.move((0, 1))
+                collides = contact_rect.colliderect
+                still_on_ground = False
+                for other in self._collision_groups[sprite.collision_layer]:
+                    if other.is_ground and collides(other.collide_rect):
+                        still_on_ground = True
+                        break
+                sprite.on_ground = still_on_ground
+            else:
+                # Are we currently in contact with the groun
+                contact_rect = pygame.Rect(
+                        (sprite.collide_rect.left, sprite.collide_rect.bottom -1),
+                        (sprite.collide_rect.width, 1))
+                collides = contact_rect.colliderect
+                for other in self._collision_groups[sprite.collision_layer]:
+                    if other.is_ground and collides(other.collide_rect):
+                        sprite.on_ground = True
+                        break
+
 
         # call update methods
         self._updaters.update()
--- a/skaapsteker/sprites/base.py	Tue Apr 05 15:00:31 2011 +0200
+++ b/skaapsteker/sprites/base.py	Tue Apr 05 15:46:13 2011 +0200
@@ -71,6 +71,8 @@
     mobile = False
     gravitates = False
     collides_with = set([PC_LAYER, MONSTER_LAYER])
+    is_ground = True
+    bounce_factor = (0.0, 0.0)
 
     def __init__(self, pos, image):
         Sprite.__init__(self)
--- a/skaapsteker/sprites/player.py	Tue Apr 05 15:00:31 2011 +0200
+++ b/skaapsteker/sprites/player.py	Tue Apr 05 15:46:13 2011 +0200
@@ -39,16 +39,16 @@
         if self._animation_frame >= len(images):
             self._animation_frame = 0.0
         if self.rect:
-            cur_pos = self.rect.topleft
+            cur_pos = self.collide_rect.topleft
         else:
             cur_pos = (0, 0)
         self.image = images[int(self._animation_frame)]
         self.rect = self.image.get_rect()
         self.collide_rect = self.image.get_bounding_rect(1)
-        self.rect_offset = self.collide_rect.left - self.rect.left, self.collide_rect.top - self.rect.top
-        self.rect.topleft = cur_pos
-        self.collide_rect.topleft = cur_pos[0] + self.rect_offset[0], cur_pos[1] + self.rect_offset[1]
-
+        self.rect_offset = self.rect.left - self.collide_rect.left, self.rect.top - self.collide_rect.top
+        self.collide_rect.topleft = cur_pos
+        self.rect.topleft = cur_pos[0] + self.rect_offset[0], cur_pos[1] + self.rect_offset[1]
+        self.init_pos()
 
     def update(self):
         v_x, v_y = self.velocity
@@ -65,8 +65,8 @@
 
     def set_pos(self, pos):
         self.starting_tile_pos = pos
-        self.collide_rect.topleft = pos[0] * TILE_SIZE[0] + self.rect_offset[0], pos[1] * TILE_SIZE[1] + self.rect_offset[1]
-        self.rect.topleft = pos[0] * TILE_SIZE[0], pos[1] * TILE_SIZE[1]
+        self.rect.topleft = pos[0] * TILE_SIZE[0] + self.rect_offset[0], pos[1] * TILE_SIZE[1] + self.rect_offset[1]
+        self.collide_rect.topleft = pos[0] * TILE_SIZE[0], pos[1] * TILE_SIZE[1]
 
     def action_left(self):
         if self.facing != 'left':
@@ -81,7 +81,9 @@
         self.deltav((100.0, 0.0))
 
     def action_up(self):
-        self.deltav((0.0, -100.0))
+        if self.on_ground:
+            self.deltav((0.0, -350.0))
+            self.on_ground = False
 
     def action_down(self):
         self.deltav((0.0, 100.0))