Sprite Object not being shown on display (Pygame)? - python

I am currently trying to develop a small, basic game using Pygame. However, I am not able to draw my sprite object onto the screen. My code is below:
import pygame
# window
WIDTH = 400
HEIGHT = 400
FPS = 60
# colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
PINK = (255, 0, 191)
BROWN = (102, 51, 0)
GRAY = (102, 102, 51)
# player
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50,50))
self.image.fill(GREEN)
# variables needed for movement
self.rect = self.image.get_rect()
self.rect.centerx = 200
self.rect.bottom = 200
self.speedx = 0
self.speedy = 0
def update(self):
# move sprite
self.speedx = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_w]:
self.speedx = -5
if keystate[pygame.K_d]:
self.speedx = 5
self.rect.x += self.speedx
# boundary collision
if self.rect.x < 0:
self.rect.x = 0
elif self.rect.x < 400:
self.rect.x = 400
# initialisation
pygame.init()
# sound
pygame.mixer.init()
# draw window
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Shoot")
clock = pygame.time.Clock()
# sprite group
all_sprites = pygame.sprite.Group()
# create player
player = Player()
# add sprites to sprite group
all_sprites.add(player)
# game loop
running = True
while running:
# keeps speed constant
clock.tick(FPS)
# allows for exit
for event in pygame.event.get():
# check before closing window
if event.type == pygame.QUIT:
running = False
# update sprites
all_sprites.update()
# draw screen
#screen.fill(BLACK)
all_sprites.draw(screen)
#player.draw(screen)
# used to draw
pygame.display.flip()
pygame.quit()
As you can see in the code, I have a player object which I create from the Player class. I then add this to an all_sprites group. This should then be drawn on the screen. However, nothing is drawn

The issue is that the rectangle of the Player object is out of the window, because of if self.rect.x < 400: self.rect.x = 400 in:
class Player(pygame.sprite.Sprite):
# [...]
def update(self):
# [...]
if self.rect.x < 0:
self.rect.x = 0
elif self.rect.x < 400:
self.rect.x = 400
It has to be self.rect.x > 400:
class Player(pygame.sprite.Sprite):
# [...]
def update(self):
# [...]
if self.rect.x < 0:
self.rect.x = 0
elif self.rect.x > 400: # <----
self.rect.x = 400

Related

Collison and Resetting in PyGame [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 1 year ago.
My problem: I am trying to create a vertical ball drop game, and I am testing for collision, which does work. But how would I reset my ball when it hits the hoop? Instead of it detecting and then hitting the bottom of the screen which results in a Game over screen because you lose. Any help is appreciated.
import time, random
from pygame.locals import *
import pygame, sys
pygame.init()
FPS = 60
FramePerSec = pygame.time.Clock()
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 600
SCREEN_BOTTOM = 0
SPEED = 5
SCORE = 0
font = pygame.font.SysFont("Verdana", 60)
font_small = pygame.font.SysFont("Verdana", 20)
game_over = font.render("Game Over", True, BLACK)
background = pygame.image.load("background.jpg")
DISPLAYSURF = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
DISPLAYSURF.fill(WHITE)
pygame.display.set_caption("Ball Drop")
class Ball(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("Ball.png")
self.rect = self.image.get_rect()
self.rect.center = (random.randint(40, SCREEN_WIDTH - 40), 0)
def move(self):
global SCORE
self.rect.move_ip(0, SPEED)
if (self.rect.bottom > 600):
self.rect.top = 0
self.rect.center = (random.randint(30, 380), 0)
# Need to check PNG for hit detection
class Basket(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("Basket.png")
self.rect = self.image.get_rect()
self.rect.center = (160, 520)
def move(self):
pressed_keys = pygame.key.get_pressed()
if(self.rect.x >= (SCREEN_WIDTH - 145)):
self.rect.x -= 5;
elif(self.rect.x <= -5):
self.rect.x += 5;
else:
if pressed_keys[pygame.K_a]:
self.rect.move_ip(-SPEED, 0)
if pressed_keys[pygame.K_d]:
self.rect.move_ip(SPEED, 0)
class Wall(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("wall.png")
self.rect = self.image.get_rect()
self.rect.center = (0, 670)
B2 = Basket()
B1 = Ball()
W1 = Wall()
balls = pygame.sprite.Group()
balls.add(B1)
# Need to fix wall sprite group
walls = pygame.sprite.Group()
walls.add(W1)
all_sprites = pygame.sprite.Group()
all_sprites.add(B2)
all_sprites.add(B1)
INC_SPEED = pygame.USEREVENT + 1
pygame.time.set_timer(INC_SPEED, 1000)
while True:
for event in pygame.event.get():
if event.type == INC_SPEED:
SPEED += 0.3
if event.type == QUIT:
pygame.quit()
sys.exit()
DISPLAYSURF.blit(background, (0, 0))
scores = font_small.render(str(SCORE), True, BLACK)
DISPLAYSURF.blit(scores, (10, 10))
for entity in all_sprites:
DISPLAYSURF.blit(entity.image, entity.rect)
entity.move()
# NEed to fix collison and Counting stats
if pygame.sprite.spritecollideany(W1, balls):
DISPLAYSURF.fill(RED)
DISPLAYSURF.blit(game_over, (30, 250))
pygame.display.update()
for entity in all_sprites:
entity.kill()
time.sleep(2)
pygame.quit()
sys.exit()
if pygame.sprite.spritecollideany(B2, balls):
print("Hit")
SCORE += 1
pygame.display.update()
pygame.display.update()
FramePerSec.tick(FPS)
pygame.sprite.spritecollideany() returns the hit Sprite (ball) object. Change the position of this ball:
while True:
# [...]
ball_hit = pygame.sprite.spritecollideany(B2, balls)
if ball_hit:
ball_hit.rect.center = (random.randint(30, 380), 0)
SCORE += 1
print("Hit")
# [...]

Pygame Collision not working

For my computing project I need to make some sort of app or game. I decided I would make space invaders. I have managed to get the basics working, the sprites would spawn I could move the player, the enemies fell from the top of the screen and the background and music worked properly, but the collision is not working properly. When the corners touch or the midpoint between two corners touch then it works fine, the program closes. But if the corners/midpoints don't touch then the sprites pass right through each other. Any help would be much appreciated. Here is the code :
import pygame
import random
import sys
# --- constants --- (UPPER_CASE names)
WHITE = (255, 255, 255)
BLACK = ( 0, 0, 0)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
ORANGE = (255, 255, 0)
YELLOW = ( 0, 255, 255)
DISPLAY_WIDTH = 720
DISPLAY_HEIGHT = 720
FPS = 60
# --- classes --- (CamelCase names)
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("images\\user1.gif").convert()
self.image = pygame.transform.scale(self.image, (50, 50))
self.rect = self.image.get_rect()
self.speed_x = 0
def update(self):
self.speed_x = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speed_x = -7
if keystate[pygame.K_RIGHT]:
self.speed_x = 7
self.rect.x += self.speed_x
if self.rect.right > DISPLAY_WIDTH:
self.rect.right = DISPLAY_WIDTH
if self.rect.left < 0:
self.rect.left = 0
class Mob(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("images\\enemy1.gif").convert()
self.image = pygame.transform.scale(self.image, (50, 50))
self.rect = self.image.get_rect()
self.speed_x = 0
self.rect.x = random.randrange(0, DISPLAY_WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(3, 11)
def update(self):
self.rect.y +=self.speedy
if self.rect.top > DISPLAY_HEIGHT + 10:
self.rect.x = random.randrange(0, DISPLAY_WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(3, 11)
# --- functions --- (lower_case names)
# empty
# --- main --- (lower_case names)
# - init -
pygame.init()
pygame.mixer.init()
display = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
display_rect = display.get_rect()
# - objects -
all_sprites = pygame.sprite.Group()
mobs = pygame.sprite.Group()
player = Player()
player.rect.center = ((DISPLAY_WIDTH / 2), DISPLAY_HEIGHT/1.2)
all_sprites.add(player)
for z in range(8):
mob = Mob()
mobs.add(mob)
all_sprites.add(mob)
background = pygame.image.load("images\\background.jpg")
background = pygame.transform.scale(background, (DISPLAY_WIDTH, DISPLAY_HEIGHT))
# - other -
pygame.mixer.music.load("audio\\soundtrack.mp3")
pygame.mixer.music.play(-1)
pygame.mixer.music.set_volume(0.4)
# - mainloop -
crashed = False
clock = pygame.time.Clock()
while not crashed:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
print(event)
# - checks for a hit -
col = pygame.sprite.collide_rect(mob, player)
if col:
sys.exit()
# - updates (without draws) -
all_sprites.update()
# - draws (without updates) -
display.blit(background, (0, 0))
all_sprites.draw(display)
pygame.display.update()
# - FPS -
clock.tick(FPS)
# - end -
pygame.quit()
Any help would be great, thanks.
The function pygame.sprite.collide_rect is not intended for direct use. It's intended to be passed into the 'main' collide functions
pygame.sprite.spritecollide()
pygame.sprite.groupcollide()
pygame.sprite.spritecollideany()
In your case you want pygame.sprite.spritecollideany:
col = pygame.sprite.spritecollideany(player, mobs)
if col:
sys.exit()
This function will return one in mobs that collided with player.

Pygame logic issue

I'm trying to develop a Brick Breaker/Breakout game using pygame, but I'm facing a annoying problem that blots the "ball" on the paddle(player) when I move with the arrows, i'm lefting the code for you to have a look.
import pygame
pygame.init()
isRunning = True
WIDTH = 800
HEIGHT = 600
FPS = 60
clock = pygame.time.Clock()
window = pygame.display.set_mode((WIDTH, HEIGHT))
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((60, 30))
self.image.fill((200, 255, 200))
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 30
def update(self):
self.speedx = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_a]:
self.speedx = -5
if keystate[pygame.K_d]:
self.speedx = 5
self.rect.x += self.speedx
if self.rect.right + self.speedx > WIDTH:
self.rect.right = WIDTH
if self.rect.left + self.speedx < 0:
self.rect.left = 0
ball = Ball(self.rect.centerx, self.rect.top)
for i in all_sprites:
print(i)
all_sprites.add(ball)
def shoot(self):
ball = Ball(self.rect.centerx, self.rect.top)
all_sprites.add(ball)
balls.add(ball)
class Ball(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 10))
self.image.fill((100, 150, 200))
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = -3
def update(self):
pass
#self.rect.y += self.speedy
all_sprites = pygame.sprite.Group()
balls = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
while isRunning:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
isRunning = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.shoot()
window.fill((0, 0, 30))
all_sprites.update()
all_sprites.draw(window)
pygame.display.flip()
pygame.quit()
quit()
I'm not sure if I understand your question, but the problem with the code is that you're creating a ball every time you're updating the player. Just remove the last four lines in the player update method and uncomment the line in the ball's update method and everything works as it should.
Additional tips
Delete the balls when they exit the screen. This will prevent the game from running slow or crashing. It can be don by adding if self.rect.bottom < 0: self.kill() in the ball update method.
Use elif when appropriate.
In the player update method you define an attribute self.speedx which is only used inside that method, thus it's better to just use a local variable instead. Also, it's discourage to define attributes outside the __init__ method.
Here's your code slightly modified.
import pygame
pygame.init()
is_running = True # Use lowercase_and_underscore for variable names.
WIDTH = 800
HEIGHT = 600
FPS = 60
clock = pygame.time.Clock()
window = pygame.display.set_mode((WIDTH, HEIGHT))
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((60, 30))
self.image.fill((200, 255, 200))
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 30
def update(self):
speedx = 0 # Don't define attributes outside __init__. Local variable works in this case instead.
keystate = pygame.key.get_pressed()
if keystate[pygame.K_a]:
speedx = -5
elif keystate[pygame.K_d]: # Use elif so it doesn't need to check this condition if the above is true.
speedx = 5
self.rect.x += speedx
if self.rect.right + speedx > WIDTH:
self.rect.right = WIDTH
elif self.rect.left + speedx < 0: # Use elif so it doesn't need to check this condition if the above is true.
self.rect.left = 0
def shoot(self):
ball = Ball(self.rect.centerx, self.rect.top)
all_sprites.add(ball)
balls.add(ball)
class Ball(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 10))
self.image.fill((100, 150, 200))
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = -3
def update(self):
if self.rect.bottom < 0: # Chack if the ball has exit above the screen.
self.kill() # If it has it should delete itself.
self.rect.y += self.speedy
all_sprites = pygame.sprite.Group()
balls = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
while is_running:
clock.tick(FPS)
print(all_sprites)
for event in pygame.event.get():
if event.type == pygame.QUIT:
is_running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.shoot()
window.fill((0, 0, 30))
all_sprites.update()
all_sprites.draw(window)
pygame.display.flip()
pygame.quit()
quit()
EDIT: KEEPING THE BALL ON THE PADDLE AND LAUNCHING WITH SPACE
First you could create an attribute self.current_ball in the Player class which will reference the ball on the paddle. Then in the update method of the Player class you update the ball's position relative to the paddle.
To keep the ball at the paddle you have to change the ball's self.speedy to start at 0, otherwise it will move directly after being created. When you call the player.shoot() method you'll set self.speedy = -3 which will start the ball to move.
After it has been launched you just create a new ball on the paddle and repeat the same process.
import pygame
pygame.init()
is_running = True # Use lowercase_and_underscore for variable names.
WIDTH = 800
HEIGHT = 600
FPS = 60
clock = pygame.time.Clock()
window = pygame.display.set_mode((WIDTH, HEIGHT))
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((60, 30))
self.image.fill((200, 255, 200))
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 30
self.current_ball = Ball(self.rect.centerx, self.rect.top)
all_sprites.add(self.current_ball)
balls.add(self.current_ball)
def update(self):
speedx = 0 # Don't define attributes outside __init__. Local variable works in this case instead.
keystate = pygame.key.get_pressed()
if keystate[pygame.K_a]:
speedx = -5
elif keystate[pygame.K_d]: # Use elif so it doesn't need to check this condition if the above is true.
speedx = 5
self.rect.x += speedx
if self.rect.right + speedx > WIDTH:
self.rect.right = WIDTH
elif self.rect.left + speedx < 0: # Use elif so it doesn't need to check this condition if the above is true.
self.rect.left = 0
self.current_ball.rect.midbottom = self.rect.midtop # Set the ball position relative to paddle position.
def shoot(self):
self.current_ball.speedy = -3 # The ball should start moving.
self.current_ball = Ball(self.rect.centerx, self.rect.top) # Create a new ball on the paddle.
all_sprites.add(self.current_ball)
balls.add(self.current_ball)
class Ball(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 10))
self.image.fill((100, 150, 200))
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = 0 # The ball don't move from the beginning.
def update(self):
if self.rect.bottom < 0: # Chack if the ball has exit above the screen.
self.kill() # If it has it should delete itself.
self.rect.y += self.speedy
all_sprites = pygame.sprite.Group()
balls = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
while is_running:
clock.tick(FPS)
print(all_sprites)
for event in pygame.event.get():
if event.type == pygame.QUIT:
is_running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.shoot()
window.fill((0, 0, 30))
all_sprites.update()
all_sprites.draw(window)
pygame.display.flip()
pygame.quit()
quit()

How do I make enemies drop money after they die?

I'm looking to make some enemies in my game drop "UP Points" (Upgrade Points in the form of small yellow squares) when I shoot them. I've tried a few different things but can't seem to figure out how to spawn these collectable points in place of where an enemy just died. Does anyone have any ideas about how I could implement this?
UP class:
class Up(pygame.sprite.Sprite):
def __init__(self, color):
super().__init__()
self.image = pygame.Surface([5, 5])
self.image.fill(color)
self.rect = self.image.get_rect()
Here's the loop for when an enemy gets shot and dies:
for bullet in bullet_list: #For each bullet:
# Whenever a bullet collides with a zombie,
block_hit_list = pygame.sprite.spritecollide(bullet, zombie_list, True)
for i in block_hit_list:
# Destroy the bullet and zombie and add to the score
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 100
Sorry for not posting my whole code, the main game loop is at the bottom :)
import pygame
import math
import random
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
ORANGE = (255, 119, 0)
ZOMBIE_GREEN = (122, 172, 34)
YELLOW = (255, 255, 0)
cursor_x = 100
cursor_y = 100
class Player(pygame.sprite.Sprite):
def __init__(self, color):
super().__init__()
# pygame.Surface will create a rectangle with the width and height given
# and the command below it tells it to fill it in with that color
self.image = pygame.Surface([15, 15])
self.image.fill(color)
self.rect = self.image.get_rect()
# This defines the starting position (x, y)
# of whatever sprite is passed through
self.rect.x = 600
self.rect.y = 300
# This is the current speed it will move when drawn
self.change_x = 0
self.change_y = 0
self.walls = None
# Defines how the player will move
def movement(self, x, y):
self.change_x += x
self.change_y += y
# Updates the information so the screen shows the player moving
def update(self):
self.rect.x += self.change_x
block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_hit_list:
if self.change_x > 0:
self.rect.right = block.rect.left
else:
self.rect.left = block.rect.right
self.rect.y += self.change_y
block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_hit_list:
if self.change_y > 0:
self.rect.bottom = block.rect.top
else:
self.rect.top = block.rect.bottom
class Enemy(pygame.sprite.Sprite):
def __init__(self, color):
super().__init__()
self.image = pygame.Surface([20, 20])
self.image.fill(color)
self.rect = self.image.get_rect()
self.pos_x = self.rect.x = random.randrange(35, screen_width - 35)
self.pos_y = self.rect.y = random.randrange(35, screen_height - 135)
# How Zombies move towards player
def update(self):
zombie_vec_x = self.rect.x - player.rect.x
zombie_vec_y = self.rect.y - player.rect.y
vec_length = math.sqrt(zombie_vec_x ** 2 + zombie_vec_y ** 2)
if self.rect.x != player.rect.x and self.rect.y != player.rect.y:
zombie_vec_x = (zombie_vec_x / vec_length) * 1 # These numbers determine
zombie_vec_y = (zombie_vec_y / vec_length) * 1 # zombie movement speed
self.pos_x -= zombie_vec_x
self.pos_y -= zombie_vec_y
self.rect.x = self.pos_x
self.rect.y = self.pos_y
block_hit_list = pygame.sprite.spritecollide(self, sprites_list, False)
for block in block_hit_list:
if self.rect.x > 0:
self.rect.right = block.rect.left
elif self.rect.x < 0:
self.rect.left = block.rect.right
elif self.rect.y > 0:
self.rect.bottom = block.rect.top
else:
self.rect.top = block.rect.bottom
class Wall(pygame.sprite.Sprite):
def __init__(self, color, x, y, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class Cursor(pygame.sprite.Sprite):
def __init__(self, width, height):
self.groups = all_sprites_list
self._layer = 1
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(RED)
self.rect = self.image.get_rect()
self.walls = None
# This updates the cursor to move along with your
# mouse position (defined in control logic)
def update(self):
self.rect.x = cursor_x
self.rect.y = cursor_y
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface([8, 8])
self.image.fill(ORANGE)
self.rect = self.image.get_rect()
# Instead of using the rect. positions, we'll use pos_ variables
# to calculate position. This is because the rect. uses integers
# while a variable can have exact float numbers. This will keep
# the bullets trajectory exact istead of useing a general
# (rounded) whole number <3
self.pos_x = player.rect.x + 4 # Set up pos_x and pos_y here
self.pos_y = player.rect.y + 4 # rather than rect.x and rect.y
self.walls = None
self.change_x = 0
self.change_y = 0
speed = 6
bullet_vec_x = (cursor.rect.x - 4) - player.rect.x
bullet_vec_y = (cursor.rect.y - 4) - player.rect.y
vec_length = math.sqrt(bullet_vec_x ** 2 + bullet_vec_y ** 2)
bullet_vec_x = (bullet_vec_x / vec_length) * speed
bullet_vec_y = (bullet_vec_y / vec_length) * speed
self.change_x += bullet_vec_x
self.change_y += bullet_vec_y
def update(self):
self.pos_x += self.change_x # Update pos_x and pos_y. They will become floats
self.pos_y += self.change_y # which will let them maintain sub-pixel accuracy.
self.rect.x = self.pos_x # Copy the pos values into the rect, where they will be
self.rect.y = self.pos_y # rounded off. That's OK since we never read them back.
pygame.init()
screen_size = pygame.display.Info()
size = (1300, 720)
screen = pygame.display.set_mode(size)
#size = (screen_size.current_w, screen_size.current_h)
#screen = pygame.display.set_mode(
# ((screen_size.current_w, screen_size.current_h)),pygame.FULLSCREEN
# )
screen_width = screen_size.current_w
screen_height = screen_size.current_h
pygame.display.set_caption("Zombie Shooter")
wall_list = pygame.sprite.Group()
zombie1_list = pygame.sprite.Group()
sprites_list = pygame.sprite.Group()
bullet_list = pygame.sprite.Group()
all_sprites_list = pygame.sprite.Group()
# Walls are made here = (x_coord for where it starts,
# y_coord for where it starts, width of wall, height of wall)
# These walls are made with fullscreen dimentions, not any set dimentions
# Left
wall = Wall(BLUE, 0, 0, 10, screen_height)
wall_list.add(wall)
all_sprites_list.add(wall)
# Top
wall = Wall(BLUE, 0, 0, screen_width, 10)
wall_list.add(wall)
all_sprites_list.add(wall)
# Bottom
wall = Wall(BLUE, 0, screen_height - 10, screen_width, 10)
wall_list.add(wall)
all_sprites_list.add(wall)
# Right
wall = Wall(BLUE, screen_width - 10, 0, 10, screen_width)
wall_list.add(wall)
all_sprites_list.add(wall)
# HUD Border
wall = Wall(BLUE, 0, screen_height - 100, screen_width, 10)
wall_list.add(wall)
all_sprites_list.add(wall)
# This creates the actual player with the parameters set in ( ).
# However, we must add the player to the all_sprites_list
# so that it will actually be drawn to the screen with the draw command
# placed right after the screen.fill(BLACK) command.
player = Player(WHITE)
player.walls = wall_list
all_sprites_list.add(player)
zombie = Enemy(ZOMBIE_GREEN)
zombie.walls = wall_list
for i in range(5):
zombie = Enemy(ZOMBIE_GREEN)
all_sprites_list.add(zombie)
zombie1_list.add(zombie)
sprites_list.add(zombie)
cursor = Cursor(7, 7)
cursor.walls = wall_list
all_sprites_list.add(cursor)
bullet = Bullet()
font = pygame.font.SysFont("crushed", 30)
score = 0
up_score = 0
done = False
clock = pygame.time.Clock()
pygame.mouse.set_visible(0)
# -------- Main Program Loop -----------
while not done:
# --- Main event loop ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Press 'P' to quit the game_____
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
done = True
#________________________________
# Keyboard controls. The numbers inside change the speed of the player
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
player.movement(-4, 0)
elif event.key == pygame.K_d:
player.movement(4, 0)
elif event.key == pygame.K_w:
player.movement(0, -4)
elif event.key == pygame.K_s:
player.movement(0, 4)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.movement(4, 0)
elif event.key == pygame.K_d:
player.movement(-4, 0)
elif event.key == pygame.K_w:
player.movement(0, 4)
elif event.key == pygame.K_s:
player.movement(0, -4)
# ___________________________________________________________________
# Mouse Controls----------------------------
pos = pygame.mouse.get_pos()
cursor_x = pos[0]
cursor_y = pos[1]
if cursor_x <= 10:
cursor_x = 10
if cursor_x >= (screen_width - 17):
cursor_x = (screen_width - 17)
if cursor_y <= 10:
cursor_y = 10
if cursor_y >= (screen_height - 107):
cursor_y = (screen_height - 107)
elif event.type == pygame.MOUSEBUTTONDOWN:
bullet = Bullet()
all_sprites_list.add(bullet)
bullet_list.add(bullet)
#--------------------------------------------
all_sprites_list.update()
# How bullets vanish when they hit a sprite or a wall______________________
for bullet in bullet_list: #For each bullet:
# Whenever a bullet collides with a zombie,
block_hit_list = pygame.sprite.spritecollide(bullet, zombie1_list, True)
for i in block_hit_list:
# Destroy the bullet and zombie and add to the score
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 100
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, wall_list, False)
for i in block_hit_list:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
#--------------------------------------------------------------------------
cursor.update()
bullet_list.update()
sprites_list.update()
pygame.mouse.set_visible(0)
screen.fill(BLACK)
all_sprites_list.draw(screen)
text = font.render("Score: " + str(score), True, WHITE)
screen.blit(text, [30, screen_height - 64])
pygame.display.flip()
clock.tick(60)
pygame.quit()
What you will have to do is before you destroy the zombie sprite grab its location and draw the UP coin. Then make sure that whenever the player controlled sprite, in this case the bullet, upon contact "collects" the coin. I haven't worked with python that much but the main code would look something like this:
def drawCoin():
zombieCoords = grabZombieCoords()
drawSprite(zombieCoords())
This would essentially just get the coordinates of the zombie, destroy it, and then place the coin at the last known location of the zombie.
Hope this helps.
Change your Up class to accept an argument pos, and use it to set the starting position:
class Up(pygame.sprite.Sprite):
def __init__(self, color, pos):
super().__init__()
self.image = pygame.Surface([5, 5])
self.image.fill(color)
self.rect = self.image.get_rect(center=pos)
Now, when you hit a zombie, create an Up, using the position of the zombie you just killed.
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, zombie_list, True)
for i in block_hit_list:
# just use .kill() to remove a Sprite from all of its Groups
# kill() may get called multiple times, but that does not hurt
bullet.kill()
score += 100
# create an Up for each killed zombie
# takes two arguments: color and pos
# we call .add() to add the Sprite immediately to
# the all_sprites_list and the up_list
Up((255, 0, 0), i.rect.center).add(all_sprites_list, up_list)
You didn't show the rest of your code, but I guess you call .draw and .update on your all_sprites_list and create a Group called up_list.

PyGame collision working but sprite being reset - motion not continuing.

Having trouble with my PyGame experimental game - I'm learning how to work with sprites.
I have been trying to code 'collision' detection between sprites (ball and paddle) and have managed to get the collision detection working but my ball sprite seems to reset its position instead of carrying on. Could anyone take a look and see where my error is?
Here is my code:
import pygame
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
#variables, constants, functions
x = 1
y = 1
x_vel = 10
y_vel = 10
bat_x = 1
bat_y = 1
bat_x_vel = 0
bat_y_vel = 0
score = 0
class Ball(pygame.sprite.Sprite):
"""
This class represents the ball.
It derives from the "Sprite" class in Pygame.
"""
def __init__(self, width, height):
""" Constructor. Pass in the color of the block,
and its x and y position. """
# Call the parent class (Sprite) constructor
super().__init__()
# Set the background color and set it to be transparent
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
# Draw the ellipse
pygame.draw.ellipse(self.image, (255,0,0), [0,0,width,height], 10)
# Fetch the rectangle object that has the dimensions of the image
# image.
# Update the position of this object by setting the values
# of rect.x and rect.y
self.rect = self.image.get_rect()
# Instance variables that control the edges of where we bounce
self.left_boundary = 0
self.right_boundary = 0
self.top_boundary = 0
self.bottom_boundary = 0
# Instance variables for our current speed and direction
self.vel_x = 5
self.vel_y = 5
def update(self):
""" Called each frame. """
self.rect.x += self.vel_x
self.rect.y += self.vel_y
if self.rect.right >= self.right_boundary or self.rect.left <= self.left_boundary:
self.vel_x *= -1
if self.rect.bottom >= self.bottom_boundary or self.rect.top <= self.top_boundary:
self.vel_y *= -1
class Paddle(pygame.sprite.Sprite):
"""
This class represents the ball.
It derives from the "Sprite" class in Pygame.
"""
def __init__(self, width, height):
""" Constructor. Pass in the color of the block,
and its x and y position. """
# Call the parent class (Sprite) constructor
super().__init__()
# Set the background color and set it to be transparent
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
# Draw the rectangle
pygame.draw.rect(self.image, (0, 255, 0), [0, 0, width, height], 0)
# Fetch the rectangle object that has the dimensions of the image
# image.
# Update the position of this object by setting the values
# of rect.x and rect.y
self.rect = self.image.get_rect()
# Instance variables for our current speed and direction
self.x_vel = 0
self.y_vel = 0
def update(self):
# Get the current mouse position. This returns the position
# as a list of two numbers.
self.rect.x = self.rect.x + self.x_vel
self.rect.y = self.rect.y + self.y_vel
#initialise ball and paddle
paddle = Paddle(20, 100)
ball = Ball(100,100)
# This is a list of every sprite.
# All blocks and the player block as well.
all_sprites_list = pygame.sprite.Group()
all_sprites_list.add(ball)
all_sprites_list.add(paddle)
ball_sprites_list = pygame.sprite.Group()
ball_sprites_list.add(ball)
# Initialize Pygame
pygame.init()
# Set the height and width of the screen
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode((screen_width, screen_height))
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# Loop until the user clicks the close button.
done = False
# -------- Main Program Loop -----------
while not done:
# --- Events code goes here (mouse clicks, key hits etc)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
paddle.y_vel = -3
if event.key == pygame.K_DOWN:
paddle.y_vel = 3
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
paddle.y_vel = 0
if event.key == pygame.K_DOWN:
paddle.y_vel = 0
# --- Game logic should go here
# Calls update() method on every sprite in the list
all_sprites_list.update()
# collision check
ball_hit_list = pygame.sprite.spritecollide(paddle, ball_sprites_list, False)
# Check the list of collisions.
for ball in ball_hit_list:
score +=1
print(score)
# --- Clear the screen
screen.fill((255,255,255))
# --- Draw all the objects
all_sprites_list.draw(screen)
# render text
myfont = pygame.font.SysFont("monospace", 15)
label = myfont.render(str(score), 1, (0,0,0))
screen.blit(label, (100, 100))
# --- Update the screen with what we've drawn.
pygame.display.flip()
# --- Limit to 60 frames per second
clock.tick(60)
pygame.quit()
Sorry,
Have found the error.
Didn't set the boundaries of the window properly.
# Instance variables that control the edges of where we bounce
self.left_boundary = 0
self.right_boundary = 700
self.top_boundary = 0
self.bottom_boundary = 400

Categories