I am working on a simple 2D platformer, but I am having some trouble with the hitboxes of my sprites. I use the pygame.sprite.spritecollide function to generate a list of blocks(platforms) touching my player sprite.
Here is my player class:
class Player( pygame.sprite.Sprite ):
def __init__(self,x,y,image):
super( Player, self ).__init__()
self.vel_x = 0
self.vel_y = 0
self.moveSpeed = 3
self.jumpPower = 7
self.direction = idle
self.grounded = False
self.falling = True
self.jumping = False
self.climbing = False
self.image = pygame.Surface((50,50))#pygame.transform.scale( image, (50,50))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.width = 50
self.height = 50
self.rect.bottom = self.rect.y + self.height
def set_position( self,x,y ):
self.rect.x = x
self.rect.y = y
def experience_gravity(self, gravity = 0.3):
if not self.grounded:
self.vel_y += gravity
self.falling = True
else:
self.vel_y = 0
self.grounded = True
self.falling = False
def jump(self):
self.grounded = False
self.vel_y -= self.jumpPower
def update(self, collidable = pygame.sprite.Group() ):
global collision
self.experience_gravity()
self.rect.x += self.vel_x
self.rect.y += self.vel_y
collision_list = pygame.sprite.spritecollide( self, collidable, False )
for p in collision_list:
if ( self.vel_y > 0 ):
self.vel_y = 0
self.grounded = True
In my the update method, I check for collisions between a sprite group holding all of my platforms (parameter collidable) and the player.
Here's my block class:
class Block( pygame.sprite.Sprite ):
def __init__( self, x, y, width, height):
super( Block, self ).__init__()
self.image = pygame.Surface( ( width, height ) )
self.image.fill( black )
self.rect = self.image.get_rect()
self.type = platform
self.rect.x = x
self.rect.y = y
self.width = width
self.height = height
Pretty self-explanatory block class. The rest of my code is to update and blit everything onto a white background. The problem I run into is that when the player lands on a platform, it only stops falling (becomes grounded) when it is already in the platform. Even stranger, the depth the block sinks intot he platform is not consistent. Sometimes it will fall in 10 pixels, other ties 20 pixels. Here's a screenshot of the player getting stuck:
The player block is stuck inside the platform block
So this is really baffling me, especially since the amount the block falls in is inconsistent. If anyone could give me an idea on how to fix this, I'll really appreciate it.
With kindest regards,
Derek
It's hard to diagnose without any debugging trace, but I have an idea or two.
First of all, dump a debugging trace: print the salient values at each time step: position of the player block, positions of colliding blocks, and applicable velocities. This should give you a table-like snapshot at each time step of the involved objects.
Now, look at the values in the last time step before the collision. I suspect that you might have something like a player velocity of -20, but landing on a platform only 10 pixels below. Your update logic needs to handle the landing between the two time steps, and stop the player at the platform height. It looks to me as if the player is allowed to fall for the entire time step, which will embed his tender lower extremities 10 pixels into the platform.
Second, check that you're comparing the nearer edges: in this case, the player's lower edge with the platform's upper edge. You will later need to handle the case where a player's lower-right corner contacts the upper-left corner of a block, and you have to determine whether the player first hits the block's top (landing) or side (change horizontal velocity and drop).
I wouldn't alter the computed velocity; I would think it's easier just to adjust the final position. The last block of code you posted is where you already deal with a collision as a special case. When you detect a collision, get the top surface (y-value) of the object. If that's above the current position of the bottom of the player, assign that top surface as the correct position. This will leave your player sitting on top of whatever is the highest object on the collision list.
Note that this works only as long as your entire collision list is from falling; for lateral collisions as well, you'll need to separate the lists and adjust each boundary as needed. This can get complicated if there are collisions from several sides on the same time step -- for instance, a goombah slams into the player from the left just as the player hits both a wall and a platform.