PyGame Collision with Tile Rendered map? - python

I'm having problems with my tile renderer, which goes through a text file and finds characters, converting them into rects. My problem is that only the most recent tile / rect is counted for collisions.
A video of my problem: http://youtu.be/7wAHp-vgrLU
My code works like this:
wall = pygame.draw.rect(screen, (40,40,40), (current_tile_x,current_tile_y,tile_size,tile_size), 0)
if wall.colliderect(collision) == 1:
print "Collision!"
The player's rect is defined as collision. The problem I think is that for every wall tile, the var wall gets overwritten, so how would I go about fixing this?

You just answered yourself, you should make a iterable with all rects to be tested:
#load all the rects in one list for example
walls = get_wall_list() #returns a list [rect0,rect1,rectn]
for wall in walls:
if wall.colliderect(collision): #'if True == 1:' works as the same 'if True:'
print "Collision!"

Related

Get any objects (Rect, Surfaces...) at position / coordinates X, Y

So the question is simple:
Given a Surface, let's call it screen and x,y coordinates, can I get anything that lays at that coordinates on that Surface?
For example, let's say we have typical, Player attack, and if the attack reach the Enemy position x,y then enemy dies.
So given this simple app (is an example only not a real app)
import pygame as pg
from pygame.math import Vector2
# pygame constants
CLOCK = pg.time.Clock()
WIN_SIZE = (1280, 640)
# pygame setup
pg.init()
# screen
window = pg.display.set_mode(WIN_SIZE, 0, 32)
background = pg.Surface(WIN_SIZE)
player = pg.Surface(Vector2(12, 64))
player_rect = player.get_rect(topleft=Vector2(150, 150))
player_attack = False
player.fill((102, 255, 178))
player_attack_range = 20 # player can hit at min 20 pixel from target
enemy = pg.Surface(Vector2(12, 64))
enemy_rect = player.get_rect(topleft=Vector2(175, 150))
enemy.fill(pg.Color("green"))
while True:
background.fill((0, 0, 0)) # screen clear
# Render enemy
attacked = False
if player_attack:
# !!!!! HERE !!!!!
# Now we check if the playuer is close enough to the enemy, so we MUST know the enemy pos
distance_x = abs(player_rect.x - enemy_rect.x)
if distance_x > player_attack_range:
attacked = True
enemy.fill(pg.Color("red"))
if not attacked:
enemy.fill(pg.Color("green"))
background.blit(enemy, enemy_rect.topleft)
# Render player
background.blit(player, player_rect.topleft)
# Events
for event in pg.event.get():
if event.type == pg.QUIT or (
event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE): # x button and esc terminates the game!
exit(1)
# ............. Mouse ............. #
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
player_attack = True
if event.type == pg.MOUSEBUTTONUP:
if event.button == 1:
player_attack = False
pg.display.update() # 2) Update the game
window.blit(background, (0, 0)) # 3) Repaint the screen
CLOCK.tick(60) # 4) Wait 60 Frames
When is attacked
Now I always seen it done this way more or less:
distance_x = abs(player_rect.x - enemy_rect.x)
if distance_x > player_attack_range:
attacked = True
enemy.fill(pg.Color("red"))
With this example, I'm not pointing out the code implementation but the fact that, the player must know the target position and then check whether or not the target is hit
But what I want to know, let's say I don't know the enemy position, and the player just attacks, is there a way that we can get what's currently on the surface at the attack range?
So do something like
attacked_area_x = abs(player_rect.x + player_attack_range) # only care of x coords
rects_or_surfaces_in_area = background.what_have_we_got_here(Vector(attacked_area, 0))
for r in rects_or_surfaces_in_area:
print("Hit!")
Update
So By checking MDN documentation of Game Development MDN I actually find a game algorithm / Technique that is similar (but concept is the same) of my solution.
Is called the Broad Phase
From the documentation:
road phase should give you a list of entities that could be colliding. This can be implemented with a spacial data structure that will give you a rough idea of where the entity exists and what exist around it. Some examples of spacial data structures are Quad Trees, R-Trees or a Spacial Hashmap.
So yes, it seems one of many good approach to solve this problem.
So, after some research and thanks to Rabbid76 and his answer here How do I detect collision in pygame? which covers in details the most common collisions in Pygame, it seems that what I was looking for natively is just not possible.
Maybe is normal, I'm also new to game development and maybe what I want to do just doesn't make any sense, but I bet it does.
The scenario I'm facing is, just one player with a sword hitting, so I asked my self, why should I need to know prior what objects lie on the sword path and check if is hit, instead, do the hit and request to the parent screen "what object are in the sword path"? , which, is for sure faster because we don't need to know what object that have collision are (and avoid a for loop and check for each react/surface).
So, let's say there are many many object that have collision and a player may hit it, it would be way faster do don't know what' there but request it instead to the surface, so basically the surface should be aware of its children / objects.
I tried a bit the Surface.subsurface() and the Surface.get_parent() but couldn't make it work, anyway still, in a surface area we may have many thinks like:
draws, Rect, Surfaces, Sprites, Imgs etc...
I have only 2 solutions in my mind:
Map the objects coordinates
This only really works if, the entities are static, if so, then we could create a dict with key x:y coordinates and then check if the sword is in within a certain x:y and exist in the dict, then the entity is hit.
But with entity then moves by themself, is a nigtmare and will have a worst problem than before, you would need to update the keys at each frame and so on..
Make it 'distance' sensible
So, this could apply to each entity that is moving and may or may not hit something that has a collision. But staying on the example context, let's say we are iterating thourgh entities / object that at each frame are updating their position.
We could check how distant are from the player, let's say 2 chunks away (or the sword lenght) and collect them in a list
Then we that list, we check if, once the sword is updated, is hitting anything close by.
Still, pretty sure there are better ways (without changing or extending pygame its self).
And maybe, by extending pygame, there may be a way to implement a 'surface aware' class that when we request a specific Rect area, it tells us what's there.

Player Keeps Sticking On The Platform Collision Pygame

I am trying to get a good collision with my rectangles but I feel like my method is bad because when ever I job and collide with my other platform my player keeps getting stuck on it, is there a way I could make it collide good without it getting stuck I am just looking for a good collision Thank You!
VIDEO < as you can see my player keeps getting stuck on the platform its the same thing for left and right when I collide with the platform it will just make my player stuck without proper collision
# collisions
for platform in platforms:
if playerman.rect.colliderect(platform.rect):
collide = True
playerman.isJump = False
if (platform.rect.collidepoint(playerman.rect.right, playerman.rect.bottom) or
platform.rect.collidepoint(playerman.rect.left, playerman.rect.bottom)):
playerman.y = platform.rect.top - playerman.height + 1
playerman.moveright = True
playerman.moveleft = True
if (platform.rect.collidepoint(playerman.rect.right, playerman.rect.top) or
platform.rect.collidepoint(playerman.rect.right, playerman.rect.bottom - 10)):
playerman.moveright = False
elif (platform.rect.collidepoint(playerman.rect.left, playerman.rect.top) or
platform.rect.collidepoint(playerman.rect.left, playerman.rect.bottom - 10)):
playerman.moveleft = False
else:
playerman.moveright = True
playerman.moveleft = True
my full code: script
I been searching everywhere for proper collision and I cant manage to find any good working ones
From your video it looks like the collision detection does not work if you come up from below a block.
In general (see below for an example): I encountered the same "puzzle" with my game and as far as I could see there are two possible ways.
Pre-Checking
You check how far away the player is from the closest "block" and let the player move only so far. This includes:
checking which blocks are the closest to the player
checking the distance to each close block and calculating the remaining possible x pixels the player can move in direction z.
move player exactly x pixels in direction z.
Post-Checking (I used this as it was easier to figure out on my own back then)
You move the player according to his current speed and then check for collisions. If you get a collision, you move the player back for the amount of pixels which is the intersection between player-border and block-border.
move player according to his speed to the full extent
check collisions for all close blocks (or all blocks on the map for starters and you can improve the performance from there)
if you get a collision, calculate by how much the player intersects with the colliding block. This is easy if your player's hitbox is a rectangle and you work with a tilemap made up of rectangular blocks, you can simply subtract player.x and block.x coordinates
move the player back (before updating the screen) by that amount of pixels.
If you want to learn more about it and have in-depth code examples (if you don't want to try and error by yourself until you figure it out) I suggest searching on youtube for pygame-2D collision detections, there are great teachers on there.
Here is an excerpt of my collisiondetection_x_axis() method (self references the player!)
# move player etc ...
for tile in map_tiles: # for all tiles on the map
if pygame.sprite.collide_rect(self.hitboxBody, tile):
if self.SpeedX < 0 and tile.rect.right > self.hitboxBody.rect.left: # moving left and collision
# move char back to "in front of the wall"
self.rect.x += tile.rect.right - self.hitboxBody.rect.left
self.SpeedX = 0 # set speedX to zero, as we cannot move in that direction anymore
elif self.SpeedX > 0 and self.hitboxBody.rect.right > tile.rect.left: # moving right and collision
# move char back to "in front of the wall"
self.rect.x -= self.hitboxBody.rect.right - tile.rect.left
self.SpeedX = 0 # set speedX to zero, as we cannot move in that direction anymore
collision_detection_y_axis:
for tile in map_tiles: # for all tiles on the map
if pygame.sprite.collide_rect(self.hitboxBody, tile):
if self.SpeedY < 0 and tile.rect.bottom > self.hitboxBody.rect.top: # moving up
self.rect.y += tile.rect.bottom - self.hitboxBody.rect.top # move char back to "below the wall"
self.SpeedY = 0
elif self.SpeedY > 0 and self.hitboxBody.rect.bottom > tile.rect.top: # moving downwards
self.rect.y -= self.hitboxBody.rect.bottom - tile.rect.top # move back to "on top of the wall"
self.SpeedY = 0
self.jumping = False # on ground
Edit: this requires your movement between collision-checks to be less than the width of a block, otherwise your character can 'glitch' through blocks if he has enough speed.
Note: you should take into the account the direction your player is moving before you do collision-tests, it makes it easier to determine which side of the player will possibly collide first with a block. For instance if you are moving to the right, then the right side of the player will collide with the left side of a block. Then write a collision detection for those two points as well as consequent action (e.g. reset to a position in front of the block and speed_x = 0)
PS: Try and use the function pygame.Rect.colliderect, it tests if two rects overlap (=collision), I have a feeling the way you set up your collidepoint-functions don't return collisions for all possible scenarios.

How to check if the Snake in a snake game has crossed the food?

I am working on a snake game using python and pygame but having problem in checking whether the snake has crossed the food or not. Can anyone here help me with that?
I've tried making the location of food to be a multiple of 10 since my snake width and height is also 10 and the window (width and height) is also a multiple of 10.
food_x = random.randrange(0, displayWidth-foodWidth, 10)
food_y = random.randrange(0, displayHeight-foodHeight, 10)
I expected that doing so will make the location of the food such that there will be no collisions but direct overlap of snake and food which would make the coding easier. However, there were collisions also.
So given your snake data structure is a set of rectangles, and the snake only "eats" from the head-rectangle, it's pretty simple to determine a collision routine.
The PyGame rect library has functions for checking collisions between rectangles.
So assuming head_rect is a rect with the co-ordinates and size of your snake's head, and food_rect is an item to check:
if ( head_rect.colliderect( food_rect ) ):
# TODO - consume food
Or if there's a list of food_rect in food_list:
def hitFood( head_rect, food_list ):
""" Given a head rectangle, and a list of food rectangles, return
the first item in the list that overlaps the list items.
Return None for a no-hit """
food_hit = None
collide_index = head_rect.collidelist( food_list )
if ( collide_index != -1 ):
# snake hit something
food_hit = food_list.pop( collide_index )
return food_hit
It's much easier to use PyGame's libraries rectangle overlap functions than making your own.

How to keep the random moving enemies inside the screen in pygame?

I want to keep the enemies (red rectangles in my case) inside the rectangle.What happens is that since it's a random function , the enemies keep on moving.But slowly some of my enemies moves outside , I don't want this to happen.I want them to be inside the screen only.
I have tried subtracting the screen width and screen height but was unsuccessful in my attempt to do so.So basically what I am asking is how do I check if rectangle is inside the screen.
EDIT :
This is not a possible duplicate as my enemies are moving randomly.In that the player is controlled by the user whereas my program , the movements of the enemy are totally random.
Here is my random movement for my enemies:
def evilMove(evilGuy):
evilCoords = []
#Returns either -1, 0 or 1
randomMovex=random.randrange(-1,2)
randomMovey=random.randrange(-1,2)
evilMake={'x':evilGuy[0]['x']+randomMovex,'y':evilGuy[0]['y']+randomMovey}
del evilGuy[-1] #Delete the previous spawn of enemy so there is no trail
evilCoords.append(evilMake['x']) #Adds x coordinate
evilCoords.append(evilMake['y']) #Adds y coordinate
deadZones.append(evilCoords) #Adds the enemy coordinates in the list
evilGuy.insert(0,evilMake)
Thanks in advance.
Well, you should really look at other questions similar to this, but you could make an if statement where you test if it is outside the screen.
Lets just say your screen is 500 by 500, you would do,
if coordinatesx < 500:
do this and that
if coordinatesy < 500:
do this and that.
This checks if the coordinates of your bad guy are being checked by the screen by saying if the bad guys coordinates are not larger than the screen, then do what you need to do.
EDIT _________________________________________
def evilMove(evilGuy):
evilCoords = []
#Returns either -1, 0 or 1
if x < 500 and y < 500:
randomMovex=random.randrange(-1,2)
randomMovey=random.randrange(-1,2)
evilMake={'x':evilGuy[0]['x']+randomMovex,'y':evilGuy[0]['y']+randomMovey}
Im not sure what your x and y variable is called, just replace it with that. And replace the 500 with your sreen width.

Sprite collide with object function?

Is it possible for me to create a function where it displays a message if the Sprite (Rocket) collides with the astroid objects?
class Rocket(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.rect=self.image.get_rect()
self.image=Rocket.image
self.firecountdown = 0
def setup(self):
self.rect.x=700
self.rect.y=random.randint(20,380)
def updateposition(self):
self.rect.x=self.rect.x-1
time.sleep(0.005)
if self.rect.x == 0 :
self.rect.x = 700 + random.randint(0, 100)
self.rect.y=random.randint(20,380)
asteroids=[]
asteroidsize=[]
for i in range(25):
x=random.randrange(700,10000)
y=random.randrange(0,400)
asteroids.append([x,y])
asteroids[i]=Asteroid()
for i in range(25):
asteroidsize.append(random.randint(6,15))
while True:
for i in range(len(asteroids)):
pygame.draw.circle(screen,GREY,asteroids[i],asteroidsize[i])
asteroids[i][0]-=2
if asteroids[i][0]<0:
y=random.randrange(0,400)
asteroids[i][1]=y
x=random.randrange(700,720)
asteroids[i][0]=x
You could write a function on your Rocket class that checks for collisions. Since the asteroids are circles, you'll want to check if the closest point on the circle to the center of your sprite's rect is within the rect's bounds:
def check_asteroid_collision( self, asteroid, size ) :
# Create a vector based on the distance between the two points
distx = self.rect.centerx - asteroid[0];
disty = self.rect.centery - asteroid[1];
# Get magnitude (sqrt of x^2 + y^2)
distmag = ((distx * distx) + (disty * disty)) ** 0.5;
# Get the closest point on the circle:
# Circle center + normalized vector * radius
clsx = asteroid[0] + distx / distmag * size;
clsy = asteroid[1] + disty / distmag * size;
# Check if it's within our rect
if self.rect.collidepoint( clsx, clsy ) :
# We're colliding!! Do whatever
print( "Oh no!" );
Then in your game loop, you could check collisions by calling this:
while True:
for i in range(len(asteroids)):
...
# Collision checking
myrocket.check_asteroid_collision( asteroids[i], asteroidsize[i] );
Keep in mind this process is somewhat expensive and it will check every asteroid if it's colliding. If there's a large number of asteroids, it'll run slowly.
While I dont code python I can give you a simple example of how to accomplish something like this.
Make all your game objects inherit from a general game item class, this way you know all items have a position and a collision radius.
class:
int positionX
int positionY
int radius
Then keep all your items in a global list of game objects.
Loop over your game list and see if any two items collide
foreach object1 in gameObjectsList:
foreach object2 in gameObjectsList:
if(object1 != object2)
if(math.sqrt(object1.positionX - object2.positionX)**2 +
(object1.positionY - object2.positionY)**2)
<= object1.radius + object2.radius.
//Things are colliding
As the game progresses make sure you keep the position variables updated in each object.
What this means is basically that you have your list of game objects, and you loop over these every game frame and check if any of them are touching each other.
Or in terms of pure math, use the distance formula (link) to get the distance between the two items, and then check if their combined radius is greater than this distance. If it is they are touching.
Yes, making the score is possible. I am asuming you know the sprite collision function : pygame.sprite.spritecollide(). If you don't, look into the PyGame Docs. or google it. But here is how you do it. First. add these lines of code at the beginning of your code after the pygame,init() line:
variable = 0
variable_font = pygame.font.Font(None, 50)
variable_surf = variable_font.render(str(variable), 1, (0, 0, 0))
variable_pos = [10, 10]
Clearly, variable can be a string, just remove the str() in line 3. Line 1 is self-explanatory - it is the value you will see on the screen (just the stuff after the = sign and/or the parantheses). Line 2 decides what font and size you want the message to be in. If you choose None and 50, it means that you want the message in a normal font and in size 50. The third line renders, or pastes, the message on the screen the name of the variable that contains the string/number, the number 1 (I have no idea why), and the color your message will be. If the variable contains a number, put a str() around it. The last line will be the position of the message. But you will need to blit it first. To prevent the message from appearing on the screen forever, make a variable:
crashed = 0
Then make your instances and groups:
ship = Rocket(None)
asteroids = pygame.sprite.Group() #This is recommended, try making a class for the asteroids
And finally your collision code:
if pygame.sprite.spritecollide(Rocket, asteroids, True):
crashed = 1
You can make your blits controlled with the crashed variable:
if crashed == 0:
screen.blit(ship.image, ship.rect)
elif crashed == 1:
screen.blit(ship.image, ship.rect)
screen.blit(variable_surf, variable_pos)
The last blit line will blit your message on the screen at the location listed (variable_pos) when your ship crashes (crashed = 1 in the collision code). You can use make some code to make crashed back to 0. Remember to do pygame.display.flip() or weird stuff will happen. I hope this answer helps you!

Categories