How do I detect the collision of two sprites using pygame? - python

This is my code so far, I can move and it places out a blip to pick up, I just need to know how to register that and move the blip to a new random spot!
I am very new to pygame and not 100% fluent in python either, but I'm decent. If there are pdf:s good for intermediate coders when it comes to python that would be wonderful!
import sys, pygame, os, math
from random import randint
pygame.init()
size = width, height = 800, 600
speed = [2, 2]
black = 1, 1, 1
screen = pygame.display.set_mode(size)
pygame.display.set_caption('Pick up the squares!')
UP='up'
DOWN='down'
LEFT='left'
RIGHT='right'
ball = pygame.image.load("ball.png")
ballrect = ball.get_rect()
ballx = 400
bally = 300
blip = pygame.image.load("blip.png")
bliprect = blip.get_rect()
blipx = randint(1,800)
blipy = randint(1,600)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((250, 250, 250))
clock = pygame.time.Clock()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
ballx -= 5
if keys[pygame.K_RIGHT]:
ballx += 5
if keys[pygame.K_UP]:
bally -= 5
if keys[pygame.K_DOWN]:
bally +=5
screen.fill(black)
screen.blit(ball, (ballx,bally))
screen.blit(blip, (blipx, blipy))
pygame.display.update()
clock.tick(40)

Use the colliderect method of rectangles:
if ballrect.colliderect(bliprect):
print 'Do something here'

A basic collision detection works like this (assuming you are working with two rectangles):
def does_collide(rect1, rect2):
if rect1.x < rect2.x + rect2.width and rect1.x + rect1.width > rect2.x \
and rect1.y < rect2.y + rect2.height and rect1.height + rect1.y > rect2.y:
return True
return False
Fortunately Pygame is packed with such methods, so you should go with #MalikBrahimi's answer - using colliderect function call, which will do the math for you.

You can also try using pygame.sprite.spritecollide():
if pygame.sprite.spritecollide(a, b, False):
pass
#Do something
a here is your variable name for the class for one of the sprites in the collision. b here is the group that your second sprite will be. You can set the last one to True, which will remove the sprite from the group. Setting to False will keep it the way it is. Here is an example:
if pygame.sprite.spritecollide(my_ball, ballGroup, False):
Ball.speed[0] = -Ball.speed[0]
hit.play()
Ball.move()
The variable name for my sprite is my_ball. The group containing the other sprite(s) in this collision is ballGroup. I set the last one to False to keep all the sprites on the surface. If I set to True, the sprite from ballGroup will be removed from the group and screen. I hope this helps you!

Related

How to keep a sprite within boundaries in Pygame? [duplicate]

This question already has an answer here:
Setting up an invisible boundary for my sprite
(1 answer)
Closed 11 months ago.
I am looking on how to keep my sprite within the set boundaries of a window in Pygame. Could anyone here please help me keep the car sprite within the lines at all times? Thanks! (please don't come to edit my question, actually help me!)
import pygame
pygame.init()
screen = pygame.display.set_mode((300,208))
pygame.display.set_caption("TinyRacer")
car = pygame.image.load("car.png")
bg = pygame.image.load("bg.png")
run = True
y = 84
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
key = pygame.key.get_pressed()
if key[pygame.K_UP]:
y -= 16
if key[pygame.K_DOWN]:
y += 16
screen.fill((255,255,255))
screen.blit(car, (0,y))
screen.blit(bg,(0,0))
pygame.display.update()
pygame.quit()
I have tried following Techwithtim's tutorial on this, but to no avail.
If you would keep position and size in pygame.Rect() then you could use special functions to check collisions.
Or simply check
car_rect.top < screen_rect.top and screen_rect.bottom < car_rect.bottom
Or you could use contains to check if one rect is fully inside another.
I create green background which is smaller than window, and I use Rect() to check if car in inside this green region.
car_rect.y -= 16
if car_rect.top < bg_rect.top:
car_rect.top = bg_rect.top
I also use Rect() to put elements in center of screen.
car_rect.centerx = screen_rect.centerx
car_rect.centery = screen_rect.centery
The same way you can put other elements (i.e. text in center of button)
To make it simpler I use surfaces instead of images so everyone can simply copy and run it.
import pygame
pygame.init()
screen = pygame.display.set_mode((300,208))
screen_rect = screen.get_rect()
pygame.display.set_caption("TinyRacer")
#car = pygame.image.load("car.png")
car = pygame.Surface((20,10))
car.fill((255,0,0))
car_rect = car.get_rect()
car_rect.centerx = screen_rect.centerx
car_rect.centery = screen_rect.centery
#bg = pygame.image.load("bg.png")
bg = pygame.Surface((300,100))
bg.fill((0,255,0))
bg_rect = bg.get_rect()
bg_rect.centery = screen_rect.centery
clock = pygame.time.Clock()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
key = pygame.key.get_pressed()
if key[pygame.K_UP]:
car_rect.y -= 16
if car_rect.top < bg_rect.top:
car_rect.top = bg_rect.top
if key[pygame.K_DOWN]:
car_rect.y += 16
if car_rect.bottom > bg_rect.bottom:
car_rect.bottom = bg_rect.bottom
screen.fill((255,255,255))
screen.blit(bg, bg_rect)
screen.blit(car, car_rect)
pygame.display.update()
clock.tick(25) # 25FPS (it gives 40ms delay)
pygame.quit()

Trouble animating sprites (Pygame)

I am working on a school project with pygame and have unfortunately run into trouble with animating my sprites, or more specifically, changing the sprite from one to another, and I'm unsure on how to do it. Currently, I have a class of my spaceship sprite and background sprite:
game_folder = os.path.dirname(__file__)
img_folder = os.path.join(game_folder, 'img')
space_background = pygame.image.load('background.png').convert()
starship_1 = pygame.image.load('starship2.png').convert()
starship_2 = pygame.image.load('starship3.png').convert()
starship_3 = pygame.image.load('starship4.png').convert()
class starship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(starship_1, (150,150))
self.image.set_colorkey(black)
self.rect = self.image.get_rect()
self.rect.center = (400, 400)
all_sprites = pygame.sprite.Group()
BackGround = background()
StarShip = starship()
all_sprites.add(BackGround)
all_sprites.add(StarShip)
My while loop looks like this:
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and StarShip.rect.x > 25:
StarShip.rect.x -= vel
if keys[pygame.K_RIGHT] and StarShip.rect.x < 600:
StarShip.rect.x += vel
if keys[pygame.K_UP] and StarShip.rect.y > 25:
StarShip.rect.y -= vel
if keys[pygame.K_DOWN] and StarShip.rect.y < 600:
StarShip.rect.y += vel
win.fill((0,0,0))
all_sprites.update()
all_sprites.draw(win)
pygame.display.update()
pygame.quit()
This has basic movement of left/right/up/down. What I want to do is have my StarShip object constantly change between the variables starship_1, starship_2, starship_3 (which contain my 3 sprites of my starship), so it looks like the starship is moving.
My sprites look like this:
As you can see, it is the engine fire that is the difference between those sprites. How would I change between those 3 sprites every 1 second?
FYI: When the program is launched, this is what appears:
Thank you!
There are 2 parts to achieve this effect.
Create a function to change image of the sprite. ( Easy Task)
Call the above function periodically every x seconds. ( Intermediate Task)
Step 1.
You can achieve this by just setting/loading the self.image variable with the next image.
Step 2.
clock = pygame.time.Clock()
time_counter = 0
images = ['starship_1', 'starship_2', 'starship_3']
current_img_index = 0
while run:
# Your Code
time_counter = clock.tick()
# 1000 is milliseconds == 1 second. Change this as desired
if time_counter > 1000:
# Incrementing index to next image but reseting back to zero when it hits 3
current_img_index = (current_img_index + 1) % 3
set_img_function(current_img_index) # Function you make in step 1
# Reset the timer
time_counter = 0
A good approach will be to complete step 1 and then bind it to a button. Test if it works and then move on to step 2.
Some good read about the functions used in this code to fully understand them is here

Simple python/pygame pong game won't use y axis

I am working on a little project to get to know Pygame and I was working with the following tutorial to introduce myself:
https://www.101computing.net/pong-tutorial-using-pygame-getting-started/
After following that tutorial and making tweaks of my own (mainly stylistic, nothing functional), every time I run the program the ball just goes back and forth on the same y coordinate and won't go up and down. It seems like everything else works but the variation in the vertical movement of the ball.
I can provide my code if needed as well, but it looks similar to the tutorial above.
edit: here's the code
import pygame
#need random integers for velocity changes
import random
Black = (0,0,0)
#create ball object for the game, wil be a sprite object
class Ball(pygame.sprite.Sprite):
#define the package function and also call the pygame sprite constructor using super()
def __init__(self, color, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(Black)
self.image.set_colorkey(Black)
#draw the ball
pygame.draw.rect(self.image, color, [0, 0, width, height])
#set velocity
self.velocity = [random.randint(4,8), random.randint(-8,8)]
#get rectangle object from image package
self.rect = self.image.get_rect()
def update(self):
self.rect.x += self.velocity[0]
self.rect.y += self.velocity[1]
#reverse velocity path of ball hits paddle
def bounce(self):
self.velocity[0] = -self.velocity[0]
self.velocity[1] = random.randint(-8,8)
-----Main File-----
import pygame
#import paddle sprites
from paddle import Paddle
#import ball
from ball import Ball
import time
pygame.init()
#set local colors: Black for background, white for text, blue and red for teams
Black = (0,0,0)
White = (255,255,255)
Red = (255,0,0)
Blue = (0,0,255)
#create paddles using paddle class and add them to a list of sprites
paddleLeft = Paddle(Red, 10, 100)
paddleLeft.rect.x = 20
paddleLeft.rect.y = 200
paddleRight = Paddle(Blue, 10, 100)
paddleRight.rect.x = 870
paddleRight.rect.y = 200
ball = Ball(White, 10, 10)
ball.rect.x = 445
ball.rect.y = 195
allSprites = pygame.sprite.Group()
allSprites.add(paddleLeft)
allSprites.add(paddleRight)
allSprites.add(ball)
#set game window
size = (900,500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Multiplayer Pong")
#to the functionality, we will have a while loop that will listen to user inputs, adding logic to the game (score, boundaries, etc.), and refreshing the program
#global "running" funtion that will control the while loop, simple bool
running = True
#need a clock for refreshing the screen (included in pygame package)
clock = pygame.time.Clock()
#scores for each side
scoreLeft = 0
scoreRight = 0
#start loop
while running:
#--listen for inputs
for event in pygame.event.get():
if event.type == pygame.QUIT: #if quit button is pressed, leave
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_x:
running = False
#keyboard inputs
key = pygame.key.get_pressed()
if key[pygame.K_w]:
paddleLeft.mUp(5)
if key[pygame.K_s]:
paddleLeft.mDown(5)
if key[pygame.K_UP]:
paddleRight.mUp(5)
if key[pygame.K_DOWN]:
paddleRight.mDown(5)
#--logic
allSprites.update()
#--drawing here (paddles, screen, scores, boundaries, etc
screen.fill(Black)
pygame.draw.line(screen, White, [448, 0], [448, 500], 4)
allSprites.draw(screen)
#check for wall bounce
#algorithms for bounce look like
#Hits right or left wall? reverse X-bound velocity
#Hits top or bottom wall? reverse Y-bound velocity
if ball.rect.x >= 890:
scoreLeft += 1
ball.rect.x = 445
ball.rect.y = 195
time.sleep(2)
ball.velocity[0] = -ball.velocity[0]
if ball.rect.x <= 0:
scoreRight += 1
ball.rect.x = 445
ball.rect.y = 195
time.sleep(2)
ball.velocity[0] = -ball.velocity[0]
#reverse ball angle
if ball.rect.y >= 490:
ball.velocity[1] = -ball.velocity[1]
if ball.rect.y >= 0:
ball.velocity[1] = -ball.velocity[1]
#check for paddle hit
if pygame.sprite.collide_mask(ball, paddleLeft) or pygame.sprite.collide_mask(ball, paddleRight):
ball.bounce()
#display scores
font = pygame.font.SysFont("impact.ttf", 50)
text = font.render(str(scoreLeft), 1, Red)
screen.blit(text, (420,10))
text = font.render(str(scoreRight), 1, Blue)
screen.blit(text, (460,10))
#--update screen with drawings
pygame.display.flip()
#--60 fps limit
clock.tick(60)
#stop program once main loop is exited
pygame.quit()
After some debugging I realized that the y values was constantly hopping from some value up then down, the velocity seemed to work but it was constantly changing signs
The problem is on line 103 the:
if ball.rect.y >= 0:
you must of missed typed it as the y value will always be greater then 0, I fixed it by switching it to
if ball.rect.y <= 0:
and it worked.

How to keep a constant rectangle around a sprite in pygame? [duplicate]

This question already has answers here:
Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
(1 answer)
How do I detect collision in pygame?
(5 answers)
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
Closed 1 year ago.
For a computer science project, I'm making a game in pygame.
I'm working on collision right now with the Mario sprite and that block. However pygame collision works based on two rectangles colliding from what I know. You can easily put a rectangle around the block, but the Mario sprite is a different matter entirely. It becomes even more complicated when you realize you switch images for going left or right or jumping or whatnot.
I've tried looking this up on Google, where none of the answers worked. My CSP teacher also said he's not familiar with pygame and sadly can't help.
import pygame
pygame.init()
#set the background
screen = pygame.display.set_mode((1600, 800))
clock = pygame.time.Clock()
FPS = 45
vel = 3.5
BLACK = (0,0,0)
back = pygame.image.load('background.png')
background = pygame.transform.scale(back, (800,600))
playerimg = pygame.image.load('mariosprite.png').convert_alpha()
playerimgflipped = pygame.image.load('normalmarioflipped.png')
playerimgjump = pygame.image.load('finishedjumpingmario.png')
blockonelol = pygame.image.load('oneblock.png')
blockoneX = 100
blockoneY = 415
blockone = pygame.transform.scale(blockonelol,(40,40))
mario = pygame.transform.scale(playerimg, (35,50))
playerX = 10
playerY = 490
isJump = False
jumpCount = 11
run = True
while run:
clock.tick(FPS)
screen.fill(BLACK)
screen.blit(background, (0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and vel < playerX:
playerX = playerX - vel
mario = pygame.transform.scale(playerimgflipped, (35,50))
if keys[pygame.K_RIGHT]:
playerX+=vel
mario = pygame.transform.scale(playerimg, (35,50))
if not(isJump):
if keys[pygame.K_UP]:
isJump = True
if(playerY == 490):
mario = pygame.transform.scale(playerimg, (35, 50))
else:
mario = pygame.transform.scale(playerimgjump, (45,55))
if jumpCount >= -11:
goingdown = 1
if jumpCount < 0:
goingdown = -1
playerY -= (jumpCount ** 2) * 0.25 * goingdown
jumpCount = jumpCount - 1
if(playerY == 490):
mario = pygame.transform.scale(playerimg, (35, 50))
else:
isJump = False
jumpCount = 11
if(playerY == 490):
mario = pygame.transform.scale(playerimg, (35, 50))
screen.blit(blockone, (blockoneX, blockoneY))
screen.blit(mario, (playerX, playerY))
pygame.display.update()
However pygame collision works based on two rectangles colliding
Yes, but you don't need to add rectangles around the sprites.
Use pygame.Rect objects and colliderect() to detect the collision between the bounding rectangles of 2 objects or 2 images:
rect1 = pygame.Rect(x1, y1, w1, h1)
rect2 = pygame.Rect(x2, y2, w2, h2)
if rect1.colliderect(rect2):
# [...]
If you have to images (pygame.Surface objects), the bounding rectangle of can be get by get_rect(), where the location of the Surface has to be set by an keyword argument, since the returned rectangle always starts at (0, 0):
mario_rect = mario.get_rect(topleft = (playerX, playerY))
blockone_rect = blockone.get_rect(topleft = (blockoneX, blockoneY))
if mario_rect.colliderect(blockone_rect):
print("hit")
See also:
How do I detect collision in pygame?
How to detect collisions between two rectangular objects or images in pygame
Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?

How do I do collision-detection in pygame?

I have been writing this code recently and I wanted collision-detection but I have never done it before and I need help. this code is written with python and pygame so it should be simple but I'm not sure whether I should have the whole world as a transparent image
import pygame, os, itertools
from pygame.locals import *
w = 640
h = 480
pink = (0,179,179)
player_x = 39
player_y = 320
def setup_background():
screen.fill((pink))
screen.blit(playerImg, (player_x,player_y))
screen.blit(brick_tile, (0,0))
pygame.display.flip()
pygame.init()
screen = pygame.display.set_mode((w, h))
clock = pygame.time.Clock()
playerImg = pygame.image.load('img/player.png').convert_alpha()
brick_tile = pygame.image.load('img/map.png').convert_alpha()
class Player(pygame.sprite.Sprite):
allsprites = pygame.sprite.Group()
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('img/player.png')
self.rect = self.image.get_rect()
class World(pygame.sprite.Sprite):
allsprites = pygame.sprite.Group()
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('img/map.png')
self.rect = self.image.get_rect()
player = Player()
world = World()
done = False
while not done:
setup_background()
block_hit_list = pygame.sprite.spritecollide(player, world.allsprites, True)
for world in block_hit_list:
print("WORKING")
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == K_RIGHT:
player_x += 5
Your sprite collision detection is almost there but incorrect. You seem to know the basics of this concept in this line:
block_hit_list = pygame.sprite.spritecollide(player, world.allsprites, True)
You are almost there, but here is how you do it. In a pygame.sprite.spritecollide() function, you need the sprite's name, the group's name of the other sprite, and either True or False. In your case, you just need to make a group for your world (assuming you want the things in that order) :
world_group = pygame.sprite.Group(world)
This line is necessary for your detection code, and will enable that sprite to be removed. If you want to add another world to that group, so you have plenty of worlds, do this and add some code around it if you want to:
world_group.add(world)
The add() function will add a sprite to that group. I recommend you do a loop to make lots of them if necessary. Now to the collision code!
Now we are ready to do this function correctly. I would delete this line first:
block_hit_list = pygame.sprite.spritecollide(player, world.allsprites, True)
That would be unnecessary and useless for now. You will need to add this if statement in the while loop but outside your for loop:
if pygame.sprite.spritecollide(player, world_group, x):
#Do something
Why did I put x instead of True or False? Because that will be your decision. If you want to delete the sprite in the group after contact, replace x with True. If you don't want to do that, replace x with False. These are the basics of collision detection code, and I hope this helps you!

Categories