Issues spawning multiple objects pygame [duplicate] - python

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()

Related

CTRL not working when clicked to perform the code allocated to it

so the code was working but i dont know what happened, so if you click the Left CTRL or the right CTRL the red/yellow should fire a bullet but that's not happening . so i used print to check if the code would append something to the red/yellow_bullet lists but it didnt so is the problem from my side or is it a problem in the code???
please help
Code :
import pygame
import os
from pygame import event #opereting system
WIDTH, HEIGHT = 900, 500 #this is to make the WINDOW
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Maisa Ahmed")
DARK_GREEN = (00, 64, 00)
BLACK = (0, 0, 0)
BLOOD_RED = (136, 8, 8)
YELLOW = (255, 255, 0)
boarder = pygame.Rect(WIDTH//2-5, 0, 10, HEIGHT)
FPS = 60
SPACESHIP_WIDTH , SPACESHIP_HEIGHT = 55,40
velocity = 5
Bullet_velocity = 7
MAX_BULLETS = 5
YELLOW_HITS = pygame.USEREVENT + 1
RED_HITS = pygame.USEREVENT + 2
#importing images
YELLOW_SPACESHIP_IMAGE = pygame.image.load(
os.path.join('Assets', 'spaceship_yellow.png'))
YELLOW_SPACESHIP = pygame.transform.rotate(pygame.transform.scale(
YELLOW_SPACESHIP_IMAGE, (SPACESHIP_WIDTH, SPACESHIP_HEIGHT)), 90)
RED_SPACESHIP_IMAGE = pygame.image.load(
os.path.join('Assets', "spaceship_red.png"))
RED_SPACESHIP = pygame.transform.rotate(pygame.transform.scale(RED_SPACESHIP_IMAGE, (SPACESHIP_WIDTH, SPACESHIP_HEIGHT)),270)
def draw_window(red ,yellow, red_bullets, yellow_bullets):
WIN.fill(DARK_GREEN)
pygame.draw.rect(WIN, BLACK, boarder)
WIN.blit(YELLOW_SPACESHIP, (yellow.x, yellow.y))
WIN.blit(RED_SPACESHIP, (red.x, red.y))
for bullet in red_bullets:
pygame.draw.rect(WIN, BLOOD_RED, bullet)
for bullet in yellow_bullets:
pygame.draw.rect(WIN, YELLOW, bullet)
pygame.display.update()
def yellow_movement(keys_pressed, yellow):
if keys_pressed[pygame.K_a] and yellow.x - velocity > 0:
yellow.x -= velocity
if keys_pressed[pygame.K_d] and yellow.x + velocity + yellow.width < boarder.x:
yellow.x += velocity
if keys_pressed[pygame.K_w] and yellow.y - velocity > 0:
yellow.y -= velocity
if keys_pressed[pygame.K_s] and yellow.y + velocity + yellow.height < HEIGHT - 10:
yellow.y += velocity
def red_movement(keys_pressed, red):
if keys_pressed[pygame.K_LEFT] and red.x - velocity > boarder.x + boarder.width: # left
red.x -= velocity
if keys_pressed[pygame.K_RIGHT] and red.x + velocity + red.width < WIDTH: # right
red.x += velocity
if keys_pressed[pygame.K_UP] and red.y - velocity > 0:# up
red.y -= velocity
if keys_pressed[pygame.K_DOWN] and red.y + velocity + red.height < HEIGHT - 10: # down
red.y += velocity
def handle_bullets(yellow_bullets, red_bullets, yellow, red):
for bullet in yellow_bullets:
bullet.x += Bullet_velocity
if red.colliderect(bullet):
pygame.event.post(event.Event(RED_HITS))
yellow_bullets.remove
for bullet in red_bullets:
bullet.x -= Bullet_velocity
if yellow.colliderect(bullet):
pygame.event.post(event.Event(YELLOW_HITS))
red_bullets.remove
def main():
red = pygame.Rect(700, 300, SPACESHIP_WIDTH, SPACESHIP_HEIGHT)
yellow = pygame.Rect(100, 300, SPACESHIP_WIDTH, SPACESHIP_HEIGHT) # x,y , width , height
red_bullets = []
yellow_bullets = []
clock = pygame.time.Clock() # create a clock object which can be used to keep track of time
run = True
while run: #event loop
clock.tick(FPS) #ensures that you will never go above the FPS you set the game on
for event in pygame.event.get():
if event.type == pygame.QUIT: #.QUIT to end pygame in the loop
run = False
if event.type == pygame.KEYDOWN:
if event.type == pygame.K_LCTRL and len(yellow_bullets) < MAX_BULLETS:
bullet = pygame.Rect(
yellow.x + yellow.width, yellow.y + yellow.height//2 - 2,10,5) # so it goes from the middle
yellow_bullets.append(bullet)
if event.type == pygame.K_RCTRL and len(red_bullets) < MAX_BULLETS:
bullet = pygame.Rect(
red.x, red.y + red.height//2 - 2,10,5) # so it goes from the middle
red_bullets.append(bullet)
print(red_bullets, yellow_bullets)
keys_pressed = pygame.key.get_pressed() # this tells us what keys are being clicked every 1
yellow_movement(keys_pressed, yellow)
red_movement(keys_pressed, red)
handle_bullets(yellow_bullets, red_bullets, yellow, red)
draw_window(red, yellow, red_bullets, yellow_bullets) #you took the yellow and red to the Drawing functions
pygame.quit #.quit to end pygame in general
if __name__ == "__main__": #doesnt matter
main()
Change event.type == pygame.K_LCTRL to event.key == pygame.K_LCTRL and event.type == pygame.K_RCTRL to event.key == pygame.K_RCTRL

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)

Randomly spawning an enemy

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)

How do I draw a list of class objects in pygame?

I am trying to create Space Invaders in pygame (I am a beginner and this is only my second game) and I have created a list of alien objects:
numOfAliens = 24
aliens = []
# An alien class containing an x, y, width, and height value
class alien:
x, y = 0, 0
width, height = 20, 20
# Loops 24 times and adds alien to the class
for i in range(numOfAliens):
aliens.append(alien)
I have another bit of code that adds 5 and the width to each x value to space the aliens apart:
for i in aliens:
i.x += 5 + i.width
print(i.x, i.y, i.width, i.height)
The print tells me that these first 2 blocks of code work fine without any issues, the problem occurs when I try to draw it to the pygame window:
# Loops 24 times, drawing each alien to the window
for i in range(numOfAliens):
pygame.draw.rect(win, GREEN, (aliens[i].x, aliens[i].y, aliens[i].width, aliens[i].height))
Surely aliens[i].x will get the x value of each object in the list, but it doesn't.
If I add a print("hi") in this for loop it infinitely prints out "hi" when it should only loop 24 times, and nothing gets drawn on the window, is there any way to fix this?
Here is all the code if needed:
import pygame
pygame.init()
clock = pygame.time.Clock()
# Window
win_width, win_height = 500, 500
win = pygame.display.set_mode((win_width, win_height))
pygame.display.set_caption("Space Invaders")
# Colours
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (30, 224, 27)
# Player
player_width, player_height = 20, 20
player_x, player_y = int((win_width / 2) - (player_width / 2)), (win_height - 50)
player_vel = 10
isShooting = False
isAlive = True
# Bullet
bullet_width, bullet_height = 4, 16
bullet_x, bullet_y = player_x, player_y
bullet_vel = 5
# Aliens
numOfAliens = 24
aliens = []
class alien:
x, y = 0, 0
width, height = player_width, player_height
for i in range(numOfAliens):
aliens.append(alien)
for i in aliens:
i.x += 5 + i.width
print(i.x, i.y, i.width, i.height)
# Draw Function
def draw():
win.fill(BLACK)
pygame.draw.rect(win, GREEN, (player_x, player_y, player_width, player_height))
for i in aliens:
pygame.draw.rect(win, GREEN, (i.x, i.y, i.width, i.height))
if isShooting:
pygame.draw.rect(win, WHITE, (bullet_x, bullet_y, bullet_width, bullet_height))
pygame.display.update()
# Game Loop
run = True
while run:
clock.tick(20)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player_x > 0:
player_x -= player_vel
if keys[pygame.K_RIGHT] and player_x < win_width - player_width:
player_x += player_vel
if keys[pygame.K_SPACE] and not(isShooting):
bullet_y = player_y
bullet_x = int(player_x + (player_width / 2))
isShooting = True
if bullet_y + bullet_height <= 0:
isShooting = False
if isShooting:
bullet_y -= bullet_vel
draw()
pygame.quit()
Actually you do not create any instance of alien. alien is just the class itself.
aliens.append(alien)
To create an instance of the class alien you have to add parentheses:
See Class Objects
aliens.append(alien())
Please note, that class names in python should start with capital letters.
See Style Guide for Python Code - Class Names
Add a constructor with a parameter for the x and y coordinate to the class Alien and create instance attributes for the position and size.
Calculate the position of the alien dependent on the control variable i:
numOfAliens = 24
aliens = []
class Alien:
def __init__(self, x, y):
self.x, self.y = x, y
self.width, self.height = player_width, player_height
for i in range(numOfAliens):
aliens.append(Alien(i * (5+player_width), 0))

Categories