I am attempting to create a racing game in pygame. I want it such that when the car goes off the track, it slows down. I have tried to do this by having another sprite that is an outline of the track and when the car touches that sprite, it slows down. This does not work and I don't know why. Is there a better way to do this?
Img is the car image
Back is the racetrack
BackHit is the outline
I receive this error code:
Traceback (most recent call last):
File "C:\Users\Daniella\Desktop\Python\Games\game.py", line 75, in
if pygame.sprite.collide_mask(Img, BackHit):
File "C:\Users\Daniella\AppData\Roaming\Python\Python36\site-packages\pygame\sprite.py", line 1470, in collide_mask
xoffset = right.rect[0] - left.rect[0]
AttributeError: 'pygame.Surface' object has no attribute 'rect'
This is the code:
import pygame
Width = 800
Height = 600
Black = (0, 0, 0)
White = (255, 255, 255)
Red = (255, 0, 0)
Green = (0, 255, 0)
Blue = (0, 0, 255)
Yellow = (255, 255, 0)
BackColour = (198, 151, 107)
pygame.init()
GameDisplay = pygame.display.set_mode((Width, Height))
pygame.display.set_caption("A bit Racey")
Clock = pygame.time.Clock()
Img = pygame.image.load("download.png")
ImgWidth = 46
ImgHeight = 68
Img = pygame.transform.scale(Img, (ImgWidth, ImgHeight))
Back = pygame.image.load("back1.png")
BackWidth = Width*4
BackHeight = Height*4
Back = pygame.transform.scale(Back, (BackWidth, BackHeight))
BackHit = pygame.image.load("back1 hit1.png")
BackHitWidth = Width*4
BackHitHeight = Height*4
BackHit = pygame.transform.scale(BackHit, (BackHitWidth, BackHitHeight))
def Car():
GameDisplay.blit(Img, (400-ImgWidth/2, 300-ImgHeight/2))
def Background(X, Y):
GameDisplay.blit(Back, (X, Y))
def BackgroundHit(X, Y):
GameDisplay.blit(BackHit, (X, Y))
X = (Width*0.45)
Y = (Height*0.5)
XChange = 0
YChange = 0
Changer = 1
Crashed = False
while not Crashed:
for Event in pygame.event.get():
if Event.type == pygame.QUIT:
Crashed = True
elif Event.type == pygame.KEYDOWN:
if Event.key == pygame.K_LEFT:
Img = pygame.transform.rotate(Img, -90)
XChange = 5 / Changer
elif Event.key == pygame.K_RIGHT:
Img = pygame.transform.rotate(Img, 90)
XChange = -5 / Changer
elif Event.key == pygame.K_UP:
Img = pygame.transform.rotate(Img, 0)
YChange = 5 / Changer
elif Event.key == pygame.K_DOWN:
Img = pygame.transform.rotate(Img, 180)
YChange = -5 / Changer
if Event.type == pygame.KEYUP:
if Event.key == pygame.K_LEFT or Event.key == pygame.K_RIGHT:
XChange = 0
elif Event.key == pygame.K_UP or Event.key == pygame.K_DOWN:
YChange = 0
if pygame.sprite.collide_mask(Img, BackHit):
Changer = 2
Y += YChange
X += XChange
GameDisplay.fill(White)
BackgroundHit(X, Y)
Background(X, Y)
Car()
pygame.display.update()
Clock.tick(200)
pygame.quit()
quit()
Here's a little example that shows you how you can use pygame.mask.from_surface and pygame.Mask.overlap for pixel perfect collision detection.
import pygame as pg
# Transparent surfaces with a circle and a triangle.
circle_surface = pg.Surface((60, 60), pg.SRCALPHA)
pg.draw.circle(circle_surface, (30, 90, 200), (30, 30), 30)
triangle_surface = pg.Surface((60, 60), pg.SRCALPHA)
pg.draw.polygon(triangle_surface, (160, 250, 0), ((30, 0), (60, 60), (0, 60)))
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
# Use `pygame.mask.from_surface` to get the masks.
circle_mask = pg.mask.from_surface(circle_surface)
triangle_mask = pg.mask.from_surface(triangle_surface)
# Also create rects for the two images/surfaces.
circle_rect = circle_surface.get_rect(center=(320, 240))
triangle_rect = triangle_surface.get_rect(center=(0, 0))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEMOTION:
triangle_rect.center = event.pos
# Now calculate the offset between the rects.
offset_x = triangle_rect.x - circle_rect.x
offset_y = triangle_rect.y - circle_rect.y
# And pass the offset to the `overlap` method of the mask.
overlap = circle_mask.overlap(triangle_mask, (offset_x, offset_y))
if overlap:
print('The two masks overlap!', overlap)
screen.fill((30, 30, 30))
screen.blit(circle_surface, circle_rect)
screen.blit(triangle_surface, triangle_rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
To create a mask for the background or track you need to create an extra image and either leave the track transparent or the area where the car should slow down and then check if the car collides with the track or with the outside area. Here I check if the green triangle collides with the "track" (the blue lines), and in your game you would then slow the car down if it doesn't collide with the track.
import pygame as pg
bg_surface = pg.Surface((640, 480), pg.SRCALPHA)
pg.draw.lines(
bg_surface, (30, 90, 200), True,
((60, 130), (300, 50), (600, 200), (400, 400), (150, 300)),
12)
triangle_surface = pg.Surface((60, 60), pg.SRCALPHA)
pg.draw.polygon(triangle_surface, (160, 250, 0), ((30, 0), (60, 60), (0, 60)))
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
bg_mask = pg.mask.from_surface(bg_surface)
triangle_mask = pg.mask.from_surface(triangle_surface)
bg_rect = bg_surface.get_rect(center=(320, 240))
triangle_rect = triangle_surface.get_rect(center=(0, 0))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEMOTION:
triangle_rect.center = event.pos
offset_x = triangle_rect.x - bg_rect.x
offset_y = triangle_rect.y - bg_rect.y
overlap = bg_mask.overlap(triangle_mask, (offset_x, offset_y))
if overlap:
print('The two masks overlap!', overlap)
screen.fill((30, 30, 30))
screen.blit(bg_surface, bg_rect)
screen.blit(triangle_surface, triangle_rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
pygame.sprite.collide_mask is meant for pygame.sprite.Sprite objects. Instead use mask_var.overlap(other_mask, offset). To calculate the offset between the two masks, use offset = [other_object_x - object_x, other_object_y - object_y]. And to get the mask of an image, use mask_var = pygame.mask.from_surface(image).
Related
I am currently just testing the waters with Pygame display as I am extremely new to the module.
Here is the code:
import pygame
pygame.init()
SCALE = 1
display_width = int(1200 * SCALE)
display_height = int(800 * SCALE)
x = (display_width * 0.45)
y = (display_height * 0.8)
transparent = (0, 0, 0, 0)
black = (0, 0, 0)
white = (255, 255, 255)
green = (44, 215, 223)
red = (255, 67, 34)
blue = (77, 77, 77)
count = 0
running = True
box1_image = True
box3_image = True
background = pygame.image.load('C:/Users/Justi/source/repos/03 Game/Game/assets/background.jpg')
dirty_spot = pygame.image.load('C:/Users/Justi/source/repos/03 Game/Game/assets/dirty.png')
background_resized = pygame.transform.scale(dirty_spot, (display_width, display_height))
background_rect = background_resized.get_rect()
gameDisplay = pygame.display.set_mode(background_rect.size)
pygame.display.set_caption("Game 1")
clock = pygame.time.Clock()
dirty_spot_resized = pygame.transform.scale(dirty_spot, (200, 200))
square_dirty_spot = dirty_spot_resized.convert()
rect_dirty_spot = square_dirty_spot.get_rect()
def dirtyspot1(imagex, imagey):
gameDisplay.blit(dirty_spot_resized, (imagex,imagey))
while running:
rects = []
if rects != []:
pygame.display.update(rects)
for event in pygame.event.get():
if event.type == pygame.quit:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
running = False
#if box3_image: #Replacing the Bool passing variable with a simple loop count to trigger the else condition
if count < 50:
print("Test 1")
dirtyspot1(0,0)
pygame.display.update()
else:
print("Test 2")
dirty_rect = background.subsurface(rect_dirty_spot)
gameDisplay.blit(dirty_rect, (0,0))
rects.append(pygame.Rect(0,0, 200, 200))
gameDisplay.fill(white)
clock.tick(30)
count += 1
pygame.quit()
quit()
box1_image & box1_image are bool variables to flag certain condition which is passed from another function.
But I have gotten the passing of said variables working by doing a simple test with print("Test 2"). However, when it tries to blit dirty_rect. Nothing changes on the pygame display.
Assets (if needed):
background.jpg
dirty.png
May I check what is missing to properly "remove/delete" the dirty_spot blit? Thank you in advance.
import pygame
pygame.init()
SCALE = 1
display_width = int(1200 * SCALE)
display_height = int(800 * SCALE)
x = (display_width * 0.45)
y = (display_height * 0.8)
transparent = (0, 0, 0, 0)
black = (0, 0, 0)
white = (255, 255, 255)
green = (44, 215, 223)
red = (255, 67, 34)
blue = (77, 77, 77)
count = 0
running = True
box1_image = True
box3_image = True
background = pygame.image.load('background.jpg')
dirty_spot = pygame.image.load('dirty.png')
background_resized = pygame.transform.scale(background, (display_width, display_height))
background_rect = background_resized.get_rect()
gameDisplay = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("Game 1")
clock = pygame.time.Clock()
dirty_spot_resized = pygame.transform.scale(dirty_spot, (200, 200))
square_dirty_spot = dirty_spot_resized.convert()
rect_dirty_spot = square_dirty_spot.get_rect()
show_dirt = False
def dirtyspot1(imagex, imagey):
gameDisplay.blit(dirty_spot_resized, (imagex,imagey))
while running:
rects = []
if rects != []:
pygame.display.update(rects)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
running = False
if event.key == pygame.K_d:
show_dirt = not show_dirt
if event.key == pygame.K_UP:
rect_dirty_spot.y -= 5
if event.key == pygame.K_DOWN:
rect_dirty_spot.y += 5
if event.key == pygame.K_RIGHT:
rect_dirty_spot.x += 5
if event.key == pygame.K_LEFT:
rect_dirty_spot.x -= 5
gameDisplay.blit(background_resized, (0,0))
if show_dirt:
gameDisplay.blit(dirty_spot_resized, rect_dirty_spot)
pygame.display.update()
clock.tick(5)
count += 1
pygame.quit()
quit()
like this? (I changed the FPS to 5 so it takes a while to change)
import pygame
pygame.init()
SCALE = 1
display_width = int(1200 * SCALE)
display_height = int(800 * SCALE)
x = (display_width * 0.45)
y = (display_height * 0.8)
transparent = (0, 0, 0, 0)
black = (0, 0, 0)
white = (255, 255, 255)
green = (44, 215, 223)
red = (255, 67, 34)
blue = (77, 77, 77)
count = 0
running = True
box1_image = True
box3_image = True
background = pygame.image.load('background.jpg')
dirty_spot = pygame.image.load('dirty.png')
background_resized = pygame.transform.scale(background, (display_width, display_height))
background_rect = background_resized.get_rect()
gameDisplay = pygame.display.set_mode(background_rect.size)
pygame.display.set_caption("Game 1")
clock = pygame.time.Clock()
dirty_spot_resized = pygame.transform.scale(dirty_spot, (200, 200))
square_dirty_spot = dirty_spot_resized.convert()
rect_dirty_spot = square_dirty_spot.get_rect()
def dirtyspot1(imagex, imagey):
gameDisplay.blit(dirty_spot_resized, (imagex,imagey))
while running:
rects = []
if rects != []:
pygame.display.update(rects)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
running = False
gameDisplay.fill(white)
#if box3_image: #Replacing the Bool passing variable with a simple loop count to trigger the else condition
if count < 50:
print("Test 1")
dirtyspot1(0,0)
else:
print("Test 2")
dirty_rect = background.subsurface(rect_dirty_spot)
gameDisplay.blit(dirty_rect, (0,0))
rects.append(pygame.Rect(0,0, 200, 200))
pygame.display.update()
clock.tick(5)
count += 1
pygame.quit()
quit()
I think you have misunderstood things. You can blit one image or surface on top of another, and you build things up like that, then blit them to the display and finally update the display.
You have this line:
background_resized = pygame.transform.scale(dirty_spot, (display_width, display_height))
which I assume should be background not dirty_spot.
I moved the call to display.update() out of the if loop, because you call display.update() last.
# no errors were shown in the consle
import pygame
pygame.init()
# display size
width = 1800
height =1000
# colors
black = (0, 0, 0)
blue = (0, 0, 255)
white = (255, 255, 255)
green = (0, 255, 0)
red = (255, 0, 0)
aqua = (1, 255, 255)
# main display
gamedisplay = pygame.display.set_mode((width, height))
pygame.display.set_caption('Race Game') # the
clock = pygame.time.Clock()
# the car image
carImg = pygame.image.load('myCar.png')
# car
def car(x, y):
gamedisplay.blit(carImg, (x, y))
x = (width * 0.45)
y = (height * 0.8)
# events
crashed1 = False
x_change = 0
while not crashed1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed1 = True
# keybinds
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -5
elif event.key == pygame.K_RIGHT:
x_change = 5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or pygame.K_RIGHT:
x_change = 0
x += x_change
# update loop
print(event)
gamedisplay.fill(white)
car(x, y)
pygame.display.update()
clock.tick(120)
pygame.quit()
Try to put 4 spaces or one tab before these commands and get
import pygame
pygame.init()
# display size
width = 1800
height =1000
# colors
black = (0, 0, 0)
blue = (0, 0, 255)
white = (255, 255, 255)
green = (0, 255, 0)
red = (255, 0, 0)
aqua = (1, 255, 255)
# main display
gamedisplay = pygame.display.set_mode((width, height))
pygame.display.set_caption('Race Game') # the
clock = pygame.time.Clock()
# the car image
carImg = pygame.image.load('myCar.png')
# car
def car(x, y):
gamedisplay.blit(carImg, (x, y))
x = (width * 0.45)
y = (height * 0.8)
# events
crashed1 = False
x_change = 0
while not crashed1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed1 = True
# keybinds
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -5
elif event.key == pygame.K_RIGHT:
x_change = 5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or pygame.K_RIGHT:
x_change = 0
x += x_change
# update loop
print(event)
gamedisplay.fill(white)
car(x, y)
pygame.display.update()
clock.tick(120)
pygame.quit()
I'm having trouble keeping track of two rectangles that I blit onto a surface after I then rotate that surface.
There are three rectangles draw on to the surface, representing the player. As the left and right keys are pressed, the rectangles all rotate around the centre point correctly.
When the space bar is pressed, the "lights" are supposed to toggle on and off, however they are always redrawn at the bottom of the surface.
Can you advise what I'm doing wrong?
import pygame
from pygame.locals import *
class Player(pygame.sprite.Sprite):
height = 48
width = 48
colour = (30,144,255)
def __init__(self):
super(Player, self).__init__()
self.surf = pygame.Surface((self.width, self.height))
self.surf.fill((0, 0, 0))
self.rect = self.surf.get_rect(center = (screen_width / 2, screen_height / 2))
self.rotation = 0
self.PlayerBody = pygame.draw.rect(self.surf, self.colour, Rect(15, 15, 20, 35))
self.PlayerLightLeft = pygame.draw.rect(self.surf, (125, 0, 0), Rect(19, 45, 4, 4))
self.PlayerLightRight = pygame.draw.rect(self.surf, (125, 0, 0), Rect(27, 45, 4, 4))
self.lights = False
self.lightsOnColour = (255, 0, 0)
self.lightsOffColour = (125, 0, 0)
self.lightsColour = self.lightsOffColour
def update(self, pressedKey):
if pressedKey == pygame.K_RIGHT:
self.surf = pygame.transform.rotate(self.surf, -90)
if pressedKey == pygame.K_LEFT:
self.surf = pygame.transform.rotate(self.surf, 90)
if pressedKey == pygame.K_SPACE:
if self.lights:
self.lightsColour = self.lightsOffColour
self.lights = False
else:
self.lightsColour = self.lightsOnColour
self.lights = True
# always draws rectangles at the bottom of the surface
self.PlayerLightLeft = pygame.draw.rect(self.surf, self.lightsColour, self.PlayerLightLeft)
self.PlayerLightRight = pygame.draw.rect(self.surf, self.lightsColour, self.PlayerLightRight)
# initialize pygame
pygame.init()
screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
background = pygame.Surface(screen.get_size())
background.fill((255, 255, 255))
Player = Player()
running = True
while running:
pressedKey = pygame.K_0
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
pressedKey = event.key
elif event.type == QUIT:
running = False
screen.blit(background, (0, 0))
Player.update(pressedKey)
screen.blit(Player.surf, Player.rect)
pygame.display.flip()
# end of game, quit
pygame.quit()
Don't rotate the player surface when a key is presse, but add an attribute angle to the class Player, which stores the current angle of the surface.
Change the angle when the K_RIGHT or K_LEFT key is pressed.
class Player(pygame.sprite.Sprite):
height = 48
width = 48
colour = (30,144,255)
def __init__(self):
super(Player, self).__init__()
# [...]
self.angle = 0
def update(self, pressedKey):
if pressedKey == pygame.K_RIGHT:
self.angle = (self.angle - 90) % 360
if pressedKey == pygame.K_LEFT:
self.angle = (self.angle + 90) % 360
# [...]
this causes that the original surface is never changed and changing the "lights" will always work.
Create a rotated surface, which is rotated by Player.angle and blit the rotated surface:
rotSurf = pygame.transform.rotate(Player.surf, Player.angle)
screen.blit(rotSurf, Player.rect)
Brilliant #Rabbid76! Thank you!
Updated code below in case anyone has a similar problem.
import pygame
from pygame.locals import *
class Player(pygame.sprite.Sprite):
height = 48
width = 48
colour = (30,144,255)
def __init__(self):
super(Player, self).__init__()
self.surf = pygame.Surface((self.width, self.height))
self.surf.fill((0, 0, 0))
self.rect = self.surf.get_rect(center = (screen_width / 2, screen_height / 2))
self.angle = 0
self.PlayerBody = pygame.draw.rect(self.surf, self.colour, Rect(15, 15, 20, 35))
self.PlayerLightLeft = pygame.draw.rect(self.surf, (125, 0, 0), Rect(19, 45, 4, 4))
self.PlayerLightRight = pygame.draw.rect(self.surf, (125, 0, 0), Rect(27, 45, 4, 4))
self.lights = False
self.lightsOnColour = (255, 0, 0)
self.lightsOffColour = (125, 0, 0)
self.lightsColour = self.lightsOffColour
def update(self, pressedKey):
if pressedKey == pygame.K_RIGHT:
self.angle = (self.angle - 90) % 360
if pressedKey == pygame.K_LEFT:
self.angle = (self.angle + 90) % 360
if pressedKey == pygame.K_SPACE:
if self.lights:
self.lightsColour = self.lightsOffColour
self.lights = False
else:
self.lightsColour = self.lightsOnColour
self.lights = True
self.PlayerLightLeft = pygame.draw.rect(self.surf, self.lightsColour, self.PlayerLightLeft)
self.PlayerLightRight = pygame.draw.rect(self.surf, self.lightsColour, self.PlayerLightRight)
# initialize pygame
pygame.init()
screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
background = pygame.Surface(screen.get_size())
background.fill((255, 255, 255))
Player = Player()
running = True
while running:
pressedKey = pygame.K_0
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
pressedKey = event.key
elif event.type == QUIT:
running = False
screen.blit(background, (0, 0))
Player.update(pressedKey)
rotSurf = pygame.transform.rotate(Player.surf, Player.angle)
screen.blit(rotSurf, Player.rect)
pygame.display.flip()
# end of game, quit
pygame.quit()
I currently have a racecar game in development, using Pygame. I understand that in order to get the sprite to move the way a real car does, trigonometry is required. However, for now, I am simply trying to get the racecar image to rotate as the user holds a button. What is the simplest way to do so?
import pygame
pygame.init()
#DEFING COLOURS
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
BLUE = (0, 0, 255)
GREEN = ( 0, 255, 0)
RED = (255, 0, 0)
size = (800,600)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My First Game")
class Car(pygame.sprite.Sprite):
#BASE CAR CLASS
def __init__(self, filename):
#INITIALISE OBJECT PROPERTIES
super().__init__()
#LOAD IMAGE
self.image = pygame.image.load(filename).convert_alpha()
#SET BACKGROUND COLOUR
#self.image.set_colorkey(WHITE)
#SET RECTANGLE COLLISION BOX
self.rect = self.image.get_rect()
self.angle = 0
self.angle_change = 0
#Create sprites list
all_sprites_list = pygame.sprite.Group()
#Create F1car object
F1car = Car("car.png")
car_rotation = 0.0
surface = pygame.Surface((15, 15))
#Add F1car to sprites list
all_sprites_list.add(F1car)
#LOOP UNTIL USER EXITS THE GAME
carryOn = True
#CLOCK TO CONTROL FRAME RATE
clock = pygame.time.Clock()
##MAIN LOOP##
while carryOn:
#MAIN EVENT LOOP
for event in pygame.event.get(): #USER DID SOMETHING
if event.type == pygame.QUIT: #IF USER CLICKED CLOSE
carryOn = False #END THE LOOP
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
car_rotation += 0.1
pygame.transform.rotate(surface, car_rotation)
#GAME LOGIC
#DRAWING CODE
#CLEARING SCREEN
screen.fill(WHITE)
#DRAWING SHAPES
pygame.draw.rect(screen, RED, [55, 200, 100, 70], 0)
pygame.draw.rect(screen, BLUE, [78, 300, 60, 70], 0)
#LIST OF SPRITES TO COLLIDE WITH EACHOTHER
#blocks_hit_list = pygame.sprite.spritecollide(F1car, )
#DRAW SPRITES FROM all_sprites_list LIST
all_sprites_list.draw(screen)
#UPDATE THE SCREEN
pygame.display.flip()
#SET UPDATE RATE
clock.tick(60)
pygame.quit()
I'd give the Car class an update method and in this method rotate the car if self.angle_change is not 0. That allows you to call all_sprites.update() to call the update methods of all contained sprites.
Set the angle_change in the event loop to start the rotation. In the update method, increase the self.angle by the self.angle_change, then use pygame.transform.rotate or .rotozoom and pass the self.angle. Afterwards you need to get a new rect and pass the center of the old rect as the center argument.
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
WHITE = (255, 255, 255)
CAR_IMAGE = pygame.Surface((45, 90), pygame.SRCALPHA)
CAR_IMAGE.fill((150, 20, 0))
class Car(pygame.sprite.Sprite):
def __init__(self, pos, image):
super().__init__()
self.image = image
# Store a reference to the original to preserve the image quality.
self.orig_image = self.image
self.rect = self.image.get_rect(center=pos)
self.angle = 0
self.angle_change = 0
def update(self):
if self.angle_change != 0:
self.angle += self.angle_change
# I prefer rotozoom because it looks smoother.
self.image = pygame.transform.rotozoom(self.orig_image, self.angle, 1)
self.rect = self.image.get_rect(center=self.rect.center)
all_sprites = pygame.sprite.Group()
f1_car = Car((300, 300), CAR_IMAGE)
all_sprites.add(f1_car)
carryOn = True
while carryOn:
for event in pygame.event.get():
if event.type == pygame.QUIT:
carryOn = False
elif event.type == pygame.KEYDOWN:
# Set the rotation speed of the car sprite.
if event.key == pygame.K_RIGHT:
f1_car.angle_change = -3
elif event.key == pygame.K_LEFT:
f1_car.angle_change = 3
elif event.type == pygame.KEYUP:
# Stop rotating if the player releases the keys.
if event.key == pygame.K_RIGHT and f1_car.angle_change < 0:
f1_car.angle_change = 0
elif event.key == pygame.K_LEFT and f1_car.angle_change > 0:
f1_car.angle_change = 0
all_sprites.update()
screen.fill(WHITE)
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
This can be done rather simply. You need a game loop that checks for inputs. Then you must check that the desired input is present, and increase the rotation of your car each time the input is present.
import pygame
run = True
car_rotation = 0.0
surface = pygame.Surface((100, 60)) # 100 horizontal length. 60 is the vertical length.
while run:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
car_rotation += 0.1
surface = pygame.transform.rotate(surface, car_rotation)
For the case of your code
You have done some wrong checks in the loop. Change your code from pygame.K_RIGHT to pygame.K_r, to use your R key to rotate your sprite. In order to use the mouse, change the pygame.event.type to .MOUSEBUTTONDOWN or .MOUSEBUTTONUP, and keep pygame.K_RIGHT.
Change the if statement, if event.key == pygame.K_r, to
if event.key == pygame.K_r
car_rotation += 1.0
for car in all_sprites_list:
car.image = pygame.transform.rotate(car.image, car_rotation)
and then remove surface = pygame.Surface((15, 15)).
I'm writing code for a one paddle pong game in pygame where the ball will bounce off of the 3 walls the paddle is not on, and when it does hit the paddle's wall instead of the paddle, it makes you lose a life. My problem is that I cannot figure out the collision detection to do this. Here is what I've done so far:
import pygame, sys
from pygame.locals import *
pygame.init()
screen_width = 640
screen_height = 480
Display = pygame.display.set_mode((screen_width, screen_height), 0, 32)
pygame.display.set_caption('Lonely Pong')
back = pygame.Surface((screen_width, screen_height))
background = back.convert()
background.fill((255, 255, 255))
paddle = pygame.Surface((80, 20))
_paddle = paddle.convert()
_paddle.fill((128, 0, 0))
ball = pygame.Surface((15, 15))
circle = pygame.draw.circle(ball, (0, 0, 0), (7, 7), 7)
_ball = ball.convert()
_ball.set_colorkey((0, 0, 0))
_paddle_x = (screen_width / 2) - 40
_paddle_y = screen_height - 20
_paddlemove = 0
_ball_x, _ball_y = 8, 8
speed_x, speed_y, speed_circle = 250, 250, 250
Lives = 5
clock = pygame.time.Clock()
font = pygame.font.SysFont("verdana", 20)
paddle_spd = 5
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN:
if event.key == K_RIGHT:
_paddlemove = paddle_spd
elif event.key == K_LEFT:
_paddlemove = -paddle_spd
elif event.type == KEYUP:
if event.key == K_RIGHT:
_paddlemove = 0
elif event.key == K_LEFT:
_paddlemove = 0
Livespr = font.render(str(Lives), True, (0, 0, 0))
Display.blit(background, (0, 0))
Display.blit(_paddle, (_paddle_x, _paddle_y))
Display.blit(_ball, (_ball_x, _ball_y))
Display.blit(Livespr, ((screen_width / 2), 5))
_paddle_x += _paddlemove
passed = clock.tick(30)
sec = passed / 1000
_ball_x += speed_x * sec
_ball_y += speed_y * sec
pygame.display.update()
Another problem I've been having is that when I start it up, the ball doesn't appear at all. Could somebody please point me in the right direction?
first things first, the ball doesn't appear because you are not filling the surface, you are setting the color_key:
_ball.set_colorkey((0, 0, 0))
should be
_ball.fill((0, 0, 0))
Second, for collision detection, pygame offers some functions such as collidepoint and colliderect.
https://www.pygame.org/docs/ref/rect.html#pygame.Rect.collidepoint
which means you will need the rectangles of the surfaces. do this by:
my_surface.get_rect()
https://www.pygame.org/docs/ref/surface.html#pygame.Surface.get_rect
In order to collide with the edges of the screen, you can always do
if _ball_x <= 0 or _ball_y <= 0 or _ball_x >= 640 or _ball_y >= 480:
# collision detected