Creating collision in pygame [closed] - python

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
i'm having a problem with my code. So, I want to create a game, in Pygame, where the bananas fall from the sky, and the monkey have to grab them. I'm having quite a hard time creating a collision between those two (spent hours trying already).
So, this is my code:
import pygame, sys, random, time, os
from pygame.locals import *
#Variáveis necessárias
banana_speed = 5
monkey_speed = 20
WIDTH = 800
HEIGHT = 800
pontos = 0
vidas = 3
#Nome do jogo
pygame.display.set_caption("Catch the fruit")
#Tamanho do ecrã do jogo
screen = pygame.display.set_mode((WIDTH, HEIGHT))
class Macaco(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.image.load('monkey.png')
self.rect = self.image
self.x = 300
self.y = 640
def keyboard(self):
key = pygame.key.get_pressed()
if key[pygame.K_RIGHT]:
self.x += monkey_speed
elif key[pygame.K_LEFT]:
self.x -= monkey_speed
def draw (self, screen):
screen.blit(self.rect, (self.x, self.y))
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
class Banana(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.image.load('banana.png')
self.rect = self.image
self.x = random.randrange(0,WIDTH)
self.y = -50
def draw(self, screen):
self.y = self.y + banana_speed
screen.blit(self.rect,(self.x, self.y))
#Funções necessárias para o Loop
macaco = Macaco()
banana = Banana()
Background = Background('background.png', [0,0])
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
while vidas > 0:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
elif event.type == QUIT:
running = False
screen.blit(Background.image, Background.rect)
pressed_keys = pygame.key.get_pressed()
macaco.keyboard()
macaco.draw(screen)
banana.draw(screen)
pygame.display.update()
edit:
So i'm trying another solution and did this:
import pygame, sys, random, time, os
from pygame.locals import *
#Variáveis necessárias
banana_speed = 5
monkey_speed = 20
WIDTH = 800
HEIGHT = 800
pontos = 0
vidas = 3
green = (0, 0 , 255)
#Nome do jogo
pygame.display.set_caption("Catch the fruit")
#Tamanho do ecrã do jogo
screen = pygame.display.set_mode((WIDTH, HEIGHT))
class Macaco(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.image.load('monkey.png')
self.image = pygame.Surface([WIDTH, HEIGHT])
self.x = 300
self.y = 640
self.image.fill(green)
def keyboard(self):
key = pygame.key.get_pressed()
if key[pygame.K_RIGHT]:
self.x += monkey_speed
elif key[pygame.K_LEFT]:
self.x -= monkey_speed
def draw (self, screen):
screen.blit(self.rect, (self.x, self.y))
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
class Banana(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.image.load('banana.png')
self.image = pygame.Surface([WIDTH, HEIGHT])
self.x = random.randrange(0, WIDTH)
self.y = -50
def draw(self, screen):
self.y = self.y + banana_speed
screen.blit(self.rect,(self.x, self.y))
#Funções necessárias para o Loop
macaco = Macaco()
banana = Banana()
Background = Background('background.png', [0,0])
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
while vidas > 0:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
elif event.type == QUIT:
running = False
screen.blit(Background.image, Background.rect)
pressed_keys = pygame.key.get_pressed()
macaco.keyboard()
macaco.draw(screen)
banana.draw(screen)
blocks_hit_list = pygame.sprite.spritecollide(macaco, banana, True)
for blocks in blocks_hit_list:
pontos +=1
pygame.display.update()
Could you give me some help please?

I've created two sprite groups: all_sprites which contains all sprites and is used to update and draw them with just two lines of code, and the bananas group which is used to check for collisions between the macaco and the bananas. Then we need the pygame.sprite.spritecollide function to get the collided sprites. You have to pass a sprite instance and a sprite group and it'll check for you if the sprite has collided with the sprites in the group. It returns the collided bananas as a list over which you can iterate to do something with them or for example to increment a points counter.
You have to call the __init__ method of the parent class in your sprites to be able to use them correctly with sprite groups (do that with the super function super().__init__() (in Python 2 super(Macaco, self).__init__()).
To update the positions of your sprites, set their topleft (or center) attribute to the new x, y coordinates.
To get a rect from an image call self.rect = self.image.get_rect().
import sys
import random
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 800))
class Macaco(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((30, 30))
self.image.fill((190, 140, 20))
self.rect = self.image.get_rect()
self.x = 300
self.y = 640
self.speed = 20
def keyboard(self, keys):
if keys[pygame.K_RIGHT]:
self.x += self.speed
elif keys[pygame.K_LEFT]:
self.x -= self.speed
def update(self):
self.rect.topleft = self.x, self.y
class Banana(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((30, 30))
self.image.fill((230, 230, 40))
self.rect = self.image.get_rect()
self.x = random.randrange(0, 770)
self.y = -50
self.speed = 5
def update(self):
self.y += self.speed
self.rect.topleft = self.x, self.y
macaco = Macaco()
banana = Banana()
all_sprites = pygame.sprite.Group(macaco, banana)
bananas = pygame.sprite.Group(banana)
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
keys = pygame.key.get_pressed()
macaco.keyboard(keys)
all_sprites.update()
# Collision detection. Check if macaco collided with bananas group,
# return collided bananas as a list. dokill argument is True, so
# that collided bananas will be deleted.
collided_bananas = pygame.sprite.spritecollide(macaco, bananas, True)
for collided_banana in collided_bananas:
print('Collision.')
screen.fill((70, 40, 70))
all_sprites.draw(screen)
pygame.display.update()
clock.tick(30)
pygame.quit()
sys.exit()

Related

Pygame mask collisions not detected [duplicate]

I have made a putt-putt game and now I want to add a slanted wall type. Because of this, I need to use masks for the collision (until now I have just used rects). I have spent hours learning about masks and trying to figure out why my code won't work. There are no errors, the collision just isn't detected.
I have simplified my code down to something much smaller just as a way for me to test it efficiently. From everything I've seen this seems like it should work, but it doesnt. Here it is:
import pygame
# Pygame init stuff
pygame.init()
wind_width = 1200
wind_height = 700
gameDisplay = pygame.display.set_mode((wind_width, wind_height))
pygame.display.set_caption("Mini Golf!")
pygame.display.update()
gameExit = False
clock = pygame.time.Clock()
# Class setups
class Ball:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("sball.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image)
def render(self):
self.rect.topleft = (self.x, self.y)
gameDisplay.blit(self.image, self.rect)
class Slant:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("posslant.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image)
def render(self):
self.rect.topleft = (self.x, self.y)
gameDisplay.blit(self.image, self.rect)
# Creating objects
ball = Ball(250, 250)
slant = Slant(270, 250)
# Game loop
gameExit = False
while not(gameExit):
# Moves ball
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
ball.y -= 1
elif event.key == pygame.K_DOWN:
ball.y += 1
elif event.key == pygame.K_LEFT:
ball.x -= 1
elif event.key == pygame.K_RIGHT:
ball.x += 1
# Collision detection
offset_x, offset_y = (slant.rect.x - ball.rect.x), (slant.rect.y - ball.rect.y)
if slant.mask.overlap(ball.mask, (offset_x, offset_y)):
print("hit")
# Draws everything
gameDisplay.fill((0, 0, 0))
ball.render()
slant.render()
pygame.display.update()
clock.tick(100)
The offset parameter of the method overlap() is the relative position of the othermask in relation to the pygame.mask.Mask object.
So the offset is calculated by subtracting the coordinates of slant from the coordinates of ball:
offset_x, offset_y = (slant.rect.x - ball.rect.x), (slant.rect.y - ball.rect.y)
offset = (ball.rect.x - slant.rect.x), (ball.rect.y - slant.rect.y)
if slant.mask.overlap(ball.mask, offset):
print("hit")
When you create the mask images, then I recommend to ensure that the image has per pixel alpha format by calling .convert_alpha():
class Ball:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("sball.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image.convert_alpha()) # <---
class Slant:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("posslant.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image.image.convert_alpha()) # <---
Minimal example: repl.it/#Rabbid76/PyGame-SurfaceMaskIntersect
See also: Mask

Pygame sprite rotation changes its collision [duplicate]

I have made a putt-putt game and now I want to add a slanted wall type. Because of this, I need to use masks for the collision (until now I have just used rects). I have spent hours learning about masks and trying to figure out why my code won't work. There are no errors, the collision just isn't detected.
I have simplified my code down to something much smaller just as a way for me to test it efficiently. From everything I've seen this seems like it should work, but it doesnt. Here it is:
import pygame
# Pygame init stuff
pygame.init()
wind_width = 1200
wind_height = 700
gameDisplay = pygame.display.set_mode((wind_width, wind_height))
pygame.display.set_caption("Mini Golf!")
pygame.display.update()
gameExit = False
clock = pygame.time.Clock()
# Class setups
class Ball:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("sball.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image)
def render(self):
self.rect.topleft = (self.x, self.y)
gameDisplay.blit(self.image, self.rect)
class Slant:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("posslant.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image)
def render(self):
self.rect.topleft = (self.x, self.y)
gameDisplay.blit(self.image, self.rect)
# Creating objects
ball = Ball(250, 250)
slant = Slant(270, 250)
# Game loop
gameExit = False
while not(gameExit):
# Moves ball
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
ball.y -= 1
elif event.key == pygame.K_DOWN:
ball.y += 1
elif event.key == pygame.K_LEFT:
ball.x -= 1
elif event.key == pygame.K_RIGHT:
ball.x += 1
# Collision detection
offset_x, offset_y = (slant.rect.x - ball.rect.x), (slant.rect.y - ball.rect.y)
if slant.mask.overlap(ball.mask, (offset_x, offset_y)):
print("hit")
# Draws everything
gameDisplay.fill((0, 0, 0))
ball.render()
slant.render()
pygame.display.update()
clock.tick(100)
The offset parameter of the method overlap() is the relative position of the othermask in relation to the pygame.mask.Mask object.
So the offset is calculated by subtracting the coordinates of slant from the coordinates of ball:
offset_x, offset_y = (slant.rect.x - ball.rect.x), (slant.rect.y - ball.rect.y)
offset = (ball.rect.x - slant.rect.x), (ball.rect.y - slant.rect.y)
if slant.mask.overlap(ball.mask, offset):
print("hit")
When you create the mask images, then I recommend to ensure that the image has per pixel alpha format by calling .convert_alpha():
class Ball:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("sball.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image.convert_alpha()) # <---
class Slant:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("posslant.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image.image.convert_alpha()) # <---
Minimal example: repl.it/#Rabbid76/PyGame-SurfaceMaskIntersect
See also: Mask

My pygame player sprite is not moving and/or updating

I am trying to build a simple "flappy bird" like game. I am trying to sort all the code into classes and methods. How do I fix this problem? Is it the code not working because of calling some method too early or is it because there's something missing? I would really love it if someone would try to explain to me.
sprites.py:
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 50))
self.image.fill((255, 255, 0))
self.rect = self.image.get_rect()
self.rect.x = 0
self.rect.y = (700 / 2)
self.movex = 0
self.movey = 0
def control(self, x, y):
self.movex += x
self.movey += y
def update(self):
self.rect.x += self.movex
self.rect.y += self.movey
def animate(self):
pass
class Obstacle(pygame.sprite.Sprite):
def __init__(self, x, y, width, height):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.width = width
self.height = height
main.py:
from sprites import *
import pygame
WIDTH = 700
HEIGHT = 700
class Game:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
self.clock = pygame.time.Clock()
self.score = 0
self.running = True
def new(self):
pass
def events(self):
self.game_on = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.game_on = False
self.running = False
if event.type == pygame.KEYDOWN:
if event.type == pygame.K_UP:
self.croc.control(0, -20)
def update(self):
self.croc = Player()
self.all_sprites = pygame.sprite.Group()
self.all_sprites.add(self.croc)
self.all_sprites.update()
def draw(self):
self.screen.fill((0, 0, 0))
self.all_sprites.draw(self.screen)
pygame.display.flip()
game = Game()
while game.running:
game.clock.tick(60)
game.new()
game.events()
game.update()
game.draw()
Thank you
There are 2 mistakes. The Player object is recreated in every frame. Create the player in the constructor of Game rather than in the method update:
class Game:
def __init__(self):
# [...]
self.croc = Player() # <--- ADD
self.all_sprites = pygame.sprite.Group() # <--- ADD
self.all_sprites.add(self.croc) # <--- ADD
def update(self):
# self.croc = Player() # <--- DELETE
# self.all_sprites = pygame.sprite.Group() # <--- DELETE
# self.all_sprites.add(self.croc) # <--- DELETE
self.all_sprites.update()
There is a type in the event loop. You've to get the key from the .key attribute rather than the .type attriburte:
if event.type == pygame.K_UP:
if event.key == pygame.K_UP:
Complete code:
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 50))
self.image.fill((255, 255, 0))
self.rect = self.image.get_rect()
self.rect.x = 0
self.rect.y = (700 / 2)
self.movex = 0
self.movey = 0
def control(self, x, y):
self.movex += x
self.movey += y
def update(self):
self.rect.x += self.movex
self.rect.y += self.movey
def animate(self):
pass
class Obstacle(pygame.sprite.Sprite):
def __init__(self, x, y, width, height):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.width = width
self.height = height
WIDTH = 700
HEIGHT = 700
class Game:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
self.clock = pygame.time.Clock()
self.score = 0
self.running = True
self.croc = Player()
self.all_sprites = pygame.sprite.Group()
self.all_sprites.add(self.croc)
def new(self):
pass
def events(self):
self.game_on = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.game_on = False
self.running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
self.croc.control(0, -20)
def update(self):
self.all_sprites.update()
def draw(self):
self.screen.fill((0, 0, 0))
self.all_sprites.draw(self.screen)
pygame.display.flip()
game = Game()
while game.running:
game.clock.tick(60)
game.new()
game.events()
game.update()
game.draw()

What is wrong with this code? I can't seem to get the bullet to be shot from the rocket

Basically the rocket moves up and down, stops when it reaches the top or bottom and recognizes that the spacebar is being pressed. However the bullet won't shoot from the ship. I'm new to python and pygame. Anyways here is the code, thank you for any help!:
import pygame
import sys
from pygame.locals import*
bg_color = (0, 191, 255)
d_w = 1200
d_h= 800
class Rocket(object):
def __init__(self):
self.image = pygame.image.load('Rocket.bmp')
self.x = 0
self.y = d_h/2
def handle_keys(self):
key = pygame.key.get_pressed()
dist = 6.5
if key[pygame.K_UP]:
if self.y > 0:
self.y -= dist
elif key[pygame.K_DOWN]:
if self.y < 775:
self.y += dist
elif key[pygame.K_SPACE]:
Bullet.update(Bullet)
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface([2, 5])
self.image.fill(0, 0, 0)
self.rect = self.image.get_rect()
def update(self):
""" Move the bullet. """
self.image = pygame.Surface([2, 5])
self.image.fill((0, 0, 0))
self.rect = self.image.get_rect()
self.rect.x += 3
pygame.init()
screen = pygame.display.set_mode((d_w, d_h))
pygame.display.set_caption("Game Character")
Mario = pygame.image.load('Mario_Sprite.bmp')
rocket = Rocket()
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
rocket.handle_keys()
screen.fill(bg_color)
rocket.draw(screen)
pygame.display.update()
clock.tick(72)
Bullet.update(Bullet)
You're calling update statically. You need to create an instance of the class and then update that instance, as in:
bullet = Bullet()
...
bullet.update()

Building a keyboard controlled game in pygame. I'm getting an indentation error but I dont know why

This is my game so far. I was able to display the Player and make it move with the keyboard. On a separate program I displayed the Enemy class randomly within the range. When I combined the two programs I started getting a bunch of indentation errors. If I fix one, another one pops ups. Please help!
import pygame
import os
import random
black = (0,0,0)
white = (255,255,255)
red = (255, 0, 0)
green = (0, 100, 0)
# This class represents the bar at the bottom that the player controls
class Player(object):
def __init__(self):
self.image = pygame.image.load("player_one.png").convert()
self.image.set_colorkey(white)
self.width = 15
self.height = 15
self.x = 940
self.y = 240
def handle_keys(self):
key = pygame.key.get_pressed()
if key[pygame.K_DOWN]:
if self.y < 470:
self.y += self.height
elif key[pygame.K_UP]:
if self.y > 0:
self.y -= self.height
if key[pygame.K_RIGHT]:
if self.x < 940:
self.x += self.width
elif key[pygame.K_LEFT]:
if self.x > 0:
self.x -= self.width
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
class Enemy(object):
def __init__(self):
self.image = pygame.image.load(image).convert()
self.image.set_colorkey(white)
image_rect = image.get_rect()
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
def draw(self, screen):
surface.blit(self.image, self.rect)
def update(self):
self.rect.topleft = random.randint(60, 220+1), random.randint( 0, 475+1)
class Game():
def __init__(self):
pygame.init()
pygame.display.set_caption('Best Football Game Ever!')
self.screen = pygame.display.set_mode((1000, 500))
self.multi_enemies = []
for i in range(1, 4):
enemy = Enemy("enemy_"+str(i)+".png")
enemy.update()
self.multi_enemies.append(enemy)
def run(self):
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
for enemy in self.multi_enemies:
enemy.update()
#---updates----
# place for updates
# --- draws ---
for enemy in self.multi_enemies:
enemy.draw(self.screen)
for x in range(60,940,35):
pygame.draw.line(screen, white, [x, 0], [x, 500], 1)
player.handle_keys()
self.screen.fill(green)
pygame.display.flip()
clock.tick(20)
pygame.quit()
Game().run()
You are mixing tabs with spaces for indentation. The only way this "works" is if you have your tabstop set to 8
Since nearly everyone uses 4 spaces for indentation, a tab looks like two levels of indentation, but Python only counts it as one
Here tabs are highlighted in yellow:
Edit-Select All
then go to
Format- Untabify
That should fix your problem

Categories