I am currently building a mockup of space invaders.
I want to make the user shoot stuff and the invaders will die (1hit kill), but I can't seem to figure out how to do that. I have tried drawing a small rectangle, but then the background paints over it. Thanks for your help!
import pygame, sys, random
from pygame.locals import *
# set up pygame
pygame.init()
mainClock = pygame.time.Clock()
# set up the window
windowwidth = 800
windowheight = 700
windowSurface = pygame.display.set_mode((windowwidth, windowheight), 0, 32)
pygame.display.set_caption('Sp#c3 inv#d3r5')
# set up movement variables
moveLeft = False
moveRight = False
moveUp = False
moveDown = False
# set up direction variables
DOWNLEFT = 1
DOWNRIGHT = 3
UPLEFT = 7
UPRIGHT = 9
LEFT = 4
RIGHT = 6
UP = 8
DOWN = 2
MOVESPEED = 10
MOVE = 2
# set up counting
score = 0
# set up the colors
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (135, 206, 250)
# set up font
font = pygame.font.SysFont(None, 50)
def makeship(windowwidth): # set up the bouncer and food data structures
user = pygame.Rect(350, 600, 100, 25)
return user
def drawwalls(walls):
for i in range(6):
wall = pygame.Rect(0,i+1,100,25)
pygame.draw.rect(windowSurface,GREEN,wall)
walls.append(wall)
def makeinvaders(invaders):
y = 0
for i in invaders: # each row
x = 0
for j in range(10):
# create invader
invader = pygame.Rect(75+x, 100+y, 40, 25)
# append invader to invaders[row]
i.append(invader)
# increase invader's x attribute by 50
x += 60
# increase invaders y by 35
y += 45
return invaders
def movepaddle(user):
# move the paddle
if moveLeft and user.left > 0:
user.left -= MOVESPEED
if moveRight and user.right < windowwidth:
user.right += MOVESPEED
return user
def moveinvaders(invaders, invdir):
# move the invaders
for row in invaders:
for invader in row:
if invdir == RIGHT and invaders[1][9].right < windowwidth:
invader.right += MOVE
elif invaders[1][9].right > windowwidth:
invader.left -= MOVE
invdir = LEFT
if invdir == LEFT and invaders[0][0].left > 0:
invader.left -= MOVE
elif invaders[0][0].left < 0:
invader.right += MOVE
invdir = RIGHT
return invdir
def shootbullets(windowSurface,blue2):
x = pygame.Rect(400,595,2,5)
pygame.draw.rect(windowSurface,GREEN,x)
def movebullets(bullets):
for bullet in bullets:
pass
def drawstuff(user, invaders):
# draw the user onto the surface
pygame.draw.rect(windowSurface, BLACK, user)
for i in invaders:
for invader in i:
if invader in invaders[0]:
pygame.draw.rect(windowSurface, BLACK, invader)
elif invader in invaders[1]:
pygame.draw.rect(windowSurface, BLACK, invader)
elif invader in invaders[2]:
pygame.draw.rect(windowSurface, BLACK, invader)
elif invader in invaders[3]:
pygame.draw.rect(windowSurface, BLACK, invader)
elif invader in invaders[4]:
pygame.draw.rect(windowSurface, BLACK, invader)
invaders = [[],[],[],[],[]]
invdir = LEFT
walls = []
drawwalls(walls)
# make the figures
user = makeship(windowwidth)
# make invaders
invaders = makeinvaders(invaders)
# run the game loop
while True:
# draw the black background onto the surface
windowSurface.fill(WHITE)
# check for the QUIT event
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
# change the keyboard variables
if event.key == K_LEFT or event.key == ord('a'):
moveRight = False
moveLeft = True
if event.key == K_RIGHT or event.key == ord('d'):
moveLeft = False
moveRight = True
if event.key == K_SPACE:
shootbullets(windowSurface,RED)
if event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == K_LEFT or event.key == ord('a'):
moveLeft = False
if event.key == K_RIGHT or event.key == ord('d'):
moveRight = False
# draw stuff
drawstuff(user, invaders)
# move invaders
invdir = moveinvaders(invaders, invdir)
# paddle movement
user = movepaddle(user)
# draw the window onto the screen
pygame.display.update()
mainClock.tick(80)
Take a look at this minimal space invaders-like game I've written for another question:
import pygame
# you'll be able to shoot every 450ms
RELOAD_SPEED = 450
# the foes move every 1000ms sideways and every 3500ms down
MOVE_SIDE = 1000
MOVE_DOWN = 3500
screen = pygame.display.set_mode((300, 200))
clock = pygame.time.Clock()
pygame.display.set_caption("Micro Invader")
# create a bunch of events
move_side_event = pygame.USEREVENT + 1
move_down_event = pygame.USEREVENT + 2
reloaded_event = pygame.USEREVENT + 3
move_left, reloaded = True, True
invaders, colors, shots = [], [] ,[]
for x in range(15, 300, 15):
for y in range(10, 100, 15):
invaders.append(pygame.Rect(x, y, 7, 7))
colors.append(((x * 0.7) % 256, (y * 2.4) % 256))
# set timer for the movement events
pygame.time.set_timer(move_side_event, MOVE_SIDE)
pygame.time.set_timer(move_down_event, MOVE_DOWN)
player = pygame.Rect(150, 180, 10, 7)
while True:
clock.tick(40)
if pygame.event.get(pygame.QUIT): break
for e in pygame.event.get():
if e.type == move_side_event:
for invader in invaders:
invader.move_ip((-10 if move_left else 10, 0))
move_left = not move_left
elif e.type == move_down_event:
for invader in invaders:
invader.move_ip(0, 10)
elif e.type == reloaded_event:
# when the reload timer runs out, reset it
reloaded = True
pygame.time.set_timer(reloaded_event, 0)
for shot in shots[:]:
shot.move_ip((0, -4))
if not screen.get_rect().contains(shot):
shots.remove(shot)
else:
hit = False
for invader in invaders[:]:
if invader.colliderect(shot):
hit = True
i = invaders.index(invader)
del colors[i]
del invaders[i]
if hit:
shots.remove(shot)
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]: player.move_ip((-4, 0))
if pressed[pygame.K_RIGHT]: player.move_ip((4, 0))
if pressed[pygame.K_SPACE]:
if reloaded:
shots.append(player.copy())
reloaded = False
# when shooting, create a timeout of RELOAD_SPEED
pygame.time.set_timer(reloaded_event, RELOAD_SPEED)
player.clamp_ip(screen.get_rect())
screen.fill((0, 0, 0))
for invader, (a, b) in zip(invaders, colors):
pygame.draw.rect(screen, (150, a, b), invader)
for shot in shots:
pygame.draw.rect(screen, (255, 180, 0), shot)
pygame.draw.rect(screen, (180, 180, 180), player)
pygame.display.flip()
It uses colliderect to detect collisions, which your code is missing.
Also, the code in general and especially the movement code is a lot simpler than yours; so maybe it can serve as an inspiration for you.
Related
This question already has answers here:
How do I rotate an image around its center using Pygame?
(6 answers)
Closed 1 year ago.
I'm trying to rotate a sprite 90 degrees whenever the player presses the A and D keys, and I've got that working, but the sprite is always sent to the top left corner of the screen whenever this happens. Can someone shed some light on this? As well as that, I've tried to get a shooting system working with shooting asteroids, by creating an asteroid and appending it to a list, but it seems to become a tuple instead of a pygame rectangle when I try to change the y of it, can someone help with that as well?
import pygame, sys, random
from pygame.locals import *
#Imports pygame, system and random as modules to use later in the code.
#Also imports extra stuff from pygame that contains useful variables.
pygame.init()
mainClock = pygame.time.Clock()
FONT = pygame.font.SysFont(None, 48)
#Initialises pygame, sets up the clock to stop the program running too fast
#And also makes the font (must happen after pygame initialises)
WINDOWWIDTH = 1000
WINDOWHEIGHT = 1000
BACKGROUNDCOLOUR = (255, 255, 255)
TEXTCOLOUR = (0, 0, 0)
FPS = 60
PLAYERSPEED = 5
PLAYERIMAGE = pygame.image.load("images/P1.png")
PLAYERRECT = PLAYERIMAGE.get_rect()
ASTEROIDIMAGE = pygame.image.load("images/asteroid.png")
ASTEROIDRECT = ASTEROIDIMAGE.get_rect()
ASTEROIDMINSIZE = 3
ASTEROIDMAXSIZE = 5
ASTEROIDSPEED = 5
ASTEROIDS = []
#Defining Variables and setting up player sprite
def terminate():
pygame.quit()
sys.exit()
def pendingKey():
while True:
for event in pygame.event.get():
if event.type == QUIT:
terminate()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
terminate()
return
def drawText(text, font, surface, x, y):
textobj = font.render(text, 1, TEXTCOLOUR)
textrect = textobj.get_rect()
textrect.topleft = (x, y)
surface.blit(textobj, textrect)
#Defining functions, to quit pygame and the system.
#And to wait for the escape key to be pressed to start the terminate function.
#And to wait for the quit event (such as at the end of the game).
#And a function to create text on the screen, such as for the title.
WINDOWSURFACE = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('Space Penguin Remastered')
pygame.mouse.set_visible(False)
#Creates the games window, sets the name of the window, and makes the mouse invisible
WINDOWSURFACE.fill(BACKGROUNDCOLOUR)
drawText('Space Penguin Remastered', FONT, WINDOWSURFACE, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3))
drawText('Press a key to start!', FONT, WINDOWSURFACE, (WINDOWWIDTH / 3) - 30, (WINDOWHEIGHT / 3) + 50)
pygame.display.update()
pendingKey()
#Sets the colour of the background and draws the title and some basic instructions
#And updates the display and activates the terminate function
while True:
LEFT = RIGHT = UP = DOWN = SHOOT = LEFTROTATE = RIGHTROTATE = False
#Sets the players start position to half through the screen, and 50 pixels down
#And sets the movement variables to false
while True:
for event in pygame.event.get():
if event.type == QUIT:
terminate()
#Checks for events, first event being the game quitting
if event.type == KEYDOWN:
if event.key == K_LEFT:
RIGHT = False
LEFT = True
if event.key == K_RIGHT:
LEFT = False
RIGHT = True
if event.key == K_UP:
DOWN = False
UP = True
if event.key == K_DOWN:
UP = False
DOWN = True
if event.key == K_SPACE:
SHOOT = True
if event.key == K_a:
RIGHTROTATE = False
LEFTROTATE = True
if event.key == K_d:
LEFTROTATE = False
RIGHTROTATE = True
#Checks for keys being pressed, corresponding to movement.
if event.key == K_ESCAPE:
terminate()
#Checks for escape being pressed, which quits the game.
if event.type == KEYUP:
if event.key == K_ESCAPE:
terminate()
if event.key == K_LEFT:
LEFT = False
if event.key == K_RIGHT:
RIGHT = False
if event.key == K_UP:
UP = False
if event.key == K_DOWN:
DOWN = False
if event.key == K_SPACE:
SHOOT = False
if event.key == K_a:
RIGHTROTATE = False
LEFTROTATE = False
if event.key == K_d:
LEFTROTATE = False
RIGHTROTATE = False
#Checks whether keys have been let go of.
if LEFT and PLAYERRECT.left > 0:
PLAYERRECT.move_ip(-1 * PLAYERSPEED, 0)
if RIGHT and PLAYERRECT.right < WINDOWWIDTH:
PLAYERRECT.move_ip(PLAYERSPEED, 0)
if UP and PLAYERRECT.top > 0:
PLAYERRECT.move_ip(0, -1 * PLAYERSPEED)
if DOWN and PLAYERRECT.bottom < WINDOWHEIGHT:
PLAYERRECT.move_ip(0, PLAYERSPEED)
if SHOOT:
ASTEROIDS.append(ASTEROIDIMAGE.get_rect(center=PLAYERRECT.midtop))
if LEFTROTATE:
PLAYERIMAGE = pygame.transform.rotate(PLAYERIMAGE, 90)
PLAYERRECT = PLAYERIMAGE.get_rect()
if RIGHTROTATE:
PLAYERIMAGE = pygame.transform.rotate(PLAYERIMAGE, -90)
PLAYERRECT = PLAYERIMAGE.get_rect()
for asteroid in ASTEROIDS:
asteroid.y -= 4
for asteroid in ASTEROIDS:
WINDOWSURFACE.blit(ASTEROIDIMAGE, asteroid)
#Moves the player a certain number of pixels in a direction and shoots asteroids
WINDOWSURFACE.fill(BACKGROUNDCOLOUR)
WINDOWSURFACE.blit(PLAYERIMAGE, PLAYERRECT)
pygame.display.update()
mainClock.tick(FPS)
#Fills the background, draws the players image on the rectangle
#And updates the screen and selects how many frames per second the game should tick by
Thanks in advance
Note that you should only call pygame.event.get() once in your application.
When you rotate, you set your player rect as such:
PLAYERRECT = PLAYERIMAGE.get_rect()
You have never specified the value of PLAYERIMAGE.get_rect() and it is (0, 0) by default, so if the player is transported to the top left of the screen. To fix that, simply remove it, it serves no purpose.
Also, your movement code can be simplified.
keys = pygame.key.get_pressed()
PLAYERRECT.move_ip
(
(keys[K_RIGHT] - keys[K_LEFT]) * PLAYERSPEED,
(keys[K_DOWN] - keys[K_UP]) * PLAYERSPEED
)
Thats it.
Code to handle rotation can be simplified as well. Make a function that gets the players rotation angle:
def getPlayerRotation(keys):
if keys[K_a]: return 90
elif keys[K_d]: return -90
return 0
Then use that to rotate your image and draw it.
#https://stackoverflow.com/questions/4183208/how-do-i-rotate-an-image-around-its-center-using-pygame
rotated_image = pygame.transform.rotate(PLAYERIMAGE, getPlayerRotation(keys))
new_rect = rotated_image.get_rect(center = PLAYERIMAGE.get_rect(topleft = PLAYERRECT.topleft).center)
WINDOWSURFACE.blit(rotated_image, new_rect)
Your asteroid isn't drawing because you are trying to draw it before you clear the screen, which draws over the asteroids.
Also you cannot do
ASTEROIDS.append(ASTEROIDIMAGE.get_rect(center=PLAYERRECT.midtop))
because there is only one asteroid.rect object, so what you are appending is a reference to the same object over and over. You need to create a new rect if your want to use a rect, but I got around the problem by using a list.
ASTEROIDS.append(list(PLAYERRECT.center))
and then later:
for asteroid in ASTEROIDS:
asteroid[1] -= 4
Lastly I changed pending key function:
def pendingKey(events):
for event in events:
if event.type == QUIT:
terminate()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
terminate()
It takes events as argument to avoid calling pygame.event.get() in the function. I also removed the return statement at the end of it since it was causing the function to exit before it got to to through all the events.
Here is the new code:
import pygame, sys, random
from pygame.locals import *
WINDOWWIDTH = 1000
WINDOWHEIGHT = 1000
pygame.init()
mainClock = pygame.time.Clock()
FONT = pygame.font.SysFont(None, 48)
BACKGROUNDCOLOUR = (255, 255, 255)
TEXTCOLOUR = (0, 0, 0)
FPS = 60
PLAYERSPEED = 5
PLAYERIMAGE = pygame.image.load("images/P1.png")
PLAYERRECT = PLAYERIMAGE.get_rect()
ASTEROIDIMAGE = pygame.image.load("images/asteroid.png")
ASTEROIDRECT = ASTEROIDIMAGE.get_rect()
ASTEROIDMINSIZE = 3
ASTEROIDMAXSIZE = 5
ASTEROIDSPEED = 5
ASTEROIDS = []
#Defining Variables and setting up player sprite
def terminate():
pygame.quit()
sys.exit()
def pendingKey(events):
for event in events:
if event.type == QUIT:
terminate()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
terminate()
def drawText(text, font, surface, x, y):
textobj = font.render(text, 1, TEXTCOLOUR)
textrect = textobj.get_rect()
textrect.topleft = (x, y)
surface.blit(textobj, textrect)
WINDOWSURFACE = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('Space Penguin Remastered')
pygame.mouse.set_visible(False)
WINDOWSURFACE.fill(BACKGROUNDCOLOUR)
drawText('Space Penguin Remastered', FONT, WINDOWSURFACE, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3))
drawText('Press a key to start!', FONT, WINDOWSURFACE, (WINDOWWIDTH / 3) - 30, (WINDOWHEIGHT / 3) + 50)
pygame.display.update()
def getPlayerRotation(keys):
if keys[K_a]: return 90
elif keys[K_d]: return -90
return 0
while True:
events = pygame.event.get()
pendingKey(events)
keys = pygame.key.get_pressed()
PLAYERRECT.move_ip((keys[K_RIGHT] - keys[K_LEFT]) * PLAYERSPEED, (keys[K_DOWN] - keys[K_UP]) * PLAYERSPEED)
#https://stackoverflow.com/questions/4183208/how-do-i-rotate-an-image-around-its-center-using-pygame
rotated_image = pygame.transform.rotate(PLAYERIMAGE, getPlayerRotation(keys))
new_rect = rotated_image.get_rect(center = PLAYERIMAGE.get_rect(topleft = PLAYERRECT.topleft).center)
for event in events:
if event.type == KEYDOWN and event.key == K_SPACE:
ASTEROIDS.append(list(PLAYERRECT.center))
for asteroid in ASTEROIDS:
asteroid[1] -= 4
WINDOWSURFACE.fill(BACKGROUNDCOLOUR)
WINDOWSURFACE.blit(rotated_image, new_rect)
for asteroid in ASTEROIDS:
WINDOWSURFACE.blit(ASTEROIDIMAGE, asteroid)
pygame.display.update()
mainClock.tick(FPS)
I've been attempting to make a Space Invaders clone in pygame. I decided to write it so that the bullet shot from the player's ship can only be fired again when it leaves the screen but I've not been capable of doing so. How do I do it?
import pygame
pygame.init()
screen_size = (500, 500)
ship_size = (50, 50)
x, y = 250, 450
x_rect, y_rect = x + 15, y - 20
height_rect, width_rect = 20, 20
vel = 50
shoot = False
"""Loads screen and set gives it a title."""
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption("Space Invaders")
"""Initializes images for the game and resizes them."""
space_ship = pygame.image.load("Space Invaders Ship.jpg")
space_ship = pygame.transform.scale(space_ship, ship_size)
space = pygame.image.load("Space.jpg")
space = pygame.transform.scale(space, screen_size)
clock = pygame.time.Clock()
run = True
while run:
"""Controls the fps."""
clock.tick(60)
"""Registers keyboard's input."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT and x > 0:
x -= vel
elif event.key == pygame.K_RIGHT and x + 50 < 500:
x += vel
elif event.key == pygame.K_SPACE:
x_rect, y_rect = x + 15, y - 20
shoot = True
"""Constantly draws on the screen."""
screen.fill((0, 0, 0))
screen.blit(space, (0, 0))
screen.blit(space_ship, [x, y])
"""Shoots a bullet from where the ship currently is."""
if shoot:
pygame.draw.rect(screen, (250, 250, 0),
[x_rect, y_rect, height_rect, width_rect])
y_rect -= 5
elif y_rect + width_rect > 0:
shoot = False
y_rect = y - 20
pygame.display.flip()
You have to create a list of bullets:
bullet_list = []
Add a new bullet position to the list when SPACE is pressed:
elif event.key == pygame.K_SPACE:
bullet_list.append([x + 15, y - 20])
Move and draw the bullets in the list in a loop:
for bullet in bullet_list:
pygame.draw.rect(screen, (250, 250, 0),
[*bullet, height_rect, width_rect])
bullet[1] -= 5
Delete a bullet from the list if the y-coordinate is less than 0:
for bullet in bullet_list[:]:
if bullet[1] < 0:
bullet_list.remove(bullet)
Complete example:
import pygame
pygame.init()
screen_size = (500, 500)
ship_size = (50, 50)
x, y = 250, 450
x_rect, y_rect = x + 15, y - 20
height_rect, width_rect = 20, 20
vel = 50
bullet_list = []
"""Loads screen and set gives it a title."""
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption("Space Invaders")
"""Initializes images for the game and resizes them."""
space_ship = pygame.image.load("Space Invaders Ship.jpg")
space_ship = pygame.transform.scale(space_ship, ship_size)
space = pygame.image.load("Space.jpg")
space = pygame.transform.scale(space, screen_size)
clock = pygame.time.Clock()
run = True
while run:
"""Controls the fps."""
clock.tick(60)
"""Registers keyboard's input."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT and x > 0:
x -= vel
elif event.key == pygame.K_RIGHT and x + 50 < 500:
x += vel
elif event.key == pygame.K_SPACE:
bullet_list.append([x + 15, y - 20])
"""Constantly draws on the screen."""
screen.fill((0, 0, 0))
screen.blit(space, (0, 0))
screen.blit(space_ship, [x, y])
"""Shoots a bullet from where the ship currently is."""
for bullet in bullet_list:
pygame.draw.rect(screen, (250, 250, 0),
[*bullet, height_rect, width_rect])
bullet[1] -= 5
for bullet in bullet_list[:]:
if bullet[1] < 0:
bullet_list.remove(bullet)
pygame.display.flip()
been trying for a while to find the problem with the following code. I'm trying to have the pointer move up and down like the title of the question states but it just won't move. Any and all help is welcome.
Code for Pause Screen event processing:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP or event.key == pygame.K_w:
self.pointer.moveUp()
print("Up we go")
if event.key == pygame.K_DOWN or event.key == pygame.K_s:
self.pointer.moveDown()
print("Down we go")
if event.key == pygame.K_RETURN:
self.selection = int(pointer.Pointer.getPosition(self.pointer))
print(str(pointer.Pointer.getPosition(self.pointer)))
return True
return False
Code for displaying which shows the pointer in the same place.
self.active_sprite_list.draw(screen)
font = pygame.font.SysFont("serif", 25)
text = []
center_x = []
center_y = []
for counter in range(1,5):
text.append(font.render(self.options[counter-1], True, constants.WHITE))
center_x.append(150)
center_y.append((counter * 120) - (text[counter-1].get_height() // 2) + (self.pointer.image.get_height() // 2))
screen.blit(text[counter-1], [center_x[counter-1],center_y[counter-1]])
pygame.display.flip()
And for reference the same code in the Menu which has the pointer moving up and down:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP or event.key == pygame.K_w:
self.pointer.moveUp()
if event.key == pygame.K_DOWN or event.key == pygame.K_s:
self.pointer.moveDown()
if event.key == pygame.K_RETURN:
self.selection = int(pointer.Pointer.getPosition(self.pointer))
#print(str(self.selection))
return True
###Some code later###
screen.fill(constants.BLACK)
font = pygame.font.SysFont("serif", 25)
for counter in range(1,5):
text = font.render(self.options[counter-1], True, constants.WHITE)
center_x = 150
center_y = (counter * 120) - (text.get_height() // 2) + (self.pointer.image.get_height() // 2)
screen.blit(text, [center_x, center_y])
self.active_sprite_list.draw(screen)
pygame.display.flip()
And before you suggest, the screen for the pause has been declared before here:
while notPaused == False:
#print("Received")
notPaused = pause.processEvents()
print(str(notPaused))
if firstTime == True:
self.pauseScreen.fill(constants.ABLACK)
pause.displayFrame(self.pauseScreen)
self.pauseScreen.set_alpha(128)
screen.blit(self.pauseScreen, [0,0])
firstTime = False
pause.displayFrame(self.pauseScreen)
clock.tick(60)
As per requested, here is the MoveUp and MoveDown functions in the Pointer Class:
def moveUp(self):
if self.rect.y <= 120:
self.rect.y = 480
else:
self.rect.y -= 120
def moveDown(self):
if self.rect.y >= 480:
self.rect.y = 120
else:
self.rect.y += 120
And as suggested, the modular/self-contained code that can be run on its own as long as you have some kind of image in a Resources Folder next to the saved code file.
import pygame, sys
"""
Global constants
"""
# Colors
ABLACK = ( 0, 0, 0, 125)
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
BLUE = ( 0, 0, 255)
YELLOW = ( 255, 255, 0)
# Screen dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class Pointer(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Resources/Pointer.png")
self.rect = self.image.get_rect()
self.rect.x = 100
self.rect.y = 120
def moveUp(self):
if self.rect.y <= 120:
self.rect.y = 480
else:
self.rect.y -= 120
print("Within pointer object moving up from ",self.rect.x,self.rect.y)
def moveDown(self):
if self.rect.y >= 480:
self.rect.y = 120
else:
self.rect.y += 120
print("Within pointer object moving up from ",self.rect.x,self.rect.y)
def getPosition(self):
self.position = self.rect.y / 120
return self.position
class Pause(object):
def __init__(self,screen):
self.selection = 4
self.options = ["Resume Game","Review Controls","Back to Menu","Quit"]
self.active_sprite_list = pygame.sprite.Group()
self.pointer = Pointer()
self.active_sprite_list.add(self.pointer)
def processEvents(self):
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP or event.key == pygame.K_w:
self.pointer.moveUp()
print("Up we go")
if event.key == pygame.K_DOWN or event.key == pygame.K_s:
self.pointer.moveDown()
print("Down we go")
if event.key == pygame.K_RETURN:
self.selection = int(Pointer.getPosition(self.pointer))
print(str(Pointer.getPosition(self.pointer)))
return False
return True
def displayFrame(self,screen):
self.active_sprite_list.draw(screen)
for pointer in self.active_sprite_list:
print("The y value of this pointer is:" + str(pointer.rect.y))
font = pygame.font.SysFont("serif", 25)
text = []
center_x = []
center_y = []
for counter in range(1,5):
text.append(font.render(self.options[counter-1], True, WHITE))
center_x.append(150)
center_y.append((counter * 120) - (text[counter-1].get_height() // 2) + (self.pointer.image.get_height() // 2))
for pointer in self.active_sprite_list:
print("The y value of this pointer is:" + str(pointer.rect.y))
screen.blit(text[counter-1], [center_x[counter-1],center_y[counter-1]])
for pointer in self.active_sprite_list:
print("The y value of this pointer is:" + str(pointer.rect.y))
pygame.display.flip()
pygame.init()
pygame.display.set_caption("Pause Error Finder")
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
Paused = True
clock = pygame.time.Clock()
pauseScreen = pygame.Surface(size,pygame.SRCALPHA,32)
pauseScreen.fill(ABLACK)
pause = Pause(screen)
pauseScreen.set_alpha(128)
Paused = pause.processEvents()
print(str(notPaused))
pause.displayFrame(pauseScreen)
screen.blit(pauseScreen, [0,0])
while Paused:
notPaused = pause.processEvents()
print(str(Paused))
pause.displayFrame(pauseScreen)
#screen.blit(pauseScreen, [0,0])
clock.tick(60)
Your issue is in the main game loop, first off, you had the blitting of Pause Screen to Screen commented out. readding that in gave the pointer seeming to multiply and go all over the place (getting closer!).
The reason it does that is you did not update your pauseScreen in each pass of the loop. Your displayFrame will add your pointer to the proper location, but the one from last frame, and 2 frames ago, and... are still there. by moving the lines
pauseScreen.fill(ABLACK)
pauseScreen.set_alpha(128)
And placing them within your game loop, the pause screen is reset ever frame and only the latest pointer is displayed. Here is the updated game loop:
pygame.init()
pygame.display.set_caption("Pause Error Finder")
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
notPaused = False
clock = pygame.time.Clock()
pauseScreen = pygame.Surface(size,pygame.SRCALPHA,32)
pause = Pause(screen)
notPaused = pause.processEvents()
print(str(notPaused))
pause.displayFrame(pauseScreen)
screen.blit(pauseScreen, [0,0])
while not notPaused:
notPaused = pause.processEvents()
print(str(notPaused))
pauseScreen.fill(ABLACK)
pauseScreen.set_alpha(128)
pause.displayFrame(pauseScreen)
screen.blit(pauseScreen, [0,0])
clock.tick(60)
I'm trying to create a little game as a training, but I'm blocked because I don't know how I can collide 2 moving cubes.
The game is simple, there is a red box that you can move and if this box touches a green cube, then you lost. (the green cubes are always moving)
I tried to read some documentations but it's not really easy to understand as a beginner.
Here is the code:
import pygame
import random
from threading import Timer
pygame.init()
screenWidth = 1100
screenHeight = 600
white = (255,255,255)
red = (255, 0, 0)
yellow = (50, 250, 20)
FPS = 60
gameDisplay = pygame.display.set_mode((screenWidth, screenHeight))
pygame.display.set_caption('Tekken')
pygame.display.update()
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 28)
class Players:
def __init__(self, playerName, playerAttribute, cubeheight, cubewidth, missilesHeight, missilesWidth):
self.playerName = playerName
self.playerAttribute = playerAttribute
self.playerLife = 100
self.droite_x = 300
self.droite_y = 600
self.cubeheight = cubeheight
self.cubewidth = cubewidth
self.missiles = True
self.missilesHeight = missilesHeight
self.missilesWidth = missilesWidth
self.missiles_droite_x = 0
self.missiles_droite_y = round(random.randrange(50, screenHeight-50))
self.missiles_droite_x_inverse = screenWidth-50
self.missiles_droite_y_inverse = round(random.randrange(50, screenHeight-50))
self.vitesse_missiles = 10
print(self.playerName, self.playerAttribute, self.playerLife)
def environment_un(self):
gameExit = False
gameOver = False
droite_x_change = 0
droite_y_change = 0
missiles_droite_x_change = 0
missiles_droite_x_change_inverse = 0
while not gameExit:
while gameOver:
gameDisplay.fill(red)
screen_text = font.render("Game Over, do you want to play again? [Q] to quit", True, white)
gameDisplay.blit(screen_text, [100, 300])
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
gameOver = False
gameExit = True
break
if event.type == pygame.QUIT:
gameOver = False
gameExit = True
break
for event in pygame.event.get(): #va chercher les events
if event.type == pygame.QUIT: #Si j'appuie sur X
gameExit = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
droite_x_change = -3
if event.key == pygame.K_RIGHT:
droite_x_change = +3
if event.key == pygame.K_UP:
droite_y_change = -3
if event.key == pygame.K_DOWN:
droite_y_change = +3
if event.key == pygame.K_SPACE:
missiles_droite_x_change = self.vitesse_missiles
missiles_droite_x_change_inverse = -self.vitesse_missiles
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
droite_x_change = 0
if event.key == pygame.K_RIGHT:
droite_x_change = 0
if event.key == pygame.K_UP:
droite_y_change = 0
if event.key == pygame.K_DOWN:
droite_y_change = 0
self.missiles_droite_x_inverse += missiles_droite_x_change_inverse
self.missiles_droite_x += missiles_droite_x_change
self.droite_x += droite_x_change
self.droite_y += droite_y_change
if self.droite_y + self.cubeheight <= 0:
self.droite_y = 0
elif self.droite_y + self.cubeheight >= screenHeight:
self.droite_y = screenHeight-self.cubeheight
elif self.droite_x + self.cubewidth <= 0:
self.droite_x = 0
elif self.droite_x + self.cubewidth >= screenWidth:
self.droite_x = screenWidth-self.cubewidth
gameDisplay.fill(white)
gameDisplay.fill(red, rect=[self.droite_x, self.droite_y, self.cubewidth, self.cubeheight])
gameDisplay.fill(yellow, rect=[self.missiles_droite_x, self.missiles_droite_y, self.missilesWidth, self.missilesHeight])
gameDisplay.fill(yellow, rect=[self.missiles_droite_x_inverse, self.missiles_droite_y_inverse, self.missilesWidth, self.missilesHeight])
pygame.display.update()
if self.missiles_droite_x + self.missilesWidth >= screenWidth:
missiles_droite_x_change = 0
if missiles_droite_x_change == 0:
self.missiles_droite_x = 0
self.missiles_droite_y = round(random.randrange(50, screenHeight-50))
missiles_droite_x_change = self.vitesse_missiles
if self.missiles_droite_x_inverse <= 0:
missiles_droite_x_change_inverse = 0
if missiles_droite_x_change >= 0:
self.missiles_droite_x_inverse = screenWidth-50
self.missiles_droite_y_inverse = round(random.randrange(50, screenHeight-50))
missiles_droite_x_change_inverse = -12
clock.tick(FPS)
pygame.quit()
Player_1 = Players('John', 'sometext', 50, 50, 100, 100)
Player_1.environment_un()
What should do I in order to detect the collision?
I can not run your code at the moment as I dont have pygame installed. However, you can use the pygame.sprite.collide_rect() if you declare your objects to have in their class an pygame.sprite.Sprite-object or inherit from that class (as suggested below). The code below may note work as I can not test it but it should be close to a functioning code snippet. In the case you would like to test collision of a sprite against multiple other sprites - consider looking at pygame.sprite.Group(). I believe that something like this should work:
class SpriteObject(pygame.sprite.Sprite):
def __init__(self,pos_x, pos_y):
pygame.sprite.Sprite.__init__(self)
self.rect = self.original.get_rect()
self.rect.center = (pos_x, pos_y)
class Players:
def __init__(self, playerName, playerAttribute, cubeheight, cubewidth, missilesHeight, missilesWidth):
sprite1 = SpriteObject(1,2)
sprite2 = SpriteObject(1,2)
sprite1.rect.collide_rect(sprite2)
If you are looking for a conceptual answer:
Since you are considering just cubes and if they are of the same size, two cubes will occupy the same space 'if and only if' a corner of one cube is between (inclusive) two parallel planes of another. There are many ways to do this in practice.
I would check if between by evaluating an inward normal vector of cube 1 dotted with a vector to a corner (of cube 2) from any corner (of cube 1) . Do so for both parallel sides. If both are positive, its inside.
It's slightly more complicated for different shapes and varying sizes.
Use pygame.Rect() to keep cube position and size - and then you can use pygame.Rect.colliderect() to check collision between two cubes.
cube1 = pygame.Rect((x1, y1), (width, height))
cube2 = pygame.Rect((x2, y2), (width, height))
if cube1.colliderect(cube2):
print("Collision !")
PyGame has other usefull classes - pygame.sprite.Sprite and pygame.sprite.Group - which use Rect and collision detection functions.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I am new to Python and I have modified dodger (here's the link http://inventwithpython.com/dodger.py) to add ''goodies'', sprites similar to the baddies, but that give you score when you touch them; instead of killing you as the baddies do.
(I have made a change at the start with easygui too, but it works fine).
I am really confused as this code works (I mean this code starts) but the goodies don't appear, like if I didn't put them in at all. I have tried to figure out by myself what the problem is but I haven't found it. The source code is long but there are some comments to make it more readable. I think that the multimedia files are right because it doesn't give me error messages.
Here you have the not working program:
import pygame, random, sys
from pygame.locals import *
import easygui
#Message to make the user decide the hardness of the game
msg = 'Inserisci un numero da 1 a 20\n per la difficoltà: \n1 = Semplice\n 20 = Impossibile'
title = 'Difficoltà'
#Message to make the user decide the colour of the background of the game
Difficoltà = easygui.enterbox(msg,title)
msg = "Quale colore preferisci fra questi come sfondo?"
choices = ["Nero","Blu","Verde"]
COLORESCELTODALLUTENTE = easygui.buttonbox(msg,choices=choices)
#Unused Values as it runs in fullscreen mode
WINDOWWIDTH = 800
WINDOWHEIGHT = 600
#The text is white
TEXTCOLOR = (255, 255, 255)
#Changes the colour of the background according to the choice of the user
if COLORESCELTODALLUTENTE == 'Nero':
BACKGROUNDCOLOR = (0, 0, 0)
elif COLORESCELTODALLUTENTE == 'Blu':
BACKGROUNDCOLOR = (36, 68, 212)
elif COLORESCELTODALLUTENTE == 'Verde':
BACKGROUNDCOLOR = (36, 237, 52)
#Frames per second the game will run at
FPS = 40
#Description of the baddies
baddie_type_1MINSIZE = 20
baddie_type_1MAXSIZE = 40
baddie_type_1MINSPEED = 4
baddie_type_1MAXSPEED = 5
ADDNEWbaddie_type_1RATE = 21 - int(Difficoltà)
#Description of the goddies
goddie_type_1MINSIZE = 20
goddie_type_1MAXSIZE = 40
goddie_type_1MINSPEED = 4
goddie_type_1MAXSPEED = 5
ADDNEWgoddie_type_1RATE = 10
#How fast you move with the arrows
PLAYERMOVERATE = 5
def terminate():
pygame.quit()
sys.exit()
def waitForPlayerToPressKey():
while True:
for event in pygame.event.get():
if event.type == QUIT:
terminate()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: # pressing escape quits
terminate()
return
def playerHasHitbaddie_type_1(playerRect, baddies_type_1):
for b in baddies_type_1:
if playerRect.colliderect(b['rect_b']):
return True
return False
def playerHasHitgoddie_type_1(playerRect, goddies_type_1):
for g in goddies_type_1:
if playerRect.colliderect(g['rect_g']):
return True
return False
def drawText(text, font, surface, x, y):
textobj = font.render(text, 1, TEXTCOLOR)
textrect = textobj.get_rect()
textrect.topleft = (x, y)
surface.blit(textobj, textrect)
# set up pygame, the window, and the mouse cursor
pygame.init()
mainClock = pygame.time.Clock()
#This down here is windowed mode
#windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
#This down here is fullscreen mode
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), pygame.FULLSCREEN)
pygame.display.set_caption('Dodger')
pygame.mouse.set_visible(False)
# set up fonts
font = pygame.font.SysFont(None, 48)
# set up sounds
gameOverSound = pygame.mixer.Sound('Gameover.wav')
pygame.mixer.music.load('Background.mp3')
# set up images
playerImage = pygame.image.load('Player.png')
playerRect = playerImage.get_rect()
baddie_type_1Image = pygame.image.load('Baddie_type_1.png')
goddie_type_1Image = pygame.image.load('Goddie_type_1.png')
# show the "Start" screen
drawText('Dodger', font, windowSurface, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3))
drawText('Press a key to start.', font, windowSurface, (WINDOWWIDTH / 3) - 30, (WINDOWHEIGHT / 3) + 50)
pygame.display.update()
waitForPlayerToPressKey()
topScore = 0
while True:
# set up the start of the game
baddies_type_1 = []
goddies_type_1 = []
score = 0
playerRect.topleft = (WINDOWWIDTH / 2, WINDOWHEIGHT - 50)
moveLeft = moveRight = moveUp = moveDown = False
reverseCheat = slowCheat = False
baddie_type_1AddCounter = 0
goddie_type_1AddCounter = 0
pygame.mixer.music.play(-1, 0.0)
while True: # the game loop runs while the game part is playing
score += 1 # increase score
for event in pygame.event.get():
if event.type == QUIT:
terminate()
if event.type == KEYDOWN:
if event.key == ord('z'):
reverseCheat = True
if event.key == ord('x'):
slowCheat = True
if event.key == K_LEFT or event.key == ord('a'):
moveRight = False
moveLeft = True
if event.key == K_RIGHT or event.key == ord('d'):
moveLeft = False
moveRight = True
if event.key == K_UP or event.key == ord('w'):
moveDown = False
moveUp = True
if event.key == K_DOWN or event.key == ord('s'):
moveUp = False
moveDown = True
if event.type == KEYUP:
if event.key == ord('z'):
reverseCheat = False
score = 0
if event.key == ord('x'):
slowCheat = False
score = 0
if event.key == K_ESCAPE:
terminate()
if event.key == K_LEFT or event.key == ord('a'):
moveLeft = False
if event.key == K_RIGHT or event.key == ord('d'):
moveRight = False
if event.key == K_UP or event.key == ord('w'):
moveUp = False
if event.key == K_DOWN or event.key == ord('s'):
moveDown = False
if event.type == MOUSEMOTION:
# If the mouse moves, move the player where the cursor is.
playerRect.move_ip(event.pos[0] - playerRect.centerx, event.pos[1] - playerRect.centery)
# ize is for size
# Add new baddies_type_1 at the top of the screen, if needed.
if not reverseCheat and not slowCheat:
baddie_type_1AddCounter += 1
if baddie_type_1AddCounter == ADDNEWbaddie_type_1RATE:
baddie_type_1AddCounter = 0
baddies_type_1ize = random.randint(baddie_type_1MINSIZE, baddie_type_1MAXSIZE)
newbaddie_type_1 = {'rect_b': pygame.Rect(random.randint(0, WINDOWWIDTH-baddies_type_1ize), 0 - baddies_type_1ize, baddies_type_1ize, baddies_type_1ize),
'speed_b': random.randint(baddie_type_1MINSPEED, baddie_type_1MAXSPEED),
'surface_b':pygame.transform.scale(baddie_type_1Image, (baddies_type_1ize, baddies_type_1ize)),
}
baddies_type_1.append(newbaddie_type_1)
# ize is for size
# Add new goddies_type_1 at the top of the screen, if needed.
if not reverseCheat and not slowCheat:
goddie_type_1AddCounter += 1
if goddie_type_1AddCounter == ADDNEWgoddie_type_1RATE:
goddie_type_1AddCounter = 0
goddies_type_1ize = random.randint(goddie_type_1MINSIZE, goddie_type_1MAXSIZE)
newgoddie_type_1 = {'rect_g': pygame.Rect(random.randint(0, WINDOWWIDTH-goddies_type_1ize), 0 - goddies_type_1ize, goddies_type_1ize, goddies_type_1ize),
'speed_g': random.randint(goddie_type_1MINSPEED, goddie_type_1MAXSPEED),
'surface_g':pygame.transform.scale(goddie_type_1Image, (goddies_type_1ize, goddies_type_1ize)),
}
# Move the player around.
if moveLeft and playerRect.left > 0:
playerRect.move_ip(-1 * PLAYERMOVERATE, 0)
if moveRight and playerRect.right < WINDOWWIDTH:
playerRect.move_ip(PLAYERMOVERATE, 0)
if moveUp and playerRect.top > 0:
playerRect.move_ip(0, -1 * PLAYERMOVERATE)
if moveDown and playerRect.bottom < WINDOWHEIGHT:
playerRect.move_ip(0, PLAYERMOVERATE)
# Move the mouse cursor to match the player.
pygame.mouse.set_pos(playerRect.centerx, playerRect.centery)
# Move the baddies_type_1 down.
for b in baddies_type_1:
if not reverseCheat and not slowCheat:
b['rect_b'].move_ip(0, b['speed_b'])
elif reverseCheat:
b['rect_b'].move_ip(0, -5)
elif slowCheat:
b['rect_b'].move_ip(0, 1)
# Move the goddies_type_1 down.
for g in goddies_type_1:
if not reverseCheat and not slowCheat:
g['rect_g'].move_ip(0, g['speed_g'])
elif reverseCheat:
g['rect_g'].move_ip(0, -5)
elif slowCheat:
g['rect_g'].move_ip(0, 1)
# Delete baddies_type_1 that have fallen past the bottom.
for b in baddies_type_1[:]:
if b['rect_b'].top > WINDOWHEIGHT:
baddies_type_1.remove(b)
# Delete goddies_type_1 that have fallen past the bottom.
for g in goddies_type_1[:]:
if g['rect_g'].top > WINDOWHEIGHT:
goddies_type_1.remove(g)
# Draw the game world on the window.
windowSurface.fill(BACKGROUNDCOLOR)
# Draw the score and top score.
drawText('Score: %s' % (score), font, windowSurface, 10, 0)
drawText('Top Score: %s' % (topScore), font, windowSurface, 10, 40)
# Draw the player's rectangle
windowSurface.blit(playerImage, playerRect)
# Draw each baddie_type_1
for b in baddies_type_1:
windowSurface.blit(b['surface_b'], b['rect_b'])
# Draw each goddie_type_1
for g in goddies_type_1:
windowSurface.blit(g['surface_g'], g['rect_g'])
pygame.display.update()
# Check if any of the baddies_type_1 have hit the player.
if playerHasHitbaddie_type_1(playerRect, baddies_type_1):
if score > topScore:
topScore = score # set new top score
break
# Check if any of the goddies_type_1 have hit the player.
if playerHasHitgoddie_type_1(playerRect, goddies_type_1):
score = score + 200
#Frapes of the game
mainClock.tick(FPS)
# Stop the game and show the "Game Over" screen.
pygame.mixer.music.stop()
gameOverSound.play()
drawText('GAME OVER', font, windowSurface, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3))
drawText('Press a key to play again.', font, windowSurface, (WINDOWWIDTH / 3) - 80, (WINDOWHEIGHT / 3) + 50)
pygame.display.update()
waitForPlayerToPressKey()
gameOverSound.stop()
After your # Add new baddies_type_1 at the top of the screen, if needed. code, it looks like you actually add the baddie with this line:
baddies_type_1.append(newbaddie_type_1)
You don't appear to be doing that with your goodies code. Try adding:
goddies_type_1.append(newgoddie_type_1)
after your # Add new goddies_type_1 at the top of the screen, if needed. if statements.
Also, you spelled goodies as goddies throughout your code.