Pygame Collision with Changing sprites - python-2.7

Alright,
I've looked everywhere and still can't figure out PyGame collisions. I have two sprites: A lander and a moon surface. The moon is an uneven surface with a transparent background, while the lander is a sprite that changes through images with L,D,UP keypresses.
I'm doing something wrong when it comes to collision detection. Otherwise, everything in my program is going swimmingly
This is my lander class:
class ShipClass(pygame.sprite.Sprite):
def __init__(self, image_file, position):
pygame.sprite.Sprite.__init__(self)
self.imageMaster = pygame.image.load(image_file)
self.image = self.imageMaster
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.imageMaster)
self.rect.topleft = position
## This doesn't work
# def checkCollision(self, Mask):
# if Mask.overlap_area
def update(self):
self.rect = self.image.get_rect()
And this is my lunar surface class:
class MoonSurface(pygame.sprite.Sprite):
def __init__(self, image_file, position):
pygame.sprite.Sprite.__init__(self)
self.imageMaster = pygame.image.load(image_file).convert_alpha()
self.image = self.imageMaster
self.rect = self.image.get_rect()
self.rect.topleft = position
What do I need to add to this so that while in my game if the lander hits the moon it sets variable stop to 1 and crash to 1? I plan on also adding landing pads with the MoonSurface class and when it hits them only sets stop to 1.
My sprite is moved as follows:
speed = [0,0]
position = [width-524,height-718]
gravity = 0.05
StartingFuel = 100
done = False
while not done:
event = pygame.event.poll()
if event.type == QUIT:
pygame.quit()
sys.exit(0)
elif event.type == pygame.KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit(0)
else:
lander = ShipClass('lander.png', position)
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
lander = ShipClass('landerLeft.png', position)
# lander.set_colorkey(BLACK)
speed[0] += 0.3
fuel -= 1
if keys[pygame.K_RIGHT]:
lander = ShipClass('landerRight.png', position)
# lander.set_colorkey(BLACK)
speed[0] -= 0.3
fuel -= 1
if keys[pygame.K_UP]:
lander = ShipClass('landerUp.png', position)
# lander.set_colorkey(BLACK)
speed[1] -= 0.4
fuel -= 1
speed[1] += gravity
position[0] += speed[0]
position[1] += speed[1]
if position[0] < 0:
position[0] = width
elif position[0] > width:
position[0] = 0
screen.blit(lander.image, position)

It looks like you want to check if the two objects overlap. Luckily Pygame has a built- in function colliderect which checks if two rectangles are overlapping. I've put it into a class method here, as I saw your objects had Pygame rectangle variables named rect:
def overlaps (self, other_rect):
return self.rect.colliderect(other_rect)
Here's the documentation: pygame.Rect

Related

Pygame - rendering multiline text using Sprites

Struggle is that I am capable of rendering one line of text with Sprites in Pygame, I am also capable of rendering multi-line text, but without the Sprites, but I can't figure out way how to render multi-line text using Sprites.
I've got this for rendering one line text using Sprites (I am going to skip the basic for running Pygame code - background, init etc.):
class Score(pygame.sprite.Sprite):
.
.
def update(self, lives, score):
.
.
self.text = "LIVES: %d SCORE: %d" % (lives, score)
self.image = self.font.render(self.text, True, self.color)
self.rect = self.image.get_rect()
.
.
.
.
def main():
.
.
score = pygame.sprite.RenderUpdates()
score.add(Score())
.
.
while 1:
.
.
score.clear(screen, background)
score_list = score.draw(screen)
pygame.display.update(score_list)
score.update(lives, score)
.
.
I just want to know if it's even possible to do it using my render method or if I should focus on doing it some other way?
And maybe one related question; Is this method the right way to render objects (images) in Pygame?
Thank you for any help.
You could render the strings with FONT.render first, then create a transparent surface and blit the separate text surfaces onto it. To figure out the width of the transparent base image, you can use the .get_width() method of the separate text surfaces and for the height you can use FONT.get_height().
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
FONT = pg.font.Font(None, 40)
BLUE = pg.Color('dodgerblue1')
class Score(pg.sprite.Sprite):
def __init__(self, pos):
super(Score, self).__init__()
self.lives = 3
self.score = 0
self.rect = pg.Rect(pos, (1, 1))
self.update_image()
def update_image(self):
height = FONT.get_height()
# Put the rendered text surfaces into this list.
text_surfaces = []
for txt in ('lives {}'.format(self.lives),
'score {}'.format(self.score)):
text_surfaces.append(FONT.render(txt, True, BLUE))
# The width of the widest surface.
width = max(txt_surf.get_width() for txt_surf in text_surfaces)
# A transparent surface onto which we blit the text surfaces.
self.image = pg.Surface((width, height*2), pg.SRCALPHA)
for y, txt_surf in enumerate(text_surfaces):
self.image.blit(txt_surf, (0, y*height))
# Get a new rect (if you maybe want to make the text clickable).
self.rect = self.image.get_rect(topleft=self.rect.topleft)
def main():
score = Score((100, 100))
all_sprites = pg.sprite.Group(score)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_s:
# Increment the score and update the image.
# It would be nicer to turn the score into a
# property and update the image automatically.
score.score += 1
score.update_image()
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()

circle inside a rect with a button click

I am trying to create a game and i have to make this plexus made of buttons. Every time the player clicks the button, it should appear a small circle inside the pressed button. I tried to make a draw_circle function but when i place it on the action variable inside the buttons, I get this errror. Global name x is not defined.
import pygame
pygame.init()
display_width = 900
display_height = 600
black = (0,0,0)
white = (255,255,255)
red = (250,0,0)
green = (0,250,0)
bright_red =(200,0,0)
bright_green = (0,200,0)
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("A MATH'S GAME")
def draw_circle():
pygame.draw.circle(gameDisplay,black,(x,y),100)
def button(msg,x,y,w,h,ic,ac,action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay, ac,(x,y,w,h))
if click[0] == 1 and action != None:
action()
else:
pygame.draw.rect(gameDisplay, ic,(x,y,w,h))
smallText = pygame.font.SysFont("comicsansms",20)
textSurf, textRect = text_objects(msg, smallText)
textRect.center = ( (x+(w/2)), (y+(h/2)) )
gameDisplay.blit(textSurf, textRect)
def text_objects(text, font):
textSurface = font.render(text, True, black)
return textSurface, textSurface.get_rect()
def messgae_display(text):
largeText = pygame.font.Font("Arial",60)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
pygame.display.update()
def quitgame():
pygame.quit()
quit()
def game_intro():
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
largeText = pygame.font.SysFont("comicsansms",115)
TextSurf, TextRect = text_objects("A math's game", largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
button("GO!",150,450,100,50,green,bright_green,draw_circle)
button("Quit",550,450,100,50,red,bright_red,quitgame)
pygame.display.update()
def game_loop():
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
largeText = pygame.font.SysFont("comicsansms",50)
TextSurf, TextRect = text_objects("A math's game", largeText)
TextRect.center = ((display_width/2),100)
gameDisplay.blit(TextSurf, TextRect)
button(None,200,250,50,50,bright_red,red,None)
button(None,200,300,50,50,bright_red,red,None)
button(None,200,350,50,50,bright_red,red,None)
button(None,200,400,50,50,bright_red,red,None)
button(None,200,450,50,50,bright_red,red,None)
button(None,200,500,50,50,bright_red,red,None)
button(None,250,250,50,50,bright_red,red,None)
button(None,250,300,50,50,bright_red,red,None)
button(None,250,350,50,50,bright_red,red,None)
button(None,250,400,50,50,bright_red,red,None)
button(None,250,450,50,50,bright_red,red,None)
button(None,250,500,50,50,bright_red,red,None)
button(None,300,250,50,50,bright_red,red,None)
button(None,300,300,50,50,bright_red,red,None)
button(None,300,350,50,50,bright_red,red,None)
button(None,300,400,50,50,bright_red,red,None)
button(None,300,450,50,50,bright_red,red,None)
button(None,300,500,50,50,bright_red,red,None)
button(None,350,250,50,50,bright_red,red,None)
button(None,350,300,50,50,bright_red,red,None)
button(None,350,350,50,50,bright_red,red,None)
button(None,350,400,50,50,bright_red,red,None)
button(None,350,450,50,50,bright_red,red,None)
button(None,350,500,50,50,bright_red,red,None)
button(None,400,250,50,50,bright_red,red,None)
button(None,400,300,50,50,bright_red,red,None)
button(None,400,350,50,50,bright_red,red,None)
button(None,400,400,50,50,bright_red,red,None)
button(None,400,450,50,50,bright_red,red,None)
button(None,400,500,50,50,bright_red,red,None)
button(None,450,250,50,50,bright_red,red,None)
button(None,450,300,50,50,bright_red,red,None)
button(None,450,350,50,50,bright_red,red,None)
button(None,450,400,50,50,bright_red,red,None)
button(None,450,450,50,50,bright_red,red,None)
button(None,450,500,50,50,bright_red,red,None)
button(None,500,250,50,50,bright_red,red,None)
button(None,500,300,50,50,bright_red,red,None)
button(None,500,350,50,50,bright_red,red,None)
button(None,500,400,50,50,bright_red,red,None)
button(None,500,450,50,50,bright_red,red,None)
button(None,500,500,50,50,bright_red,red,None)
button(None,550,250,50,50,bright_red,red,None)
button(None,550,300,50,50,bright_red,red,None)
button(None,550,350,50,50,bright_red,red,None)
button(None,550,400,50,50,bright_red,red,None)
button(None,550,450,50,50,bright_red,red,None)
button(None,550,500,50,50,bright_red,red,None)
button(None,600,250,50,50,bright_red,red,None)
button(None,600,300,50,50,bright_red,red,None)
button(None,600,350,50,50,bright_red,red,None)
button(None,600,400,50,50,bright_red,red,None)
button(None,600,450,50,50,bright_red,red,None)
button(None,600,500,50,50,bright_red,red,None)
button(None,650,250,50,50,bright_red,red,None)
button(None,650,300,50,50,bright_red,red,None)
button(None,650,350,50,50,bright_red,red,None)
button(None,650,400,50,50,bright_red,red,None)
button(None,650,450,50,50,bright_red,red,None)
button(None,650,500,50,50,bright_red,red,None)
pygame.display.update()
pygame.init()
game_intro()
In the line button("GO!",150,450,100,50,green,bright_green,draw_circle) you aren't giving draw_circle a value for x or y, so they are, in fact, not defined. You need to pass these down to to the draw_circle function.
Inside of your button function, pass the arguments down to draw_circle like:
if click[0] == 1 and action != None:
action(x, y)

Optimizing Keyboard Input in Pygame

I am creating a very simple game using the pygame library in which the player moves a square around the screen with the arrow keys. Unfortunately, the response of the controls are proving to be a problem.
The controls work fine until you switch direction e.g. left to right. When the player switches direction, the square the player is controlling jars to a halt.
The code for the controls looks like this in my game:
def move(self, event):
speed = 6
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.rect.x -= speed
if event.key == pygame.K_RIGHT:
self.rect.x += speed
if event.key == pygame.K_DOWN:
self.rect.y += speed
if event.key == pygame.K_UP:
self.rect.y -= speed
This method is located in a class called Block (I stole Block from the pygame docs) that just makes a sprite for the player alongside providing movement.
The function move() is actually called inside the main.py file like this:
while going:
clock.tick(60)
# event handling
for event in pygame.event.get():
if event.type == QUIT:
going = False
# controller input
player.move(event)
Where player is an instance of the Block class and event is just an item from the event queue of my game.
I tried using the pygame.key.get_pressed function (which did work better) but I would rather use the pygame.KEYDOWN locals alternatively because it returns the key presses as events in the event queue rather than booleans returned from a list.
If you are more investigative than your average joe you can take a look at the "full" game here: https://github.com/catmes/Project_React
You will need pygame 1.9.2 and python 2.7. Keep in my mind that the game itself is more a learning exercise so don't surprised when you see bad code (that will be fixed later of course ;) ).
Thanks for stopping by and giving this question a peek, I do appreciate your time.
I've tried out a smooth way to move around a player that works pretty well. It basically keeps track of the key presses in a list and moves the player based on the last key pressed. When you press a key it'll add a speed variable at the first position in the list, and when you release the key it'll remove that variable. In the update method you'll add the first speed variable of the list to corresponding x and y value.
import pygame
pygame.init()
screen = pygame.display.set_mode((720, 480))
class Player(pygame.sprite.Sprite):
def __init__(self):
super(Player, self).__init__()
self.rect = pygame.Rect((0, 0), (32, 32))
self.image = pygame.Surface((32, 32))
self.image.fill((255, 255, 255))
self.velocity = [0, 0] # It's current velocity.
self.speed = 4 # The speed the player will move.
self.dx = [] # Keeps track of the horizontal movement.
self.dy = [] # Keeps track of the vertical movement.
def update(self):
try:
self.rect.x += self.dx[0] # Index error if the list is empty.
except IndexError:
self.rect.x += 0
try:
self.rect.y += self.dy[0] # Index error if the list is empty.
except IndexError:
self.rect.y += 0
player = Player()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise SystemExit
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
player.dx.insert(0, -player.speed)
elif event.key == pygame.K_d:
player.dx.insert(0, player.speed)
elif event.key == pygame.K_w:
player.dy.insert(0, -player.speed)
elif event.key == pygame.K_s:
player.dy.insert(0, player.speed)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.dx.remove(-player.speed)
elif event.key == pygame.K_d:
player.dx.remove(player.speed)
elif event.key == pygame.K_w:
player.dy.remove(-player.speed)
elif event.key == pygame.K_s:
player.dy.remove(player.speed)
player.update()
screen.fill((0, 0, 0))
screen.blit(player.image, player.rect)
pygame.display.update()
# print player.dx, player.dy # Uncomment to see what's happening in action!

Pygame: Movement Stops When Running a function twice [duplicate]

I have made a list of bullets and a list of sprites using the classes below. How do I detect if a bullet collides with a sprite and then delete that sprite and the bullet?
#Define the sprite class
class Sprite:
def __init__(self,x,y, name):
self.x=x
self.y=y
self.image = pygame.image.load(name)
self.rect = self.image.get_rect()
def render(self):
window.blit(self.image, (self.x,self.y))
# Define the bullet class to create bullets
class Bullet:
def __init__(self,x,y):
self.x = x + 23
self.y = y
self.bullet = pygame.image.load("user_bullet.BMP")
self.rect = self.bullet.get_rect()
def render(self):
window.blit(self.bullet, (self.x, self.y))
In PyGame, collision detection is done using pygame.Rect objects. The Rect object offers various methods for detecting collisions between objects. Even the collision between a rectangular and circular object such as a paddle and a ball can be detected by a collision between two rectangular objects, the paddle and the bounding rectangle of the ball.
Some examples:
pygame.Rect.collidepoint:
Test if a point is inside a rectangle
repl.it/#Rabbid76/PyGame-collidepoint
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(100, 100)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
point = pygame.mouse.get_pos()
collide = rect.collidepoint(point)
color = (255, 0, 0) if collide else (255, 255, 255)
window.fill(0)
pygame.draw.rect(window, color, rect)
pygame.display.flip()
pygame.quit()
exit()
pygame.Rect.colliderect
Test if two rectangles overlap
See also How to detect collisions between two rectangular objects or images in pygame
repl.it/#Rabbid76/PyGame-colliderect
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
rect1 = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
rect2 = pygame.Rect(0, 0, 75, 75)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
rect2.center = pygame.mouse.get_pos()
collide = rect1.colliderect(rect2)
color = (255, 0, 0) if collide else (255, 255, 255)
window.fill(0)
pygame.draw.rect(window, color, rect1)
pygame.draw.rect(window, (0, 255, 0), rect2, 6, 1)
pygame.display.flip()
pygame.quit()
exit()
Furthermore, pygame.Rect.collidelist and pygame.Rect.collidelistall can be used for the collision test between a rectangle and a list of rectangles. pygame.Rect.collidedict and pygame.Rect.collidedictall can be used for the collision test between a rectangle and a dictionary of rectangles.
The collision of pygame.sprite.Sprite and pygame.sprite.Group objects, can be detected by pygame.sprite.spritecollide(), pygame.sprite.groupcollide() or pygame.sprite.spritecollideany(). When using these methods, the collision detection algorithm can be specified by the collided argument:
The collided argument is a callback function used to calculate if two sprites are colliding.
Possible collided callables are collide_rect, collide_rect_ratio, collide_circle, collide_circle_ratio, collide_mask
Some examples:
pygame.sprite.spritecollide()
repl.it/#Rabbid76/PyGame-spritecollide
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
sprite1 = pygame.sprite.Sprite()
sprite1.image = pygame.Surface((75, 75))
sprite1.image.fill((255, 0, 0))
sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
sprite2 = pygame.sprite.Sprite()
sprite2.image = pygame.Surface((75, 75))
sprite2.image.fill((0, 255, 0))
sprite2.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
all_group = pygame.sprite.Group([sprite2, sprite1])
test_group = pygame.sprite.Group(sprite2)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
sprite1.rect.center = pygame.mouse.get_pos()
collide = pygame.sprite.spritecollide(sprite1, test_group, False)
window.fill(0)
all_group.draw(window)
for s in collide:
pygame.draw.rect(window, (255, 255, 255), s.rect, 5, 1)
pygame.display.flip()
pygame.quit()
exit()
For a collision with masks, see How can I make a collision mask? or Pygame mask collision
See also Collision and Intersection
pygame.sprite.spritecollide() / collide_circle
repl.it/#Rabbid76/PyGame-spritecollidecollidecircle
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
sprite1 = pygame.sprite.Sprite()
sprite1.image = pygame.Surface((80, 80), pygame.SRCALPHA)
pygame.draw.circle(sprite1.image, (255, 0, 0), (40, 40), 40)
sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(80, 80)
sprite1.radius = 40
sprite2 = pygame.sprite.Sprite()
sprite2.image = pygame.Surface((80, 89), pygame.SRCALPHA)
pygame.draw.circle(sprite2.image, (0, 255, 0), (40, 40), 40)
sprite2.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(80, 80)
sprite2.radius = 40
all_group = pygame.sprite.Group([sprite2, sprite1])
test_group = pygame.sprite.Group(sprite2)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
sprite1.rect.center = pygame.mouse.get_pos()
collide = pygame.sprite.spritecollide(sprite1, test_group, False, pygame.sprite.collide_circle)
window.fill(0)
all_group.draw(window)
for s in collide:
pygame.draw.circle(window, (255, 255, 255), s.rect.center, s.rect.width // 2, 5)
pygame.display.flip()
pygame.quit()
exit()
What does this all mean for your code?
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. The position of the rectangle can be specified by a keyword argument. For example, the centre of the rectangle can be specified with the keyword argument center. These keyword arguments are applied to the attributes of the pygame.Rect before it is returned (see pygame.Rect for a list of the keyword arguments).
See *Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
You do not need the x and y attributes of Sprite and Bullet at all. Use the position of the rect attribute instead:
#Define the sprite class
class Sprite:
def __init__(self, x, y, name):
self.image = pygame.image.load(name)
self.rect = self.image.get_rect(topleft = (x, y))
def render(self):
window.blit(self.image, self.rect)
# Define the bullet class to create bullets
class Bullet:
def __init__(self, x, y):
self.bullet = pygame.image.load("user_bullet.BMP")
self.rect = self.bullet.get_rect(topleft = (x + 23, y))
def render(self):
window.blit(self.bullet, self.rect)
Use pygame.Rect.colliderect() to detect collisions between instances of Sprite and Bullet.
See How to detect collisions between two rectangular objects or images in pygame:
my_sprite = Sprite(sx, sy, name)
my_bullet = Bullet(by, by)
while True:
# [...]
if my_sprite.rect.colliderect(my_bullet.rect):
printe("hit")
From what I understand of pygame you just need to check if the two rectangles overlap using the colliderect method. One way to do it is to have a method in your Bullet class that checks for collisions:
def is_collided_with(self, sprite):
return self.rect.colliderect(sprite.rect)
Then you can call it like:
sprite = Sprite(10, 10, 'my_sprite')
bullet = Bullet(20, 10)
if bullet.is_collided_with(sprite):
print('collision!')
bullet.kill()
sprite.kill()
There is a very simple method for what you are trying to do using built in methods.
here is an example.
import pygame
import sys
class Sprite(pygame.sprite.Sprite):
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20, 20])
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect()
self.rect.center = pos
def main():
pygame.init()
clock = pygame.time.Clock()
fps = 50
bg = [255, 255, 255]
size =[200, 200]
screen = pygame.display.set_mode(size)
player = Sprite([40, 50])
player.move = [pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN]
player.vx = 5
player.vy = 5
wall = Sprite([100, 60])
wall_group = pygame.sprite.Group()
wall_group.add(wall)
player_group = pygame.sprite.Group()
player_group.add(player)
# I added loop for a better exit from the game
loop = 1
while loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = 0
key = pygame.key.get_pressed()
for i in range(2):
if key[player.move[i]]:
player.rect.x += player.vx * [-1, 1][i]
for i in range(2):
if key[player.move[2:4][i]]:
player.rect.y += player.vy * [-1, 1][i]
screen.fill(bg)
# first parameter takes a single sprite
# second parameter takes sprite groups
# third parameter is a do kill command if true
# all group objects colliding with the first parameter object will be
# destroyed. The first parameter could be bullets and the second one
# targets although the bullet is not destroyed but can be done with
# simple trick bellow
hit = pygame.sprite.spritecollide(player, wall_group, True)
if hit:
# if collision is detected call a function in your case destroy
# bullet
player.image.fill((255, 255, 255))
player_group.draw(screen)
wall_group.draw(screen)
pygame.display.update()
clock.tick(fps)
pygame.quit()
# sys.exit
if __name__ == '__main__':
main()
Make a group for the bullets, and then add the bullets to the group.
What I would do is this:
In the class for the player:
def collideWithBullet(self):
if pygame.sprite.spritecollideany(self, 'groupName'):
print("CollideWithBullet!!")
return True
And in the main loop somewhere:
def run(self):
if self.player.collideWithBullet():
print("Game Over")
Hopefully that works for you!!!
Inside the Sprite class, try adding a self.mask attribute with
self.mask = pygame.mask.from_surface(self.image)
and a collide_mask function inside of the Sprite class with this code:
def collide_mask(self, mask):
collided = False
mask_outline = mask.outline()
self.mask_outline = self.mask.outline()
for point in range(len(mask_outline)):
mask_outline[point] = list(mask_outline[point])
mask_outline[point][0] += bullet.x
mask_outline[point][1] += bullet.y
for point in range(len(self.mask_outline)):
self.mask_outline[point] = list(mask_outline[point])
self.mask_outline[point][0] += self.x
self.mask_outline[point][1] += self.y
for point in mask_outline:
for self_mask_point in self.mask_outline:
if point = self_mask_point:
collided = True
return collided

Sprites not aligning to boundaries correctly

I am creating a basic Pong-style game using Pygame, and I have gotten as far as moving the paddles. I have code that should prevent the paddles from moving beyond the edges of the screen, but if a player holds down a movement key, the paddle moves just slightly past the edge of the screen. Once the player releases the key, the paddle snaps back to where it ought to stop.
I am somewhat new to Python and coding in general, so my code may not be the tidiest or most efficient. While this problem may not affect gameplay at all, I would like to know why it behaves this way and how to change it to how I want it.
My code is:
import random
import pygame
from pygame.locals import *
from sys import exit
screen_size = 600, 600
w,h = screen_size
screen = pygame.display.set_mode(screen_size)
clock = pygame.time.Clock()
pic = pygame.image.load
class paddle(pygame.sprite.Sprite):
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.pos = pos
self.posx, self.posy = self.pos
self.width = 10
self.height = 100
self.image = pygame.Surface((self.width, self.height))
self.image.fill((255,255,255))
self.rect = pygame.Rect((0,0), self.image.get_size())
self.speed = 150
def update(self, mov, tp):
self.posy += mov * self.speed * tp
self.rect.center = self.posx, self.posy
class box(pygame.sprite.Sprite):
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.pos = pos
self.posx, self.posy = self.pos
self.width = 10
self.height = 10
self.image = pygame.Surface((self.width, self.height))
self.image.fill((255,255,255))
self.rect = pygame.Rect((0,0), self.image.get_size())
self.speed = 300
self.directionx = 0
self.directiony = 0
def update(self, mov, tp):
self.posx += mov[0] * self.speed * tp
self.posy += mov[1] * self.speed * tp
self.rect.center = self.posx, self.posy
reset = True
done = False
while done == False:
if reset == True:
p1 = paddle((0,0))
p1 = paddle((20,(h-p1.height)/2))
p2 = paddle((0,0))
p2 = paddle((w-20,(h-p2.height)/2))
paddle_group = pygame.sprite.Group()
paddle_group.add(p1)
paddle_group.add(p2)
ball = box(((w/2)-10,h/2))
ball_group = pygame.sprite.Group(ball)
reset = False
else:
pass
time_passed = clock.tick(60)
time_passed_seconds = time_passed/1000.0
for event in pygame.event.get():
if event.type == QUIT:
done = True
p1_movey = 0
p2_movey = 0
pressed_keys = pygame.key.get_pressed()
pressed_mb = pygame.mouse.get_pressed()
if pressed_keys[K_ESCAPE]:
done = True
if pressed_keys[K_w]:
p1_movey = -2
elif pressed_keys[K_s]:
p1_movey = +2
if pressed_keys[K_UP]:
p2_movey = -2
elif pressed_keys[K_DOWN]:
p2_movey = +2
p1.update(p1_movey, time_passed_seconds)
p2.update(p2_movey, time_passed_seconds)
# This is where the border check is
for PADDLE in paddle_group.sprites():
if PADDLE.posy > h - (PADDLE.height/2):
PADDLE.posy = h - (PADDLE.height/2)
elif PADDLE.posy < (PADDLE.height/2):
PADDLE.posy = (PADDLE.height/2)
screen.fill((0,0,0))
paddle_group.draw(screen)
pygame.display.update()
pygame.quit()
The boundary part is marked with a comment.
The problem is that you're correcting each paddle's posy without adjusting its rect at the same time. posx and posy store the location of your sprite-- in this case, it's center-- but the position of what you see on-screen is determined by the rect. Because you add p#_movey, then update (which adjusts the rect), then, finally, make the posy correction for out-of-bounds values, rect remains at it's invalid location. Because you have adjusted posy, though, future p#_movey changes effect the correct location, not the invalid one (which is why your sprite remains O.B. until the movement key is released).
In short, this is what's going on:
Determine p#_movey for each player.
Apply p#_movey to player #'s paddle's posy and rect via update.
Adjust PADDLE.posy to keep it in-bounds for each PADDLE in paddle_group, but do not update PADDLE.rect to reflect the adjustment. (And rect does need to be updated.)
Conclude the frame.
There are two solutions I can think of:
1) Modify your for PADDLE in paddle_group.sprites() loop so that PADDLE.rect.center is refreshed, that it looks something like this:
# on the main loop...
for PADDLE in paddle_group.sprites():
if PADDLE.posy > h - (PADDLE.height/2):
PADDLE.posy = h - (PADDLE.height/2)
elif PADDLE.posy < (PADDLE.height/2):
PADDLE.posy = (PADDLE.height/2)
PADDLE.rect.center = PADDLE.posx, PADDLE.posy
## ^ adding this line, which recenters the rect.
2) Alternatively, you could run the boundary check in paddle's update method and remove the for PADDLE in paddle_group.sprites() loop from the main loop all together. paddle.update(..) would then look something like this:
# in class paddle(..) ...
def update(self, mov, tp):
self.posy += mov * self.speed * tp
self.posy = max(self.height / 2, min(self.posy, h - (self.height / 2)))
## ^ Adding this line, which checks boundaries for posy.
## because you're doing it here, there's no need to do it again on the main loop.
self.rect.center = self.posx, self.posy
... as long as your boundaries are defined by h (screen height; the bottom of your window) and 0 (the top of the window) and the height of the paddle.
Either solution should work, though there are almost certainly third ways, too.
Hope that helps!