I've got a level made out of sprites (in a spritegroup), these sprites are placed in by a 2 dimensional array, this is the tilemap.
I've got a player sprite (in another spritegroup)
How do you make a 2d platformer out of this ? I just need a piece of code that can tell which sides of the player is touching a tile and returns it so I can then make conditions that would stop him moving from side to side or stop him jumping. I've tried lots of different things and I just can't make this work correctly because of the tilemap.
I'm here to answer questions for people who want to learn how to program. If the question is obviously a "gimme teh codez", or "I copied some code from here & there; now fix it for me!", I don't put much coding effort into the answer. So I hope you can understand why this answer below is largely theoretical, and doesn't contain tested & debugged code.
I assume you're talking about a 2D game with "gravity", such that the player jumps and falls somehow. If you're talking about a 2D rouge-like (top-down view, no gravity) some of the nomenclature below might be a bit off, but the logic the same.
A tile-based "map" or "level" can be thought of as a set of layers. I would split the collide-able sprites into a separate sprite-group, let's call these platforms
. The decoration tiles can go into a background
group. The background is a lower-layer that apart from having to be drawn, can otherwise be ignored. A single background-image might be better for this than a whole bunch of sprites.
So whenever the player tries to move, the platforms need to be tested for collision with the new thoretical-location before the movement is finalised. There is a handy function in the PyGame Sprite object called pygame.sprite.spritecollideany()
. This allows your code to check for collisions between a single sprite and a while sprite group quite easily:
collided_with = player_sprite.spritecollideany( platform_sprite_group )
if ( collided_with != None ):
print( "Player collided with sprite: " + str( collided_with ) )
Another handy function is pygame.sprite.groupcollide()
, this is great for colliding a group of sprites against another - like a bunch of arrows against a bunch of enemies. But you could also make a sprite group with a single item in it, and use it on singular objects too.
You don't really need to know which side of a player is colliding with the platforms. It's implicit information, because you already know which way the player is moving. The basis of collision testing is that realising that moving a sprite greater than 1 pixel at a time can move the player on-top of an existing sprite without ever touching a side. Say the player moved really-fast, it's next movement position might be right into the middle of another sprite! So which side did it hit? (The side the player moved from.)
So for example: If the player wanted to move 10 pixels left, but a platform-edge is 5 pixels away, the player "collides" but without touching. Once a collision happens, the code needs to determine what the acceptable movement would have been, and adjust the movement to this delta. Since PyGame sprite objects are based on a Rect object, this is a reasonably straightforward test using the Rect's x
, y
and width
, height
. The 'x' & 'y' represent the top-left corner, so we need to take the width of the sprites in to account:
# Player has tried to move delta_x and delta_y pixels
collided_with = player_sprite.spritecollideany( background_sprite_group )
if ( collided_with != None ):
print( "Player collided with sprite: " + str( collided_with ) )
if ( delta_x > 0 ): # moving right
# collides on right side
distance_to_sprite_x = player_sprite.rect.x + player_sprite.rect.width - collided_with.rect.x
elif ( delta_x < 0 ): # moving left
# collides on left side (take width of sprite into account)
distance_to_sprite_x = player_sprite.rect.x - collided_with.rect.x + collided_with.rect.width
else: # not moving horizontally
distance_to_sprite_x = 0
# Adjust the x-movement to make it rest against the sprite
delta_x = distance_to_sprite_x
# TODO: similarly for delta_y
Obviously is the player is moving sufficiently fast (many pixels per frame), it could theoretically "jump" an entire platform, and not collide, passing right through. The code above suffers from this latent-bug. But most games would not need to worry about this sort of situation.
So I hope that the logic presented above is enough to get you started handling all sorts of collisions. Just try to get some code working, that's the best way to learn. Write something, test it, make mistakes, ask questions about it. We're here ready to help. It's easy to fall into the trap of just copying and pasting other people's code (which is sometimes of middling quality) to get that "quick solution". But it doesn't really help you learn the fundamentals.