Pygame: Collision of two images - python

I'm working on my school project for which im designing a 2D game.
I have 3 images, one is the player and the other 2 are instances (coffee and computer). What i want to do is, when the player image collides with one of the 2 instances i want the program to print something.
I'm unsure if image collision is possible. But i know rect collision is possible. However, after several failed attempts, i can't manage to make my images rects. Somebody please help me. Here is my source code:
import pygame
import os
black=(0,0,0)
white=(255,255,255)
blue=(0,0,255)
class Player(object):
def __init__(self):
self.image = pygame.image.load("player1.png")
self.image2 = pygame.transform.flip(self.image, True, False)
self.coffee=pygame.image.load("coffee.png")
self.computer=pygame.image.load("computer.png")
self.flipped = False
self.x = 0
self.y = 0
def handle_keys(self):
""" Movement keys """
key = pygame.key.get_pressed()
dist = 5
if key[pygame.K_DOWN]:
self.y += dist
elif key[pygame.K_UP]:
self.y -= dist
if key[pygame.K_RIGHT]:
self.x += dist
self.flipped = False
elif key[pygame.K_LEFT]:
self.x -= dist
self.flipped = True
def draw(self, surface):
if self.flipped:
image = self.image2
else:
im = self.image
for x in range(0, 810, 10):
pygame.draw.rect(screen, black, [x, 0, 10, 10])
pygame.draw.rect(screen, black, [x, 610, 10, 10])
for x in range(0, 610, 10):
pygame.draw.rect(screen, black, [0, x, 10, 10])
pygame.draw.rect(screen, black, [810, x, 10, 10])
surface.blit(self.coffee, (725,500))
surface.blit(self.computer,(15,500))
surface.blit(im, (self.x, self.y))
pygame.init()
screen = pygame.display.set_mode((800, 600))#creates the screen
player = Player()
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit() # quit the screen
running = False
player.handle_keys() # movement keys
screen.fill((255,255,255)) # fill the screen with white
player.draw(screen) # draw the player to the screen
pygame.display.update() # update the screen
clock.tick(60) # Limits Frames Per Second to 60 or less

Use pygame.Rect() to keep image size and position.
Image (or rather pygame.Surface()) has function get_rect() which returns pygame.Rect() with image size (and position).
self.rect = self.image.get_rect()
Now you can set start position ie. (0, 0)
self.rect.x = 0
self.rect.y = 0
# or
self.rect.topleft = (0, 0)
# or
self.rect = self.image.get_rect(x=0, y=0)
(Rect use left top corner as (x,y)).
Use it to change position
self.rect.x += dist
and to draw image
surface.blit(self.image, self.rect)
and then you can test collision
if self.rect.colliderect(self.rect_coffe):
BTW: and now class Player looks almost like pygame.sprite.Sprite :)

Related

How do I make an image which rotates without distorting in pygame [duplicate]

This question already has answers here:
How do I rotate an image around its center using Pygame?
(6 answers)
How to rotate an image(player) to the mouse direction?
(2 answers)
How to turn the sprite in pygame while moving with the keys
(1 answer)
Closed 1 year ago.
I am creating a racecar game and I have decided to rewrite it because I realised I was initially writing it in a very inefficient way. I initially used image an just changed their location around the screen but I am now trying to learn how to create an object and I am running into a problem which I had initially but I don't know how to fix within a class.
What happens is when I turn the image it distorts a lot and ends up slowing down because I think pygame can't handle it maybe. In my old code, I created a copy of the original image and rotated the copy of the image which fixed the distortion problem. I now don't know how to do the same within a class.
main.py
import pygame, random
#Let's import the Car Class
from Car import Car
pygame.init()
SCREENWIDTH=800
SCREENHEIGHT=600
size = (SCREENWIDTH, SCREENHEIGHT)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Car Racing")
#This will be a list that will contain all the sprites we intend to use in our game.
all_sprites_list = pygame.sprite.Group()
playerCar = Car(60, 80, 70)
playerCar.rect.x = 160
playerCar.rect.y = 100
# Add the car to the list of objects
all_sprites_list.add(playerCar)
#Allowing the user to close the window...
carryOn = True
clock=pygame.time.Clock()
while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn=False
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_x:
playerCar.moveRight(10)
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
#playerCar.moveLeft(5)
playerCar.rot_center(2)
if keys[pygame.K_RIGHT]:
#playerCar.moveRight(5)
playerCar.rot_center(-2)
if keys[pygame.K_UP]:
playerCar.moveUp(5)
if keys[pygame.K_DOWN]:
playerCar.moveDown(5)
all_sprites_list.update()
#Drawing on Screen
screen.fill("white")
#Now let's draw all the sprites in one go. (For now we only have 1 sprite!)
all_sprites_list.draw(screen)
#Refresh Screen
pygame.display.flip()
#Number of frames per secong e.g. 60
clock.tick(25)
pygame.quit()
Car.py
import pygame
WHITE = (255, 255, 255)
class Car(pygame.sprite.Sprite):
#This class represents a car. It derives from the "Sprite" class in Pygame.
def __init__(self, width, height, speed):
# Call the parent class (Sprite) constructor
super().__init__()
# Instead we could load a proper picture of a car...
self.image = pygame.image.load("car.png").convert_alpha()
self.image = pygame.transform.rotate(self.image, 90)
#Initialise attributes of the car.
self.width=width
self.height=height
rect_surf = pygame.Surface((width, height), pygame.SRCALPHA)
rect_surf.fill((0, 0, 0, 0))
self.rect_surf = rect_surf
# Draw the car (a rectangle!)
pygame.draw.rect(self.image, (0, 0, 0, 0), [0, 0, self.width, self.height])
# Fetch the rectangle object that has the dimensions of the image.
self.rect = self.image.get_rect()
def moveRight(self, pixels):
self.rect.x += pixels
def moveLeft(self, pixels):
self.rect.x -= pixels
def moveUp(self, pixels):
self.rect.y -= pixels
def moveDown(self, pixels):
self.rect.y += pixels
def changeSpeed(self, speed):
self.speed = speed
def rot_center(self, angle):
rotated_image = pygame.transform.rotate(self.image, angle)
new_rect = rotated_image.get_rect(center = self.image.get_rect(center = (self.rect.x + (self.rect.width / 2), self.rect.y + (self.rect.height / 2))).center)
self.image = rotated_image
self.rect = new_rect
Any help on how to slvoe this would be much appreciated. I had questions about how to initially turn the image on another question, How do I make an object in pygame rotate.
I also don't know how to apply the rect correctly so that it is the size of the image and doesn't create a transparent spot on the image.
Any help would be much appreciated thanks. :):)
You should keep original image in separated variable with angle
self.original_image = pygame.image.load("car.png").convert_alpha()
self.angle = 90
and use it to generate rotated image at start
self.image = pygame.transform.rotate(self.original_image, self.angle)
and later you should increase self.angle and again create rotated image using original image
def rot_center(self, angle):
self.angle += angle
rotated_image = pygame.transform.rotate(self.original_image, self.angle)
new_rect = rotated_image.get_rect(center=self.rect.center)
self.image = rotated_image
self.rect = new_rect
or even shorter
def rot_center(self, angle):
self.angle += angle
self.image = pygame.transform.rotate(self.original_image, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
Full working code
I used red rectangle instead image so everyone can run it and test it.
I used pygame.math.Vector2(pixels, 0).rotate(self.angle) to move up/down using current angle - car direction - instead of moving to top/bottom of the screen.
import pygame
import random
# --- constants ---
WHITE = (255, 255, 255)
RED = (255, 0, 0)
SCREENWIDTH = 800
SCREENHEIGHT = 600
size = (SCREENWIDTH, SCREENHEIGHT)
# --- classes ---
class Car(pygame.sprite.Sprite):
#This class represents a car. It derives from the "Sprite" class in Pygame.
def __init__(self, width, height, speed):
# Call the parent class (Sprite) constructor
super().__init__()
#Initialise attributes of the car.
self.width = width
self.height = height
# Instead we could load a proper picture of a car...
#self.original_image = pygame.image.load("car.png").convert_alpha()
self.original_image = pygame.surface.Surface((width, height)).convert_alpha()
self.original_image.fill(RED)
self.angle = 90
self.image = pygame.transform.rotate(self.original_image, self.angle)
# Fetch the rectangle object that has the dimensions of the image.
self.rect = self.image.get_rect()
def moveRight(self, pixels):
self.rect.x += pixels
def moveLeft(self, pixels):
self.rect.x -= pixels
def moveUp(self, pixels):
#self.rect.y += pixels
x, y = pygame.math.Vector2(pixels, 0).rotate(self.angle)
self.rect.x += x
self.rect.y -= y
def moveDown(self, pixels):
#self.rect.y += pixels
x, y = pygame.math.Vector2(pixels, 0).rotate(self.angle)
self.rect.x -= x
self.rect.y += y
def changeSpeed(self, speed):
self.speed = speed
def rot_center(self, angle):
self.angle += angle
self.image = pygame.transform.rotate(self.original_image, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
# --- main ---
pygame.init()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Car Racing")
#This will be a list that will contain all the sprites we intend to use in our game.
all_sprites_list = pygame.sprite.Group()
playerCar = Car(60, 80, 70)
playerCar.rect.x = 160
playerCar.rect.y = 100
# Add the car to the list of objects
all_sprites_list.add(playerCar)
#Allowing the user to close the window...
carryOn = True
clock = pygame.time.Clock()
while carryOn:
for event in pygame.event.get():
if event.type == pygame.QUIT:
carryOn=False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_x:
playerCar.moveRight(10)
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
#playerCar.moveLeft(5)
playerCar.rot_center(2)
if keys[pygame.K_RIGHT]:
#playerCar.moveRight(5)
playerCar.rot_center(-2)
if keys[pygame.K_UP]:
playerCar.moveUp(5)
if keys[pygame.K_DOWN]:
playerCar.moveDown(5)
all_sprites_list.update()
#Drawing on Screen
screen.fill("white")
#Now let's draw all the sprites in one go. (For now we only have 1 sprite!)
all_sprites_list.draw(screen)
#Refresh Screen
pygame.display.flip()
#Number of frames per secong e.g. 60
clock.tick(25)
pygame.quit()

How to move a surface in pygame

I'm making a game in pygame and I'm trying to move the self.jetRect, which is the rectangular area of the Surface (jetSurface), but when I try updating the coordinates of the rectangle, I get a TypeError: 'tuple' object is not callable. I think this is related to the fact that a tuple is immutable, but I don't know how to fix this.
import pygame
pygame.init()
clock = pygame.time.Clock()
# Screen setup
screen_width = 600
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Jet Fighter Game")
# Colors
black = (0, 0, 0)
white = (255, 255, 255)
grey = (100, 100, 100)
# Classes
class PLAYER1:
def __init__(self):
self.jetSurface = pygame.image.load("player1 - blackJet.png")
self.x = 100
self.y = 100
self.speed = 5
self.jetRect = self.jetSurface.get_rect(center = (self.x, self.y))
self.angle = 0
self.rotatedJet = self.jetSurface
def rotateJet(self, surface, angle):
key = pygame.key.get_pressed()
if key[pygame.K_a]:
self.angle += 10
self.rotatedJet = pygame.transform.rotozoom(surface, angle, 1)
self.jetRect = self.rotatedJet.get_rect(center=(self.x, self.y))
if key[pygame.K_d]:
self.angle -= 10
self.rotatedJet = pygame.transform.rotozoom(surface, angle, 1)
self.jetRect = self.rotatedJet.get_rect(center=(self.x, self.y))
if self.angle >= 360:
self.angle = 0
screen.blit(self.rotatedJet, self.jetRect)
def moveJet(self):
key = pygame.key.get_pressed()
if key[pygame.K_w]:
self.y -= self.speed
self.jetRect.center(self.x, self.y)
class PLAYER2:
pass
class BULLET:
pass
class MAIN:
def __init__(self):
self.player1 = PLAYER1()
def rotateJets(self):
self.player1.rotateJet(self.player1.jetSurface, self.player1.angle)
def moveJets(self):
self.player1.moveJet()
main = MAIN()
# Main loop
run = True
while run:
# Checking for events
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# Drawing on screen
screen.fill(grey)
# Movement and Collisions
main.rotateJets()
main.moveJets()
pygame.display.flip()
clock.tick(60)
pygame.quit()
Its a typo. self.jetRect.center is a tuple of (self.x, self.y), so when you do
self.jetRect.center(self.x, self.y), you are doing (self.x, self.y)(self.x, self.y), hence the error message of calling a tuple.
I think you meant to assign the tuple like self.jetRect.center = (self.x, self.y)

spritecollide() not functioning as expected

I am in the process of making a 'main character' which jumps around and can jump onto boxes etc using pygame. I have created the Player class, the Level class and the Box class. The spritecollide function never returns true, and I am unsure where I am going wrong.
I used spritecollideany and groupcollide to see if that helps (it didn't)
I have checked as best I can, and as far as I can tell the boxes are making their way into Level.box_list as a sprite Group, and both the boxes and the players are Sprites.
I have thoroughly read the documentation and feel as though there must be some key concept I am missing or misusing.
I greatly appreciate any help that you are able to offer. Thanks!
movementprototype.py
import random, pygame, sys
import math
from pygame.locals import *
from levelprototype import *
FPS = 60 # frames per second, the general speed of the program
WINDOWWIDTH = 640 # size of window's width in pixels
WINDOWHEIGHT = 480 # size of windows' height in pixels
# R G B
GRAY = (100, 100, 100)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
YELLOW = (255, 255, 0)
ORANGE = (255, 128, 0)
BLACK = ( 0, 0, 0)
BGCOLOR = WHITE
def main():
#set up all relevant variables
global FPSCLOCK, DISPLAYSURF, player, box_list
pygame.init()
FPSCLOCK = pygame.time.Clock()
#the length, in ms, of one frame
tick = 1000/FPS
#create window
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
mousex = 0 # used to store x coordinate of mouse event
mousey = 0 # used to store y coordinate of mouse event
pygame.display.set_caption('Movement test environment')
#create the player object using Player() class, add to all_sprites group.
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
#create the level object
level = Level()
while True: #main game loop
#fill with background colour
DISPLAYSURF.fill(BGCOLOR)
# Update items in the level
level.update()
level.draw(DISPLAYSURF)
#call the sprite update and draw methods.
all_sprites.update()
#check if the player has clicked the X or pressed escape.
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
[irrelevant code]
#update the screen
pygame.display.update()
FPSCLOCK.tick(FPS)
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
#.draw() called in main() requires that each Sprite have a Surface.image
#and a Surface.rect.
self.image = pygame.Surface((20,50))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
#attributes
self.velX = 0
self.velY = 0
self.x = 0
self.y = 0
self.gravity = 0.6
self.maxVelocity = 5
self.xResistance = 0.5
self.isJump = 0
self.isDash = 0
def update(self):
#check which keys are pressed and add velocity.
keys = pygame.key.get_pressed()
if (keys[K_d]) and self.velX < self.maxVelocity:
self.velX += 2.5
if (keys[K_a]) and self.velX > -self.maxVelocity:
self.velX -= 2.5
if (keys[K_w]) and self.isJump == 0:
self.velY -= 10
self.isJump = 1
if (keys[K_SPACE]) and self.isDash == 0:
#at the moment, can only dash once. add timer reset.
self.isDash = 1
if self.velX > 0:
self.x += 20
else:
self.x -= 20
#add gravity
self.velY += self.gravity
#add X resistance
if self.velX > 0.05:
self.velX -= self.xResistance
elif self.velX < -0.05:
self.velX += self.xResistance
#if velocity is really close to 0, make it 0, to stop xResistance moving it the other way.
if self.velX < 0.15 and self.velX > -0.15:
self.velX = 0
self.checkCollision()
#update position with calculated velocity
self.x += self.velX
self.y += self.velY
#call the draw function, below, to blit the sprite onto screen.
self.draw(DISPLAYSURF)
def checkCollision(self):
level = Level().box_list
for collide in pygame.sprite.spritecollide(self, level, False):
print("Collision")
#no matter what I put here, a collision never seems to be identified!!
def draw(self, DISPLAYSURF):
DISPLAYSURF.blit(self.image, (self.x, self.y))
if __name__ == '__main__':
main()
levelprototype.py
import pygame, sys
from pygame.locals import*
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
BLACK = ( 0, 0, 0)
class Box(pygame.sprite.Sprite):
""" Box the user can jump on """
def __init__(self, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(BLACK)
self.rect = self.image.get_rect()
class Level():
#create the box_list as a Class attribute that exists for all instances.
box_list = pygame.sprite.Group()
def __init__(self):
# Background image
self.background = None
# Array with width, height, x, and y of platform
level = [[210, 70, 500, 100],
[210, 70, 200, 400],
[210, 70, 600, 300],
]
# Go through the array above and add platforms
if len(Level.box_list) < 3:
for platform in level:
block = Box(platform[0], platform[1])
block.rect.x = platform[2]
block.rect.y = platform[3]
Level.box_list.add(block)
# Update everything on this level
def update(self):
""" Update everything in this level."""
# Level.box_list.update()
def draw(self, DISPLAYSURF):
""" Draw everything on this level. """
# Draw all the sprite lists that we have
Level.box_list.draw(DISPLAYSURF)
When checking for collisions, pygame.sprite.spritecollide will check whether the Sprite's rects are intersecting. You're not updating the player's rect attribute, so its position will be at (0, 0). Make sure they are synchronized:
# update position with calculated velocity
self.x += self.velX
self.y += self.velY
self.rect.x = self.x
self.rect.y = self.y

Could not draw the pillars of flappy bird using sprite in pygame

I am making a flappy bird clone game, using pygame. I want to draw pillars by using Sprite.draw. I made a Pillar class and initialized it with two rectangles p_upper and p_lower on the left side of the screen, coming towards the right side with the help of the update function of the sprite. But the screen is only showing the p_lower pillar. Can anyone help?
class Pillar(pygame.sprite.Sprite):
# the "h" parameter is height of upper pillar upto gap
# "w" is the width of the pillar
# pillar is coming from left to right
def __init__(self, w, h, gap):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((w, h))
self.image.fill(green)
self.p_upper = self.rect = self.image.get_rect()
self.p_upper.topleft = (-w, 0)
self.image = pygame.Surface((w, HEIGHT - (h + gap)))
self.image.fill(green)
self.p_lower = self.rect = self.image.get_rect()
self.p_lower.topleft = (-w, h + gap)
def update(self):
self.p_upper.x += 1
self.p_lower.x += 1
Because of the following two lines:
self.p_upper = self.rect = self.image.get_rect()
and...
self.p_lower = self.rect = self.image.get_rect()
These are both grabbing the same reference to the self.rect. The first line runs and assigns the rect reference to p_upper. Then the same reference is assigned to p_lower. Because it's the same reference, when you update the location of the lower rectangle, you're actually updating both.
Using a sprite that consists of two rects and images isn't a good solution for this problem. I suggest to create two separate sprites with their own image and rect. To create two sprite instances at the same time and to add them to a sprite group, you can write a short function as you can see in this example:
import pygame as pg
from pygame.math import Vector2
green = pg.Color('green')
HEIGHT = 480
class Pillar(pg.sprite.Sprite):
def __init__(self, x, y, w, h):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((w, h))
self.image.fill(green)
self.rect = self.image.get_rect(topleft=(x, y))
def update(self):
self.rect.x += 1
def create_pillars(w, h, gap, sprite_group):
sprite_group.add(Pillar(0, 0, w, h-gap))
sprite_group.add(Pillar(0, HEIGHT-(h+gap), w, h+gap))
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
create_pillars(50, 170, 0, all_sprites)
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_a:
create_pillars(50, 170, 15, all_sprites)
elif event.key == pg.K_s:
create_pillars(50, 170, 30, all_sprites)
elif event.key == pg.K_d:
create_pillars(50, 100, -60, all_sprites)
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()

Pygame: Adding a background

I'm making a project for my senior year. Its a game where i can move a user. I would like to add a background that goes behind all the pictures i've blitted. I've searched everywhere but i can't seem to find the solution. Could anybody help?
import pygame
import os
class Player(object):
def __init__(self):
self.image = pygame.image.load("player1.png")
self.image2 = pygame.transform.flip(self.image, True, False)
self.coffee=pygame.image.load("coffee.png")
self.computer=pygame.image.load("computer.png")
self.flipped = False
self.x = 0
self.y = 0
def handle_keys(self):
""" Movement keys """
key = pygame.key.get_pressed()
dist = 5
if key[pygame.K_DOWN]:
self.y += dist
elif key[pygame.K_UP]:
self.y -= dist
if key[pygame.K_RIGHT]:
self.x += dist
self.flipped = False
elif key[pygame.K_LEFT]:
self.x -= dist
self.flipped = True
def draw(self, surface):
if self.flipped:
image = self.image2
else:
image = self.image
surface.blit(image, (self.x, self.y))
surface.blit(self.coffee, (700,500))
surface.blit(self.computer,(0,500))
pygame.init()
screen = pygame.display.set_mode((810, 610)) #creates the screen
player = Player()
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit() # quit the screen
running = False
player.handle_keys() # movement keys
screen.fill((255,255,255)) # fill the screen with white
player.draw(screen) # draw the player to the screen
pygame.display.update() # update the screen
clock.tick(60) # Limits Frames Per Second to 60 or less
A background image is no different from any other image. Just .blit it first.

Categories

Resources