Randomly spawning an enemy - python

I am in the progress of making a small 2d game where the objective is to eat as much poop as possible, but I'm having trouble spawning the poop at random times. I want the poop to spawn at the enemy's y location but then shoot forwards like an rpg.
import pygame
from pygame.locals import *
from numpy.random import rand
pygame.init()
pygame.display.set_caption('STINKY BEETLE')
screen_width = 800
screen_height = 600
game_running = True
pl_x = int(screen_width/10)
pl_y = int(screen_height/2)
pl_width = 80
pl_height = 40
pl_vel = 30
en_width = 80
en_height = 40
en_x = screen_width - screen_width/10 - en_width
en_y = int(screen_height/2)
en_yvel = -10
po_width = 50
po_height = 30
po_x = 720
po_y = en_y
po_xvel = 15
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
while game_running:
clock.tick(10)
po_delay = rand(1)
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_running = False
if event.type == MOUSEBUTTONDOWN:
if event.button == 4 and pl_y > pl_vel:
pl_y -= pl_vel
elif event.button == 5 and pl_y < screen_height - pl_width:
pl_y += pl_vel
if po_delay < 0.01:
poop(po_x, po_y)
en_y += en_yvel
if en_y <= 0 or en_y >= screen_height - en_height:
en_yvel =- en_yvel
screen.fill((0, 0, 0))
pygame.draw.rect(screen, (105, 255, 125), (pl_x, pl_y, pl_width, pl_height))
pygame.display.update()
pygame.draw.rect(screen, (255, 125, 115), (en_x, en_y, en_width, en_height))
pygame.display.update()
pygame.quit()

If you want to mange multiple "poops", then you have to create a list. And each "poop" is an object (an instance of a class).
Crate a class Poop, which can update the position and draw the poop:
class Poop:
def __init__(self, x, y, w, h):
self.rect = pygame.Rect(x, y-h//2, w, h)
self.vel = -15
def update(self):
self.rect.x += self.vel
def draw(self, surf):
pygame.draw.rect(surf, (255, 200, 125), self.rect)
poops = []
Use a timer event to spawn the poops. Use pygame.time.set_timer() to repeatedly create an USEREVENT. The time is set in milliseconds. Set a random time by random.randint(a, b) e.g. set a time between 0.5 and 4 seconds (of course you can choose your own time interval):
min_time, max_time = 500, 4000 # 0.5 seconds to to 4 seconds
spawn_event = pygame.USEREVENT + 1
pygame.time.set_timer(spawn_event, random.randint(min_time, max_time))
Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to start at pygame.USEREVENT. In this case pygame.USEREVENT+1 is the event id for the timer event, which spawns the poops.
Create a new poop when the event occurs in the event loop and set a new random time:
for event in pygame.event.get():
# [...]
if event.type == spawn_event:
pygame.time.set_timer(spawn_event, random.randint(min_time, max_time))
poops.append(Poop(en_x, en_y+en_yvel+en_height//2, 50, 30))
Change the positions of the poops in a loop and remove them from the list, if they leave the window on the left:
for poop in poops[:]:
poop.update()
if poop.rect.right <= 0:
poops.remove(poop)
Draw them ina loop
for poop in poops:
poop.draw(screen)
See the example:
import pygame
from pygame.locals import *
import random
pygame.init()
pygame.display.set_caption('STINKY BEETLE')
class Poop:
def __init__(self, x, y, w, h):
self.rect = pygame.Rect(x, y-h//2, w, h)
self.vel = -15
def update(self):
self.rect.x += self.vel
def draw(self, surf):
pygame.draw.rect(surf, (255, 200, 125), self.rect)
screen_width = 800
screen_height = 600
game_running = True
pl_x, pl_y = screen_width//10, screen_height//2
pl_width, pl_height, pl_vel = 80, 40, 30
en_width, en_height, en_yvel = 80, 40, -10
en_x, en_y, = screen_width - screen_width//10 - en_width, screen_height//2
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
min_time, max_time = 500, 4000 # 0.5 seconds up to 4 seconds
spawn_event = pygame.USEREVENT + 1
pygame.time.set_timer(spawn_event, random.randint(min_time, max_time))
poops = []
while game_running:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_running = False
if event.type == MOUSEBUTTONDOWN:
if event.button == 4 and pl_y > pl_vel:
pl_y -= pl_vel
elif event.button == 5 and pl_y < screen_height - pl_width:
pl_y += pl_vel
if event.type == spawn_event:
pygame.time.set_timer(spawn_event, random.randint(min_time, max_time))
poops.append(Poop(en_x, en_y+en_yvel+en_height//2, 50, 30))
en_y += en_yvel
if en_y <= 0 or en_y >= screen_height - en_height:
en_yvel =- en_yvel
for poop in poops[:]:
poop.update()
if poop.rect.right <= 0:
poops.remove(poop)
screen.fill((0, 0, 0))
for poop in poops:
poop.draw(screen)
pygame.draw.rect(screen, (105, 255, 125), (pl_x, pl_y, pl_width, pl_height))
pygame.draw.rect(screen, (255, 125, 115), (en_x, en_y, en_width, en_height))
pygame.display.update()
pygame.quit()

You can use pygame's time module to randomly spawn enemies. I am assuming you are using OOP for this. Firstly, when initializing your enemy class record the time when it first spawns.
class Enemy:
def __init__(self):
self.start = time.time()
# other code
Then you can calculate the amount of time that has passed since your enemy last spawened. You can do something like now = time.time() in your main game loop and get the difference.
enemy = Enemy()
while True:
now = time.time()
time_passed = now - enemy.start()
Now you can use this time_passed as an argument to your spawn_enemy() function that you might create which might look something like this:
def spawn(self, t):
counter = t % random.randint(1, 10)
if counter >= 0 and counter <= 0.2:
#spawn enemy
Call this function as spawn(time_passed)

Related

Issues spawning multiple objects pygame [duplicate]

This question already has an answer here:
Spawning multiple instances of the same object concurrently in python
(1 answer)
Closed 4 months ago.
I'm very new to programing and I decided to try and make a very simple game to practice using pygame.
It's just a game where you're a rectangle and have to avoid geting hit by other rectangles(enemies).
However I'm having issues spawning in the enemies, something I did makes it so when I draw them to the screen they will draw on the same spot making it look like I have one very fast moving enemy rather than multiple of them.
I used a sprite for the enemy just to learn how to do sprites, it's just a red square and I'm aware that I could have used the drawRect function.
I have tried to look for answers on this and found that apparently the best to do this is with classes but I can't seem to make that work either.
Here is the code, I'm very sorry for the format of this post it's my first time posting here and I'm still not sure how to do this
Please let me know if I posted this in the wrong place, again I don't know how this works.
from textwrap import fill
import pygame
import os
import random
pygame.font.init()
text = "GAMEOVER!"
WIDTH, HEIGHT = 450, 450
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Rando game")
HEALTH_FONT = pygame.font.SysFont('comicsans', 40)
FPS = 60
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
PURPLE = (255, 0, 255)
ENEMY_VEL = 5
PLAYER_VEL = 10
HIT = pygame.USEREVENT + 1
class enemy:
enemy_x = random.randint(0, WIDTH)
enemy_y = - 30
ENEMY_SPRITE = pygame.transform.scale(pygame.image.load(
'RED.png'), (30, 30))
#DRAW STUFF
def draw_window(enemy, player, health, enemies):
WIN.fill(WHITE)
#health
health_text = HEALTH_FONT.render(str(health), 1, PURPLE)
WIN.blit(health_text,(0, 0))
#enemies
for enemy in enemies:
WIN.blit(ENEMY_SPRITE, (enemy.enemy_x, enemy.enemy_y))
#player
pygame.draw.rect(WIN, BLUE, player)
pygame.display.update()
def handle_player_movement(player, keys_pressed):
if keys_pressed[pygame.K_d] and player.x + PLAYER_VEL <= WIDTH - 25:
player.x += PLAYER_VEL
if keys_pressed[pygame.K_a] and player.x - PLAYER_VEL >= 0:
player.x -= PLAYER_VEL
def enemy_movement(enemy, enemies):
for enemy in enemies:
enemy.enemy_y += ENEMY_VEL
def handle_collision(player, enemy, enemies):
for enemy in enemies:
if player.x + 25 > enemy.enemy_x and player.x <= enemy.enemy_x + 25 and player.y == enemy.enemy_y + 25:
enemies.remove(enemy)
pygame.event.post(pygame.event.Event(HIT))
enemies.append(enemy)
enemy.enemy_y = 0
enemy.enemy_x = random.randint(0, WIDTH - 30)
elif enemy.enemy_y > HEIGHT:
enemies.remove(enemy)
enemies.append(enemy)
enemy.enemy_y = 0
enemy.enemy_x = random.randint(0, WIDTH - 30)
def main():
clock = pygame.time.Clock() #locks fps
run = True
player = pygame.Rect(0, 425, 25, 25)
health = 3
while run:
clock.tick(FPS)
enemies = []
for n in range(6):
enemies.append(enemy)
for event in pygame.event.get():
if event.type == pygame.QUIT: #quits
run = False
pygame.quit()
if event.type == HIT:
health -= 1
if health <= 0:
enemies.remove(enemy)
draw_text = HEALTH_FONT.render(text, 1, PURPLE)
WIN.blit(draw_text, (WIDTH / 2 - draw_text.get_width() / 2, HEIGHT / 2 - draw_text.get_height() / 2))
pygame.display.update()
pygame.time.delay(2000)
break
print(enemies)
keys_pressed = pygame.key.get_pressed()
enemy_movement(enemy, enemies)
handle_player_movement(player, keys_pressed)
handle_collision(player, enemy, enemies)
draw_window(enemy, player, health, enemies)
main()
if __name__ == '__main__':
main()
My recommendation is to learn classes. They are absolutely a life saver in game programming.
Some info about classes from w3schools
All classes have a function called init(), which is always
executed when the class is being initiated.
Use the init() function to assign values to object properties, or
other operations that are necessary to do when the object is being
created
In your posted example, all six enemies were spawning in the exact same spot each time.
I've updated your code example so that multiple enemies spawn in different location.
from textwrap import fill
import pygame
import os
import random
pygame.font.init()
text = "GAMEOVER!"
WIDTH, HEIGHT = 450, 450
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Rando game")
HEALTH_FONT = pygame.font.SysFont('comicsans', 40)
FPS = 60
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
PURPLE = (255, 0, 255)
ENEMY_VEL = 5
PLAYER_VEL = 10
HIT = pygame.USEREVENT + 1
ENEMY_SPRITE = pygame.transform.scale(pygame.image.load(
'RED.png'), (30, 30))
# Enemy class that stores a single enemy
class Enemy:
def __init__(self):
# Each time you call Enemy(), a new enemy is created with a random x value
self.enemy_x = random.randint(0, WIDTH)
self.enemy_y = -30
#DRAW STUFF
def draw_window(player, health, enemies):
WIN.fill(WHITE)
#health
health_text = HEALTH_FONT.render(str(health), 1, PURPLE)
WIN.blit(health_text,(0, 0))
#enemies
for enemy in enemies:
WIN.blit(ENEMY_SPRITE, (enemy.enemy_x, enemy.enemy_y))
#player
pygame.draw.rect(WIN, BLUE, player)
pygame.display.update()
def handle_player_movement(player, keys_pressed):
if keys_pressed[pygame.K_d] and player.x + PLAYER_VEL <= WIDTH - 25:
player.x += PLAYER_VEL
if keys_pressed[pygame.K_a] and player.x - PLAYER_VEL >= 0:
player.x -= PLAYER_VEL
def enemy_movement(enemies):
for enemy in enemies:
enemy.enemy_y += ENEMY_VEL
def handle_collision(player, enemies):
for enemy in enemies:
if player.x + 25 > enemy.enemy_x and player.x <= enemy.enemy_x + 25 and player.y == enemy.enemy_y + 25:
enemies.remove(enemy)
pygame.event.post(pygame.event.Event(HIT))
enemies.append(enemy)
enemy.enemy_y = 0
enemy.enemy_x = random.randint(0, WIDTH - 30)
elif enemy.enemy_y > HEIGHT:
enemies.remove(enemy)
enemies.append(enemy)
enemy.enemy_y = 0
enemy.enemy_x = random.randint(0, WIDTH - 30)
def main():
clock = pygame.time.Clock() #locks fps
run = True
player = pygame.Rect(0, 425, 25, 25)
health = 3
enemies = []
for _ in range(6):
enemies.append(Enemy()) # Instantiates 6 new enemies with different x values
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT: #quits
run = False
pygame.quit()
if event.type == HIT:
health -= 1
if health <= 0:
enemies = [] # Remove all enemies
draw_window(player, health, enemies) # Update the window
draw_text = HEALTH_FONT.render(text, 1, PURPLE)
WIN.blit(draw_text, (WIDTH / 2 - draw_text.get_width() / 2, HEIGHT / 2 - draw_text.get_height() / 2))
pygame.display.update()
pygame.time.delay(2000)
break
keys_pressed = pygame.key.get_pressed()
enemy_movement(enemies)
handle_player_movement(player, keys_pressed)
handle_collision(player, enemies)
draw_window(player, health, enemies)
if __name__ == '__main__':
main()

How to add gravity to pygame?

I've tried to add gravity using the quadratic formula but didn't seem to work and I tried to turn it into a function and that still didn't seem to work. I tried using a class but I don't quite understand how they work so can someone help me add gravity to my platformer. I've tried many videos but didn't seem to help so any help would be much appreciated
import pygame
from pygame.locals import *
from pygame import mixer
from typing import Tuple
import pickle
from os import path
import time
import sys
import os
pygame.init()
white = 255,255,255
black = 0,0,0
green = 0, 255, 0
blue = 0,0,255
font = pygame.font.SysFont('Comic Sans MS', 50, 70)
screen_width = 650
screen_height = 800
screen = pygame.display.set_mode([screen_width,screen_height])
screen.fill((white))
pygame.display.set_caption("Darsh's Game")
pygame.display.update()
pygame.draw.rect(screen,white,(200,150,100,50))
running = 1
speed = 1
x = 25
y = 669
isJump = False
jumpCount = 10
left = False
right = True
def draw_text(text, font, text_col, x, y):
img = font.render(text, True, text_col)
screen.blit(img, (x, y))
def draw_rect():
rect1 = pygame.Rect(0, 0, 25, 800)
pygame.draw.rect(screen, black, rect1)
rect2 = pygame.Rect(625, 0, 25, 800)
pygame.draw.rect(screen, black, rect2)
rect3 = pygame.Rect(0, 775, 650, 25)
pygame.draw.rect(screen, green, rect3)
pygame.display.flip()
def direction(left, right):
if left == True:
screen.blit(mainleft_img, (x, y))
elif right == True:
screen.blit(mainright_img, (x, y))
draw_rect()
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
mainright_img = pygame.image.load('newgameimg/mainright.png')
mainleft_img = pygame.image.load('newgameimg/mainleft.png')
screen.blit(mainright_img, (300, 400))
run = True
while run:
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_a] and x > speed - 16:
x -= speed
left = True
right = False
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
if keys[pygame.K_d] and x < screen_width - speed - 89:
x += speed
left = False
right = True
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
if keys[pygame.K_ESCAPE]:
pygame.quit()
if not (isJump):
if keys[pygame.K_SPACE]: # jumping code
isJump = True
else:
if jumpCount >= -10:
time.sleep(0.02)
y -= (jumpCount * abs(jumpCount)) * 0.5
jumpCount -= 1
else:
jumpCount = 10
isJump = False # jumping code
screen.fill(white)
direction(left, right)
draw_text("George is dumb", font, green, 100, 400)
draw_rect()
pygame.display.update()
You don't need the quadratic formula to get a simple gravity going; a simple gravity variable increasing every frame should do the trick.
...
gravity = 0
while run:
...
...
if keys[pygame.K_SPACE]:
gravity += 1
y -= gravity
...
Note that I don't speak your language; I mainly code in JavaScript, but I did try python once and I got the syntax down pretty quick. Hopefully this suits your needs, with a bit of tweaking of course.
Add a variable fall = 0. Increment the variable if the player does not jump (fall += 0.2). Change the y coordinate of the player through the variable fall in each frame. Get the bounding rectangle of the player with pygame.Surface.get_rect(). If the bottom of the rectangle reaches the floor then stop falling, and restricting the bottom of the rectangle on the floor. Set fall = 0 if the player starts to jump:
while run:
# [...]
if not (isJump):
fall += 0.2
y += fall
player_rect = mainleft_img.get_rect(topleft = (x, y))
if player_rect.bottom > 775:
player_rect.bottom = 775
y = player_rect.top
fall = 0
if keys[pygame.K_SPACE]: # jumping code
isJump = True
fall = 0
else:
# [...]
Multiple calls to pygame.display.update() or pygame.display.flip() cause flickering. Remove all calls to pygame.display.update() from your code, but call it once at the end of the application loop.
In addition, draw each object only once.
Do not delay, wait or sleep. Use pygame.time.Clock to control the frames per second and thus the game speed.
The method tick() of a pygame.time.Clock object, delays the game in that way, that every iteration of the loop consumes the same period of time. See pygame.time.Clock.tick():
This method should be called once per frame.
That means that the loop:
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
runs 60 times per second.
See pygame.time.Clock.tick():
This method should be called once per frame. It will compute how many milliseconds have passed since the previous call.
Complete example:
import pygame
from pygame.locals import *
pygame.init()
white = 255,255,255
black = 0,0,0
green = 0, 255, 0
blue = 0,0,255
screen_width = 650
screen_height = 800
screen = pygame.display.set_mode([screen_width,screen_height])
pygame.display.set_caption("Darsh's Game")
font = pygame.font.SysFont('Comic Sans MS', 50, 70)
running = 6
speed = 2
fall = 0
x = 25
y = 669
isJump = False
jumpCount = 10
left = False
right = True
def draw_text(text, font, text_col, x, y):
img = font.render(text, True, text_col)
screen.blit(img, (x, y))
def draw_rect():
rect1 = pygame.Rect(0, 0, 25, 800)
pygame.draw.rect(screen, black, rect1)
rect2 = pygame.Rect(625, 0, 25, 800)
pygame.draw.rect(screen, black, rect2)
rect3 = pygame.Rect(0, 775, 650, 25)
pygame.draw.rect(screen, green, rect3)
def direction(left, right):
if left == True:
screen.blit(mainleft_img, (x, y))
elif right == True:
screen.blit(mainright_img, (x, y))
draw_text("George is dumb", font, green, 100, 400)
mainright_img = pygame.image.load('newgameimg/mainright.png')
mainleft_img = pygame.image.load('newgameimg/mainleft.png')
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_a] and x > speed - 16:
x -= speed
left = True
right = False
if keys[pygame.K_d] and x < screen_width - speed - 89:
x += speed
left = False
right = True
if keys[pygame.K_ESCAPE]:
pygame.quit()
if not (isJump):
fall += 0.2
y += fall
player_rect = mainleft_img.get_rect(topleft = (x, y))
if player_rect.bottom > 775:
player_rect.bottom = 775
y = player_rect.top
fall = 0
if keys[pygame.K_SPACE]: # jumping code
isJump = True
fall = 0
else:
if jumpCount >= -10:
y -= (jumpCount * abs(jumpCount)) * 0.5
jumpCount -= 1
else:
jumpCount = 10
isJump = False # jumping code
screen.fill(white)
draw_rect()
direction(left, right)
pygame.display.update()

Why doesn't PyGame draw in the window before the delay or sleep?

I am working on a pong game. When either of the scores hit 10, it is supposed to put up some text on the screen and say the right player has won or left player has won. However, in my program, it isn't working. When it has to show the text that the right or left player has won, it doesn't show it. But it works for everything else. Here is the code:
# Importing libraries
import pygame
import random
import time
# Initializing PyGame
pygame.init()
# Setting a window name
pygame.display.set_caption("Ping Pong")
# Creating a font
pygame.font.init()
font = pygame.font.SysFont(None, 30)
pong_font = pygame.font.SysFont("comicsansms", 75)
# Set the height and width of the screen
window_width = 700
window_height = 500
size = [window_width, window_height]
game_win = pygame.display.set_mode(size)
game_win2 = pygame.display.set_mode(size)
# Creating a messaging system
def message(sentence, color, x, y, font_type, display):
sentence = font_type.render(sentence, True, color)
display.blit(sentence, [x, y])
# Creating colors
white = (225, 225, 225)
black = (0, 0, 0)
gray = (100, 100, 100)
# Setting up ball
ball_size = 25
class Ball:
"""
Class to keep track of a ball's location and vector.
"""
def __init__(self):
self.x = 0
self.y = 0
self.change_x = 0
self.change_y = 0
def make_ball():
ball = Ball()
# Starting position of the ball.
ball.x = 350
ball.y = 250
# Speed and direction of rectangle
ball.change_x = 5
ball.change_y = 5
return ball
def main():
# Scores
left_score = 0
right_score = 0
pygame.init()
# Loop until the user clicks the close button.
done = False
ball_list = []
ball = make_ball()
ball_list.append(ball)
# Right paddle coordinates
y = 200
y_change = 0
x = 50
# Left paddle coordinates
y1 = 200
y1_change = 0
x1 = 650
while not done:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
y_change = -7
elif event.key == pygame.K_s:
y_change = 7
elif event.key == pygame.K_UP:
y1_change = -7
elif event.key == pygame.K_DOWN:
y1_change = 7
elif event.type == pygame.KEYUP:
y_change = 0
y1_change = 0
y += y_change
y1 += y1_change
# Preventing from letting the paddle go off screen
if y > window_height - 100:
y -= 10
if y < 50:
y += 10
if y1 > window_height - 100:
y1 -= 10
if y1 < 50:
y1 += 10
# Logic
for ball in ball_list:
# Move the ball's center
ball.x += ball.change_x
ball.y += ball.change_y
# Bounce the ball if needed
if ball.y > 500 - ball_size or ball.y < ball_size:
ball.change_y *= -1
if ball.x > window_width - ball_size:
ball.change_x *= -1
left_score += 1
if ball.x < ball_size:
ball.change_x *= -1
right_score += 1
ball_rect = pygame.Rect(ball.x - ball_size, ball.y - ball_size, ball_size * 2, ball_size * 2)
left_paddle_rect = pygame.Rect(x, y, 25, 75)
if ball.change_x < 0 and ball_rect.colliderect(left_paddle_rect):
ball.change_x = abs(ball.change_x)
right_paddle_rect = pygame.Rect(x1, y1, 25, 75)
if ball.change_x > 0 and ball_rect.colliderect(right_paddle_rect):
ball.change_x = -abs(ball.change_x)
# Here is the where the messaging system doesn't work, I don't know why! It works fine for everything else
if right_score == 10:
message("RIGHT PLAYER HAS WON!!", white, 300, 200, font, game_win)
time.sleep(5)
pygame.quit()
quit()
elif left_score == 10:
message("LEFT PLAYER HAS WON!!", white, 300, 200, font, game_win)
time.sleep(5)
pygame.quit()
quit()
# Drawing
# Set the screen background
game_win.fill(black)
# Draw the balls
for ball in ball_list:
pygame.draw.circle(game_win, white, [ball.x, ball.y], ball_size)
# Creating Scoreboard
message("Left player score: " + str(left_score), white, 10, 10, font, game_win)
message("Right player score: " + str(right_score), white, 490, 10, font, game_win)
# Drawing a left paddle
pygame.draw.rect(game_win, white, [x, y, 25, 100])
# Drawing a right paddle
pygame.draw.rect(game_win, white, [x1, y1, 25, 100])
# Setting FPS
FPS = pygame.time.Clock()
FPS.tick(60)
# Updating so actions take place
pygame.display.flip()
while True:
game_win2.fill(black)
pygame.event.get()
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
message("Pong", white, 280, 100, pong_font, game_win2)
if 150 + 100 > mouse[0] > 150 and 350 + 50 > mouse[1] > 350:
pygame.draw.rect(game_win, gray, [150, 350, 100, 50])
if click[0] == 1:
break
else:
pygame.draw.rect(game_win, white, [150, 350, 100, 50])
if 450 + 100 > mouse[0] > 450 and 350 + 50 > mouse[1] > 350:
pygame.draw.rect(game_win, gray, [450, 350, 100, 50])
if click[0] == 1:
pygame.quit()
quit()
else:
pygame.draw.rect(game_win, white, [450, 350, 100, 50])
message("Start", black, 175, 367, font, game_win2)
message("Quit", black, 475, 367, font, game_win2)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Wrap-up
# Limit to 60 frames per second
clock = pygame.time.Clock()
clock.tick(60)
if __name__ == "__main__":
main()
I have added a little comment, it is: "# Here is the where the messaging system doesn't work, I don't know why! It works fine for everything else". Now when someone scores 10 points, Nothing happens. Its= wait for a couple of seconds. That is so you can read the "Left player has won" or "Right player has won" before the program closes. But it simply doesn't show up! I don't know why! Can someone help with this?
The display is updated only if either pygame.display.update() or pygame.display.flip()
is called. See pygame.display.flip():
This will update the contents of the entire display.
Further you've to handles the events with pygame.event.pump(), before the update of the display becomes visible in the window.
See pygame.event.pump():
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
If you want to display a text and delay the game, then you've to update the display and handle the events.
Write a function which delays the game and updates the display. I recommend to use the pygame.time module to implement the delay (e.g. pygame.time.delay())
def update_and_wait(delay):
pygame.display.flip()
pygame.event.pump()
pygame.time.delay(delay * 1000) # 1 second == 1000 milliseconds
Or even implement a function which its own event loop to keep the application responding. Measure the time by pygame.time.get_ticks():
def update_and_wait(delay):
start_time = pygame.time.get_ticks()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
print("auit")
pygame.quit()
return False
if pygame.time.get_ticks() >= start_time + delay * 1000:
break
return True
Use the function in the application:
def main():
# [...]
while not done:
# [...]
for ball in ball_list:
# [...]
if right_score == 0:
message_wait("RIGHT PLAYER HAS WON!!", white, 300, 200, font, game_win)
update_and_wait(5)
quit()
elif left_score == 0:
message_wait("LEFT PLAYER HAS WON!!", white, 300, 200, font, game_win)
update_and_wait(5)
quit()

How do I randomly spawn enemy elsewhere after collision in pygame?

for some reason when I collide with the enemy it randomly spawns elsewhere which is all fine but when I un-collide the enemy goes back to its original position
here is the full code down below. If you run this code see what happens. Im looking for the red block to spawn in a random other location when the blue box collides with it.
import pygame
import random
# places where enemies can spawn (2 to make it simple at first)
enemy_locations = [100, 200]
pygame.init()
# clock
clock = pygame.time.Clock()
# frames per second
fps = 30
# colors
background_color = (255, 255, 255)
player_color = (0, 0, 255)
enemy_color = (255, 0, 0)
# width and height of screen
width = 1000
height = 800
# screen
screen = pygame.display.set_mode((width, height))
# x, y coordinates player
player_x = 300
player_y = 300
# ememy x, y coordinates
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
# new x, y coordinates for enemy player
new_x = random.choice(enemy_locations)
new_y = random.choice(enemy_locations)
# draw player
def draw():
enemy_rect = pygame.Rect(enemy_x, enemy_y, 25, 25)
player_rect = pygame.Rect(player_x, player_y, 25, 25)
if player_rect.colliderect(enemy_rect):
enemy = pygame.draw.rect(screen, enemy_color, (new_x, new_y, 25, 25))
else:
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
player = pygame.draw.rect(screen, player_color, player_rect)
# pygame loop (always include)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# controls for player object
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player_x -= 10
if event.key == pygame.K_RIGHT:
player_x += 10
if event.key == pygame.K_UP:
player_y -= 10
if event.key == pygame.K_DOWN:
player_y += 10
draw()
pygame.display.update()
screen.fill(background_color)
clock.tick(fps)
The draw method explicitly states to draw enemy rect either in the initial point or in some ONE random point. The random point is selected only once - during the start of an application. You should set the random point every time there is a collision in a draw method, e.g.
if player_rect.colliderect(enemy_rect):
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
You have to create a new random enemy position when the player collides with the enemy
if player_rect.colliderect(enemy_rect):
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
Use the global statement, to interpreted the variables enemy_x and enemy_y as global. With the statement global it is possible to write to variables in the global namespace within a function:
global enemy_x, enemy_y
Function draw:
def draw():
global enemy_x, enemy_y
enemy_rect = pygame.Rect(enemy_x, enemy_y, 25, 25)
player_rect = pygame.Rect(player_x, player_y, 25, 25)
if player_rect.colliderect(enemy_rect):
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
player = pygame.draw.rect(screen, player_color, player_rect)
As others have said you either need to add more "locations" to the list or better, use random.randint(), and also enemy's coordinates does not "get random"(or updated in any way) after every collision, but only once when you initialize them.
I added new function get_new_coord() which will return the random number(I use these limits so the enemy won't go off the screen), which gets called once for every coordinate after each collision.
Also I moved player_rect and enemy_rect outside of the loop, and now draw() returns enemy_rect so it can stay updated if collision occurs.
This is a quick fix to your code, you should consider using Classes. And of course if you want to do so, I could help you with transforming the code.
import pygame
import random
# places where enemies can spawn (2 to make it simple at first)
enemy_locations = [100, 200]
pygame.init()
# clock
clock = pygame.time.Clock()
# frames per second
fps = 30
# colors
background_color = (255, 255, 255)
player_color = (0, 0, 255)
enemy_color = (255, 0, 0)
# width and height of screen
width = 1000
height = 800
# screen
screen = pygame.display.set_mode((width, height))
# x, y coordinates player
player_x = 300
player_y = 300
# ememy x, y coordinates
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
# new x, y coordinates for enemy player
new_x = random.choice(enemy_locations)
new_y = random.choice(enemy_locations)
def get_new_coord():
return random.randint(0, 800-25)
# draw player
def draw(player_rect, enemy_rect):
screen.fill(background_color)
if player_rect.colliderect(enemy_rect):
enemy_rect = pygame.Rect(get_new_coord(), get_new_coord(), 25, 25)
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
else:
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
player = pygame.draw.rect(screen, player_color, player_rect)
pygame.display.update()
return enemy_rect
# pygame loop (always include)
running = True
enemy_rect = pygame.Rect(enemy_x, enemy_y, 25, 25)
player_rect = pygame.Rect(player_x, player_y, 25, 25)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# controls for player object
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player_rect.x -= 10
if event.key == pygame.K_RIGHT:
player_rect.x += 10
if event.key == pygame.K_UP:
player_rect.y -= 10
if event.key == pygame.K_DOWN:
player_rect.y += 10
enemy_rect = draw(player_rect, enemy_rect)
clock.tick(fps)

One click activates multiple buttons if overlapping

Alright, so yes, I get that it is a lot of code I am about to display.
My problem: I am making a zombie shooter game where you are the main character on top of a building and you have to click on zombies as they come in waves. My current problem is whenever multiple zombies overlap on top of each other, I can kill both of them (or as many are overlapping) in one click because technically, all of their hitboxes are colliding.
If this is a bigger problem than sought out to be, I would like someone to just say that.
import pygame
import random
import time
pygame.init()
#Setting Variables
screenW = 1020
screenH = 630
x = 125
y = 164
width = 50
height = 50
velocity = 5
wave = 2
GOLD = (255, 215, 0)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 128, 0)
wallHealth = 0
zombieKilled = 0
class ZombieChars():
def __init__(self):
self.damage = 0
self.vel = 5
self.x_change = random.randrange(2,5)
self.y_change = 1
self.height = 120
self.width = 149
self.color = random.sample(range(250), 4)
self.image = pygame.Surface([self.width, self.height], pygame.HWSURFACE, 32)
self.rect = self.image.get_rect(topleft = (random.randrange(900, 1150), 330))
#pygame.draw.rect(self.image, (self.color), (self.x, self.y, self.width, self.height))
def draw(self):
window.blit(ZombieWalking, self.rect.topleft)
def update(self):
if self.rect.x >= 364:
self.rect.x -= self.x_change
else:
self.rect.x -= 0
def wallHP(self):
global wallHealth
if self.rect.x < 365:
self.damage += 1
if self.damage == 30:
self.damage = 0
wallHealth += 1
def death(self):
global zombieKilled
if event.type == pygame.MOUSEBUTTONDOWN:
gunShot.play()
mouse_pos = event.pos
if self.rect.collidepoint(mouse_pos):
self.rect.x = 5000
self.rect.x -= self.x_change
zombieHit.play()
zombieKilled += 1
print(zombieKilled)
def waveCounter(self):
global wave
print(wave)
if wave == zombieKilled / 2:
wave = 2
#FPS
clock = pygame.time.Clock()
clock.tick(60)
#Screen
window = pygame.display.set_mode((screenW,screenH))
pygame.display.set_caption(("Zombie Shooter"))
#Image Loading
bg = pygame.image.load("bg.jpg")
mainmenu = pygame.image.load("mainmenu.jpg")
ZombieWalking = pygame.image.load("Sprites/AAIdle.png")
#Sound Loading
gunShot = pygame.mixer.Sound('sounds/gunShot.wav')
zombieHit = pygame.mixer.Sound('sounds/zombieHit.wav')
gameMusic = pygame.mixer.music.load('sounds/gameMusic.mp3')
menuMusic = pygame.mixer.music.load('sounds/menuMusic.mp3')
zombies = ZombieChars()
my_list = []
for zombs in range(wave):
my_object = ZombieChars()
my_list.append(my_object)
def text_objects(text, font):
textSurface = font.render(text, True, BLACK)
return textSurface, textSurface.get_rect()
smallText = pygame.font.Font('freesansbold.ttf', 30)
tinyText = pygame.font.Font('freesansbold.ttf', 20)
TextSurf3, TextRect3 = text_objects("Wave: " + str(wave), smallText)
TextRect3.center = ((1020 / 2), (50))
#Main Loop
run = True
mainMenu = True
pygame.mixer.music.play()
global event
while mainMenu == True:
window.blit(mainmenu, (0,0))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False #if x is pressed dont run game
mainMenu = False
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
mainMenu = False #if a is pressed run game
def wallHPBar():
pygame.draw.rect(window, GREEN, (20, 20, 100, 10))
if wallHealth == 0:
pass
if wallHealth == 1:
pygame.draw.rect(window, RED, (20, 20, 25, 10))
if wallHealth == 2:
pygame.draw.rect(window, RED, (20, 20, 50, 10))
if wallHealth == 3:
pygame.draw.rect(window, RED, (20, 20, 75, 10))
if wallHealth >= 4:
pygame.draw.rect(window, RED, (20, 20, 100, 10))
def overlapKill():
if zombieKilled == 1:
print("oh my goodness we going")
if zombieKilled == 2:
print("we 2 ")
while run:
pygame.mixer.music.stop()
window.blit(bg, (0, 0))
window.blit(TextSurf3, TextRect3)
wallHPBar()
pygame.time.delay(25)
for zombie in my_list:
zombie.draw()
zombie.update()
zombie.death()
zombie.wallHP()
zombie.waveCounter()
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
Thank you.
Remove the event handling from the method death and return a boolean value, that indicates if a zombie was killed:
class ZombieChars():
# [...]
def death(self):
global zombieKilled
mouse_pos = pygame.mouse.get_pos()
if self.rect.collidepoint(mouse_pos):
self.rect.x = 5000
self.rect.x -= self.x_change
zombieHit.play()
zombieKilled += 1
print(zombieKilled)
return True
return False
Do the pygame.MOUSEBUTTONDOWN event handling in the event loop and evaluate if a zombie was killed in a loop. break the loop when a zombie is killed. Thus only one zombie can be get killed on one klick:
while run:
# [...]
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
gunShot.play()
for zombie in (reversed):
if zombie.death():
break
for zombie in my_list:
zombie.draw()
zombie.update()
# zombie.death() <--- DELETE
zombie.wallHP()
zombie.waveCounter()
A Zombie object should not be dealing with user-input. Handle the click outside of the zombie, then the outside code gets to decide if the click is "used up".
class ZombieChars():
[ ... ]
def death( self, mouse_position ):
killed = False
global zombieKilled
if self.rect.collidepoint( mouse_position ):
self.rect.x = 5000
self.rect.x -= self.x_change
zombieHit.play()
zombieKilled += 1
print(zombieKilled)
killed = True
return killed
Then in your main loop, stop processing hits once the first is found:
### Main Loop
while not exiting:
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
exiting = True
elif ( event.type == pygame.MOUSEBUTTONDOWN ):
gunShot.play()
mouse_pos = event.pos
for zombie in my_list:
if ( zombie.death( mouse_pos ) ):
break # stop on first hit

Categories