This question already has answers here:
Spawning multiple instances of the same object concurrently in python
(1 answer)
Trying to delay a specific function for spawning enemy after a certain amount of time
(1 answer)
Closed 2 years ago.
I wonder how I can spawn objects (like an enemy) at a random position with a 2 seconds delay/cooldown.
I know how to spawn them at a random coordinate. But what I'm wondering is how I can spawn multiple objects and still keep track of the other ones that are already moving, kind of like you do when you shoot bullets in a pygame.
The time delay/cooldown I can probably solve just by using pygame.time.get_ticks(). So my main question is how I can spawn multiple objects and track them with hitboxes (which I have already made)
Here is the basic part that in this example spawns an asteroid.
class Enemy:
asteroids = [pygame.image.load('rock0.png'), pygame.image.load('rock1.png'), pygame.image.load('rock2.png'),
pygame.image.load('rock3.png'), pygame.image.load('rock4.png')]
def __init__(self, y, width, height):
self.width = width
self.height = height
self.vel = 1.5
self.x = random.randrange(screen_width - self.width * 2)
self.y = y
self.asteroid = random.choice(self.asteroids)
def draw(self, win):
self.move()
win.blit(self.asteroid, (self.x, self.y))
def move(self):
self.y = self.y + self.vel
Here is the entire code for anybody who needs it.
import pygame
import random
pygame.init()
screen_width = 500
screen_height = 500
win = pygame.display.set_mode((screen_width, screen_height))
walk_left = [pygame.image.load('sprite_5.png'), pygame.image.load('sprite_6.png')]
walk_right = [pygame.image.load('sprite_3.png'), pygame.image.load('sprite_4.png')]
standing = [pygame.image.load('sprite_0.png'), pygame.image.load('sprite_1.png'), pygame.image.load('sprite_2.png')]
class Player:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 4
self.left = False
self.right = False
self.standing = True
self.walk_count = 0
self.hitbox = (self.x + 2, self.y + 26, 123, 45)
def draw(self, win):
if self.walk_count + 1 >= 12:
self.walk_count = 0
if not self.standing:
if self.left:
win.blit(walk_left[self.walk_count // 6], (self.x, self.y))
self.walk_count += 1
elif self.right:
win.blit(walk_right[self.walk_count // 6], (self.x, self.y))
self.walk_count += 1
else:
win.blit(standing[self.walk_count // 4], (self.x, self.y))
self.walk_count += 1
self.hitbox = (self.x + 2, self.y + 26, 123, 45)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
def move():
if keys[pygame.K_LEFT] and man.x > man.vel or keys[pygame.K_a] and man.x > man.vel:
man.x -= man.vel
man.left = True
man.right = False
man.standing = False
elif keys[pygame.K_RIGHT] and man.x < 500 - man.width - man.vel:
man.x += man.vel
man.left = False
man.right = True
man.standing = False
else:
man.standing = True
class Enemy:
asteroids = [pygame.image.load('rock0.png'), pygame.image.load('rock1.png'), pygame.image.load('rock2.png'),
pygame.image.load('rock3.png'), pygame.image.load('rock4.png')]
number = [0, 1, 2, 3, 4]
def __init__(self, y, width, height):
self.width = width
self.height = height
self.vel = 1.5
self.x = random.randrange(screen_width - self.width * 2)
self.y = y
self.index = random.choice(self.number)
self.hitbox = (self.x, self.y, self.width, self.height)
def draw(self, win):
self.move()
win.blit(self.asteroids[self.index], (self.x, self.y))
if self.index == 0:
self.hitbox = (self.x + 68, self.y + 68, self.width - 10, self.height - 14)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif self.index == 1:
self.hitbox = (self.x + 38, self.y + 47, self.width + 20, self.height - 5)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif self.index == 2:
self.hitbox = (self.x + 18, self.y + 12, self.width + 32, self.height + 30)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif self.index == 3:
self.hitbox = (self.x + 20, self.y + 32, self.width + 16, self.height + 5)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
else:
self.hitbox = (self.x + 4, self.y + 7, self.width - 24, self.height - 31)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
def move(self):
self.y = self.y + self.vel
class Projectile:
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
self.vel = 5
def draw(self, win):
pygame.draw.rect(win, self.color, (self.x, self.y, self.height, self. width))
class Unit:
def __init__(self):
self.last = pygame.time.get_ticks()
self.cooldown = 200
def fire(self):
now = pygame.time.get_ticks()
if now - self.last >= self.cooldown:
self.last = now
spawn_bullet()
def spawn_bullet():
if keys[pygame.K_SPACE]:
bullets.append(Projectile((man.x + man.width // 2), (man.y - 7), 7, 3, (255, 0, 0)))
def re_draw():
win.fill((0, 0, 0))
asteroid.draw(win)
man.draw(win)
for bullet in bullets:
bullet.draw(win)
pygame.display.update()
delay = Unit()
man = Player(186, 400, 128, 128)
bullets = []
asteroid = Enemy(10, 64, 64)
run = True
clock = pygame.time.Clock()
while run:
last = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
for bullet in bullets:
if 0 < bullet.y < 500:
bullet.y -= bullet.vel
else:
bullets.pop(bullets.index(bullet))
keys = pygame.key.get_pressed()
move()
delay.fire()
clock.tick(60)
re_draw()
pygame.quit()
I recommend to use a timer event. Use pygame.time.set_timer() to repeatedly create an event on the event queue.
Use a pygame.sprite.Group and derive Enemy from pygame.sprite.Sprite to manage multiple enemies. Note it is important to use the attributes .image and .rect in a sprite. e.g.:
class Enemy(pygame.sprite.Sprite):
asteroids = [pygame.image.load('rock0.png'), pygame.image.load('rock1.png'), pygame.image.load('rock2.png'),
pygame.image.load('rock3.png'), pygame.image.load('rock4.png')]
def __init__(self, y, width, height):
super().__init__()
self.width = width
self.height = height
self.vel = 1.5
x = random.randrange(screen_width - self.width * 2)
self.image = random.choice(self.asteroids)
self.rect = self.image.get_rect(center = (x, y))
def move(self):
self.rect.y += self.vel
enemies = pygame.sprite.Group()
my_event_id = pygame.USEREVENT + 1
pygame.time.set_timer(my_event_id, 2000) # 2000 milliseconds = 2 seconds
run = True
while run:
last = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == my_event_id:
# spawn new enemy
enemies.add(Enemy(10, 64, 64))
# [...]
for e in enemies:
e.move()
# [...]
enemies.draw(win)
If you make Projectile a pygame.sprite.Sprite, then you can use and bullets a pygame.sprite.Group, then you can use pygame.sprite.spritecollide() or pygame.sprite.groupcollide() to find hits and kill the enemies. e.g.:
class Projectile(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, color):
super().__init__()
self.image = pygame.Surface((width, height))
self.image.fill(color)
self.rect = self.image.get_rect(center = (x, y))
self.vel = 5
def move(self):
self.rect.y -= self.vel
bullets = pygame.sprite.Group()
def spawn_bullet():
if keys[pygame.K_SPACE]:
bullets.add(Projectile((man.x + man.width // 2), (man.y - 7), 3, 7, (255, 0, 0)))
def re_draw():
win.fill((0, 0, 0))
enemies.draw(win)
man.draw(win)
bullets.draw(win)
pygame.display.update()
while run:
# [...]
for e in enemies:
e.move()
if e.rect.y > 500:
e.kill()
for b in bullets:
b.move()
if 0 > b.rect.y or b.rect.y > 500:
b.kill()
pygame.sprite.groupcollide(bullets, enemies, True, True)
Full code:
import pygame
import random
pygame.init()
screen_width = 500
screen_height = 500
win = pygame.display.set_mode((screen_width, screen_height))
walk_left = [pygame.image.load('sprite_5.png'), pygame.image.load('sprite_6.png')]
walk_right = [pygame.image.load('sprite_3.png'), pygame.image.load('sprite_4.png')]
standing = [pygame.image.load('sprite_0.png'), pygame.image.load('sprite_1.png'), pygame.image.load('sprite_2.png')]
class Player:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 4
self.left = False
self.right = False
self.standing = True
self.walk_count = 0
self.hitbox = (self.x + 2, self.y + 26, 123, 45)
def draw(self, win):
if self.walk_count + 1 >= 12:
self.walk_count = 0
if not self.standing:
if self.left:
win.blit(walk_left[self.walk_count // 6], (self.x, self.y))
self.walk_count += 1
elif self.right:
win.blit(walk_right[self.walk_count // 6], (self.x, self.y))
self.walk_count += 1
else:
win.blit(standing[self.walk_count // 4], (self.x, self.y))
self.walk_count += 1
self.hitbox = (self.x + 2, self.y + 26, 123, 45)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
def move():
if keys[pygame.K_LEFT] and man.x > man.vel or keys[pygame.K_a] and man.x > man.vel:
man.x -= man.vel
man.left = True
man.right = False
man.standing = False
elif keys[pygame.K_RIGHT] and man.x < 500 - man.width - man.vel:
man.x += man.vel
man.left = False
man.right = True
man.standing = False
else:
man.standing = True
class Enemy(pygame.sprite.Sprite):
asteroids = [pygame.image.load('rock0.png'), pygame.image.load('rock1.png'), pygame.image.load('rock2.png'),
pygame.image.load('rock3.png'), pygame.image.load('rock4.png')]
def __init__(self, y, width, height):
super().__init__()
self.width = width
self.height = height
self.vel = 1.5
x = random.randrange(screen_width - self.width * 2)
self.image = random.choice(self.asteroids)
self.rect = self.image.get_rect(topleft = (x, y))
def move(self):
self.rect.y += self.vel
class Projectile(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, color):
super().__init__()
self.image = pygame.Surface((width, height))
self.image.fill(color)
self.rect = self.image.get_rect(topleft = (x, y))
self.vel = 5
def move(self):
self.rect.y -= self.vel
my_event_id = pygame.USEREVENT + 1
pygame.time.set_timer(my_event_id, 2000) # 2000 milliseconds = 2 seconds
class Unit:
def __init__(self):
self.last = pygame.time.get_ticks()
self.cooldown = 200
def fire(self):
now = pygame.time.get_ticks()
if now - self.last >= self.cooldown:
self.last = now
spawn_bullet()
def spawn_bullet():
if keys[pygame.K_SPACE]:
bullets.add(Projectile((man.x + man.width // 2), (man.y - 7), 3, 7, (255, 0, 0)))
def re_draw():
win.fill((0, 0, 0))
enemies.draw(win)
man.draw(win)
bullets.draw(win)
pygame.display.update()
delay = Unit()
man = Player(186, 400, 128, 128)
bullets = pygame.sprite.Group()
enemies = pygame.sprite.Group()
run = True
clock = pygame.time.Clock()
while run:
last = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == my_event_id:
# spawn new enemy
enemies.add(Enemy(10, 64, 64))
for e in enemies:
e.move()
if e.rect.y > 500:
e.kill()
for b in bullets:
b.move()
if 0 > b.rect.y or b.rect.y > 500:
b.kill()
pygame.sprite.groupcollide(bullets, enemies, True, True)
keys = pygame.key.get_pressed()
move()
delay.fire()
clock.tick(60)
re_draw()
pygame.quit()
Related
As a beginner, I am struggling to create multiple enemies in pygame. What could I possibly add or implement to my code in order to do so?
Code:
# WORK IN PROGRESS!
# I followed techwithtim's tutorial
# I do not own the images and sounds used in game
# TODO Create multiple Enemies
import pygame
import random
# Screen parameters
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption("SPPACCE")
bg = pygame.image.load("bg.png")
font = pygame.font.SysFont('comicsans', 30, True)
clock = pygame.time.Clock()
score = 0
# Music & Sound effects
bulletsound = pygame.mixer.Sound('sounds/bullet_soundeffect.wav')
explosion = pygame.mixer.Sound('sounds/explosion_effect.wav')
explosion2 = pygame.mixer.Sound('sounds/torpedo_explosion.wav')
# Player parameters
class Player(object):
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
self.player_vel = 5
def draw(self, screen):
screen.blit(player_char, (self.x, self.y))
# Enemy parameters
class Enemy(object):
def __init__(self, x, y, height, width, end):
self.x = x
self.y = y
self.height = height
self.width = width
self.enemy_vel = 1.5
self.end = end
self.path = [self.x, self.end]
self.hitbox = (self.x + 17, self.y + 2, 65, 65)
self.health = 5
self.visible = True
def draw(self, screen):
self.move()
if self.visible:
self.hitbox = (self.x + 0, self.y, 65, 65)
pygame.draw.rect(screen, (255, 0, 0), self.hitbox, 2)
screen.blit(enemy_char, (self.x, self.y))
# Health bars
pygame.draw.rect(screen, (0, 0, 0), (self.hitbox[0], self.hitbox[1] - 20, 65, 7))
pygame.draw.rect(screen, (255, 0, 0), (self.hitbox[0], self.hitbox[1] - 20, 65 - (12 * (5 - self.health)), 7))
def move(self):
if self.enemy_vel > 0:
if self.x < self.path[1] + self.enemy_vel:
self.x += self.enemy_vel
else:
self.enemy_vel = self.enemy_vel * -1
self.x += self.enemy_vel
else:
if self.x > self.path[0] - self.enemy_vel:
self.x += self.enemy_vel
else:
self.enemy_vel = self.enemy_vel * -1
self.x += self.enemy_vel
def hit(self):
if self.health > 0:
self.health -= 1
else:
self.visible = False
explosion.play()
global score
score += 1
# Player Projectile parameters
class Projectile(object):
def __init__(self, x, y, color, radius):
self.x = x
self.y = y
self.color = color
self.radius = radius
self.vel = 12.5
def draw(self, screen):
pygame.draw.circle(screen, self.color, (self.x, self.y), self.radius)
# Images
player_char = pygame.image.load('sprites/hotdog.png')
enemy_char = pygame.image.load('sprites/hamburger.png')
def blit(): # This draws the sprites
player.draw(screen)
enemy.draw(screen)
for projectile in projectiles:
projectile.draw(screen)
score_text = font.render("Score: " + str(score), 1, (0, 109, 255))
version = font.render("Version 01 ", 1, (51, 153, 255))
screen.blit(score_text, (0, 0))
screen.blit(version, (520, 0))
shootloop = 0
if shootloop > 0:
shootloop += 1
if shootloop > 2:
shootloop = 0
player = Player(300, 400, 64, 64)
enemy = Enemy(random.randint(10, 100), random.randint(20, 100), 64, 64, 480)
enemy_count = random.randint(1, 10)
projectiles = []
run = True
while run:
clock.tick(60)
screen.fill((0, 0, 0))
screen.blit(bg, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# Movement keys with playeborders
keys = pygame.key.get_pressed()
if keys[pygame.K_s] and player.y < 480 - player.height - player.player_vel:
player.y += player.player_vel
if keys[pygame.K_w] and player.y > 280:
player.y -= player.player_vel
if keys[pygame.K_d] and player.x < 640 - player.width - player.player_vel:
player.x += player.player_vel
if keys[pygame.K_a] and player.x > player.player_vel:
player.x -= player.player_vel
for projectile in projectiles:
if projectile.y - projectile.radius < enemy.hitbox[1] + enemy.hitbox[3] and projectile.y + projectile.radius > enemy.hitbox[1]:
if enemy.visible == True:
if projectile.x + projectile.radius > enemy.hitbox[0] and projectile.x - projectile.radius < enemy.hitbox[0] + enemy.hitbox[2]:
enemy.hit()
explosion2.play()
projectiles.pop(projectiles.index(projectile))
if projectile.y < 640 and projectile.y > 0:
projectile.y -= projectile.vel
else:
projectiles.pop(projectiles.index(projectile))
# Player shooting
if keys[pygame.K_SPACE] and shootloop == 0:
if len(projectiles) < 1:
projectiles.append(Projectile(round(player.x + player.width //2),
round(player.y + player.height //2), [255, 150, 0], 7))
blit()
pygame.display.update()
I tried to recycle code from my last game project, but it doesn't work because the code structures are way too different from each other. This project is OOP meanwhile the last one has scattered variables.
Similar to your projectiles make a list of Enemy objects:
enemies = []
enemy_count = random.randint(3, 10)
for i in range( enemy_count ):
new_enemy = Enemy(random.randint(10, 100), random.randint(20, 100), 64, 64, 480)
enemies.append( new_enemy )
projectiles = []
Update your blit() to draw a list of enemies:
def blit(): # This draws the sprites
player.draw(screen)
for enemy in enemies:
enemy.draw(screen)
for projectile in projectiles:
projectile.draw(screen)
score_text = font.render("Score: " + str(score), 1, (0, 109, 255))
version = font.render("Version 01 ", 1, (51, 153, 255))
screen.blit(score_text, (0, 0))
screen.blit(version, (520, 0))
And check them all for collisions:
for enemy in enemies:
for projectile in projectiles:
if projectile.y - projectile.radius < enemy.hitbox[1] + enemy.hitbox[3] and projectile.y + projectile.radius > enemy.hitbox[1]:
if enemy.visible == True:
if projectile.x + projectile.radius > enemy.hitbox[0] and projectile.x - projectile.radius < enemy.hitbox[0] + enemy.hitbox[2]:
enemy.hit()
explosion2.play()
projectiles.pop(projectiles.index(projectile))
That should get you started.
The changes are relatively simple because you already have the data compartmentalised in objects. Good job.
EDIT: Re-spawning an enemy is simply a matter of adding another Enemy object to the enemies list:
new_enemy = Enemy(random.randint(10, 100), random.randint(20, 100), 64, 64, 480)
enemies.append( new_enemy )
I'm making a breakout game using the pygame module, and I've established most of the functionalities. However, I'm having a hard time making the ball collide with the blocks at the top of the screen. Specifically in the "collisions" method in the main class. I wrote a for loop to iterate through the block list to check if the block collided with the ball, and if it did, then it would remove from the row_1 list the respective index of the block, theoretically erasing the block from the screen. But I get an AttributeError: 'pygame.Rect' object has no attribute 'collidirect'. I appreciate the help.
import pygame, sys
pygame.init()
clock = pygame.time.Clock()
screen_width = 600
screen_height = 750
black = (0, 0, 0)
white = (255, 255, 255)
purple = (138, 43, 226)
red = (255, 0, 0)
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Breakout Game")
class BLOCK:
def __init__(self):
self.width = 54
self.height = 20
self.row_1 = [[5, 20]]
self.blocks = []
for i in range(9):
self.row_1.append(self.row_1[-1][:])
self.row_1[-1][0] += 59
def draw_blocks(self):
for cor in self.row_1:
block_rect = pygame.Rect(cor[0], cor[1], self.width, self.height)
self.blocks.append(block_rect)
pygame.draw.rect(screen, red, block_rect)
class BALL:
def __init__(self):
self.width = 15
self.height = 15
self.x = screen_width/2 - self.width/2
self.y = screen_height/2 - self.height/2
self.speed_x = 5
self.speed_y = 5
self.ball_rect = pygame.Rect(self.x, self.y, self.width, self.height)
def draw_ball(self):
pygame.draw.ellipse(screen, white, self.ball_rect)
def move_ball(self):
self.x += self.speed_x
self.y += self.speed_y
self.ball_rect.topleft = (self.x, self.y)
def wall_collision(self):
if self.x >= screen_width - self.width:
self.speed_x *= -1
if self.x <= 0:
self.speed_x *= -1
if self.y >= screen_height - self.height:
self.x = screen_width/2 - self.width/2
self.y = screen_height / 2 - self.height/2
if self.y <= 0:
self.speed_y *= -1
class PADDLE:
def __init__(self):
self.width = 100
self.height = 10
self.x = screen_width/2 - self.width/2
self.y = 600
self.speed = 7
self.paddle_rect = pygame.Rect(self.x, self.y, self.width, self.height)
def draw_paddle(self):
pygame.draw.rect(screen, purple, self.paddle_rect)
def move_paddle(self):
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
self.x -= self.speed
if key[pygame.K_RIGHT]:
self.x += self.speed
self.paddle_rect.topleft = (self.x, self.y)
class MAIN:
def __init__(self):
self.block = BLOCK()
self.ball = BALL()
self.paddle = PADDLE()
def draw(self):
self.block.draw_blocks()
self.ball.draw_ball()
self.paddle.draw_paddle()
def move(self):
self.ball.move_ball()
self.paddle.move_paddle()
def collisions(self):
self.ball.wall_collision()
if self.ball.ball_rect.colliderect(self.paddle.paddle_rect):
self.ball.speed_y *= -1
for block in self.block.blocks:
if block.collidirect(self.ball.ball_rect):
self.block.row_1.pop(self.block.blocks.index(block))
main = MAIN()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(black)
main.draw()
main.move()
main.collisions()
pygame.display.flip()
clock.tick(60)
It's a typo. The name of the method is colliderect rather then collidirect:
if block.collidirect(self.ball.ball_rect):
if block.colliderect(self.ball.ball_rect):
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
Hi guy i have a problem with my game, i receive this error when u run it, "line 283, in
bullets.pop(0)", "IndexError: pop from empty list", i think it is because the list of bullets is empty and it cannot pop them, but i dont know how to fix it.
If you can help me i would appreciate it very much.
Here is the code, thanks.
import pygame
import math
import random
pygame.init()
win = pygame.display.set_mode((800, 400))
pygame.display.set_caption("Pandemic")
clock = pygame.time.Clock()
score = 0
walkup = [pygame.image.load("Soldier-1up.png"), pygame.image.load("Soldier-2up.png"), pygame.image.load("Soldier-3up.png")]
walkdown = [pygame.image.load("Soldier-1down.png"), pygame.image.load("Soldier-2down.png"), pygame.image.load("Soldier-3down.png")]
walkright = [pygame.image.load("Soldier-1right.png"), pygame.image.load("Soldier-2right.png"), pygame.image.load("Soldier-3right.png")]
walkleft = [pygame.image.load("Soldier-1left.png"), pygame.image.load("Soldier-2left.png"), pygame.image.load("Soldier-3left.png")]
bg = pygame.image.load("map.png")
ch = pygame.image.load("Soldier-1up.png")
bulletimg = pygame.image.load("bullet.png")
walkvirus1 = [pygame.image.load("virus1.png"), pygame.image.load("virus2.png"), pygame.image.load("virus3.png")]
walkvirus2 = [pygame.image.load("1virus1.png"), pygame.image.load("1virus2.png"), pygame.image.load("1virus3.png")]
background1 = pygame.image.load("menu.png")
class player(object):
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 5
self.walkcount = 0
self.right = False
self.left = False
self.up = False
self.down = False
self.standing = True
self.hitbox = (self.x, self.y, self.width, self.height)
self.life = 3
def draw(self, win):
if self.walkcount + 1 >= 60:
self.walkcount = 0
if not (self.standing):
if self.left:
win.blit(walkleft[self.walkcount // 20], (self.x, self.y))
self.walkcount += 1
elif self.right:
win.blit(walkright[self.walkcount // 20], (self.x, self.y))
self.walkcount += 1
elif self.up:
win.blit(walkup[self.walkcount // 20], (self.x, self.y))
self.walkcount += 1
elif self.down:
win.blit(walkdown[self.walkcount // 20], (self.x, self.y))
self.walkcount += 1
else:
if self.up:
win.blit(walkup[0], (self.x, self.y))
elif self.down:
win.blit(walkdown[0], (self.x, self.y))
elif self.right:
win.blit(walkright[0], (self.x, self.y))
elif self.left:
win.blit(walkleft[0], (self.x, self.y))
self.hitbox = (self.x, self.y, self.width, self.height)
#pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
def hit(self):
self.x = 0
self.y = 0
self.life -= 1
self.walkcount = 0
font1 = pygame.font.SysFont("comicsans", 100)
text = font1.render("-1 out of 3 lifes.", 1, (255,0,0))
win.blit(text, (400 - (text.get_width()/2),150))
pygame.display.update()
pygame.time.delay(1000)
if self.life == 0:
pygame.quit()
class Bullet(object):
def __init__(self, x, y, radius, color, vertfacing, hortfacing):
self.x = x
self.y = y
self.radius = radius
self.color = color
self.speed = 8
self.vertfacing = vertfacing
self.hortfacing = hortfacing
def draw(self, screen):
pygame.draw.circle(screen, self.color, (self.x, self.y), self.radius)
def move(self):
if self.hortfacing == -1:
self.x -= self.speed
elif self.hortfacing == 1:
self.x += self.speed
elif self.vertfacing == 1:
self.y += self.speed
elif self.vertfacing == -1:
self.y -= self.speed
class enemy(object):
def __init__(self, x, y, width, height, end):
self.x = x
self.y = y
self.width = width
self.height = height
self.end = end
self.path = [self.x, self.end]
self.walkcount = 0
self.vel = 2
self.life = 3
self.hitbox = (self.x, self.y, self.width, self.height)
self.health = 15
self.visible = True
def draw(self, win):
self.move()
if self.visible:
if self.walkcount + 1 >= 60:
self.walkcount = 0
if self.vel > 0:
win.blit(walkvirus1[self.walkcount // 20], (self.x, self.y))
self.walkcount += 1
else:
win.blit(walkvirus1[self.walkcount // 20], (self.x, self.y))
self.walkcount += 1
pygame.draw.rect(win, (250, 0, 0), (self.hitbox[0], self.hitbox[1] - 20, 65, 10))
pygame.draw.rect(win, (0, 250, 0), (self.hitbox[0], self.hitbox[1] - 20, 65 - ((65/15) * (15 - self.health)), 10))
self.hitbox = (self.x, self.y, self.width, self.height)
#pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
def move(self):
dx, dy = man.x - self.x, man.y - self.y
dist = math.hypot(dx, dy)
dx, dy = dx / dist, dy / dist
self.x += dx * self.vel
self.y += dy * self.vel
def hit(self):
self.walkcount = 0
if self.health > 0:
self.health -= 1
elif self.life > 0:
self.x = random.randint(400, 600)
self.y = random.randint(800, 1000)
self.health = 15
self.life -= 1
else:
self.visible = False
print("hit")
class enemy1(object):
def __init__(self, x, y, width, height, end):
self.x = x
self.y = y
self.width = width
self.height = height
self.end = end
self.path = [self.x, self.end]
self.walkcount = 0
self.vel = 2
self.hitbox = (self.x, self.y, self.width, self.height)
self.health = 30
self. life = 3
self.visible = True
def draw(self, win):
self.move()
if self.visible:
if self.walkcount + 1 >= 60:
self.walkcount = 0
if self.vel > 0:
win.blit(walkvirus2[self.walkcount // 20], (self.x, self.y))
self.walkcount += 1
else:
win.blit(walkvirus2[self.walkcount // 20], (self.x, self.y))
self.walkcount += 1
pygame.draw.rect(win, (250, 0, 0), (self.hitbox[0], self.hitbox[1] - 20, 65, 10))
pygame.draw.rect(win, (0, 250, 0), (self.hitbox[0], self.hitbox[1] - 20, 65 - ((65/30) * (30 - self.health)), 10))
self.hitbox = (self.x, self.y, self.width, self.height)
#pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
def move(self):
dx, dy = man.x - self.x, man.y - self.y
dist = math.hypot(dx, dy)
dx, dy = dx / dist, dy / dist
self.x += dx * self.vel
self.y += dy * self.vel
def hit(self):
self.walkcount = 0
if self.health > 0:
self.health -= 1
elif self.life > 0:
self.x = random.randint(400, 600)
self.y = random.randint(800, 1000)
self.health = 30
self.life -= 1
else:
self.visible = False
print("hit1")
def RedrawGame():
win.blit(bg, (0, 0))
text = font.render("score: " + str(score), 1, (0,0,0))
win.blit(text, (380, 10))
man.draw(win)
virus1.draw(win)
virus2.draw(win)
for bullet in bullets:
bullet.draw(win)
pygame.display.update()
#Loop
font = pygame.font.SysFont("comicsans",30,True)
man = player(400, 100, 64, 64)
virus1 = enemy(40, 110, 64, 64, 450)
virus2 = enemy1(40, 10, 64, 64, 450)
shoot = 0
bullets = []
run = True
menu = True
while run:
while menu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
menu = False
win.fill((0, 0, 0))
clock.tick(60)
win.blit(background1, (0, 0))
pygame.display.update()
clock.tick(60)
if virus1.visible == True:
if man.hitbox[1] < virus1.hitbox[1] + virus1.hitbox[3] and man.hitbox[1] + man.hitbox[3] > virus1.hitbox[1]:
if man.hitbox[0] + man.hitbox[2] > virus1.hitbox[0] and man.hitbox[0] < virus1.hitbox[0] + virus1.hitbox[2]:
man.hit()
if virus2.visible == True:
if man.hitbox[1] < virus2.hitbox[1] + virus2.hitbox[3] and man.hitbox[1] + man.hitbox[3] > virus2.hitbox[1]:
if man.hitbox[0] + man.hitbox[2] > virus2.hitbox[0] and man.hitbox[0] < virus2.hitbox[0] + virus2.hitbox[2]:
man.hit()
if shoot > 0:
shoot += 1
if shoot > 3:
shoot = 0
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
for bullet in bullets:
if virus1.visible == True:
if bullet.y - bullet.radius < virus1.hitbox[1] + virus1.hitbox[3] and virus1.y + bullet.radius > virus1.hitbox[1]:
if bullet.x + bullet.radius > virus1.hitbox[0] and bullet.x - bullet.radius < virus1.hitbox[0] + \
virus1.hitbox[2]:
virus1.hit()
score += 1
bullets.pop(0)
if virus2.visible == True:
if bullet.y - bullet.radius < virus2.hitbox[1] + virus2.hitbox[3] and virus2.y + bullet.radius > virus2.hitbox[1]:
if bullet.x + bullet.radius > virus2.hitbox[0] and bullet.x - bullet.radius < virus2.hitbox[0] + \
virus2.hitbox[2]:
virus2.hit()
score += 1
bullets.pop(0)
if bullet.x < 800 and bullet.x > 0 and bullet.y < 400 and bullet.y > 0:
bullet.move()
else:
bullets.remove(bullet)
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE] and shoot == 0:
hortfacing, vertfacing = 0, 0
if man.left:
hortfacing = -1
elif man.right:
hortfacing = 1
elif man.up:
vertfacing = -1
elif man.down:
vertfacing = 1
if len(bullets) < 5:
bullet = Bullet(round(man.x + man.width // 2),
round(man.y + man.height // 2), 6, (255, 165, 0),
vertfacing, hortfacing)
bullets.append(bullet)
shoot = 1
if keys[pygame.K_a] and man.x >= man.vel:
man.x -= man.vel
man.right = False
man.left = True
man.up = False
man.down = False
man.standing = False
elif keys[pygame.K_d] and man.x < 800 - man.width:
man.x += man.vel
man.right = True
man.left = False
man.up = False
man.down = False
man.standing = False
elif keys[pygame.K_s] and man.y < 400 - man.height:
man.y += man.vel
man.right = False
man.left = False
man.up = False
man.down = True
man.standing = False
elif keys[pygame.K_w] and man.y >= man.vel:
man.y -= man.vel
man.right = False
man.left = False
man.up = True
man.down = False
man.standing = False
else:
man.standing = True
man.walkcount = 0
RedrawGame()
pygame.quit()
Add an if statement so the program will only execute the for loop when there are bullets in the list:
if bullets:
for bullet in bullets:
if virus1.visible == True:
if bullet.y - bullet.radius < virus1.hitbox[1] + virus1.hitbox[3] and virus1.y + bullet.radius > virus1.hitbox[1]:
if bullet.x + bullet.radius > virus1.hitbox[0] and bullet.x - bullet.radius < virus1.hitbox[0] + \
virus1.hitbox[2]:
virus1.hit()
score += 1
bullets.pop(0)
How can I store the previous coordinate of an object in pygame? My problem may be a bit hard to explain, but I will try my best, it may help if you try out my code yourself to understand what I mean.
Here is what my game is about. I hope that it will make my problem more understandable.
I'm creating a pygame where you have a character at the bottom shooting at asteroids coming towards him. The asteroids appear every 1 second. When the asteroids move toward the player I want them to move in a straight line down, but my variable for the x position is a random one every for every asteroid.
However, the x position of the previous asteroid is updated with the new one. That means that the previous asteroid moves to the same x position as the new asteroid. How can I prevent this from happening?
This is where the problem is (scroll down for full code)
while run:
last = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == my_event_id:
x = random.randrange(screen_width - 64 * 2) # right here
index = random.choice(number)
asteroids_on_screen.append(Enemy(rock.y, rock.width, rock.height))
for a in asteroids_on_screen:
if -141 < a.y < 500:
a.y += a.vel
else:
asteroids_on_screen.pop(asteroids_on_screen.index(a))
I'm including my full code for anyone to try it out to clarify my problem.
import pygame
import random
pygame.init()
screen_width = 500
screen_height = 500
win = pygame.display.set_mode((screen_width, screen_height))
walk_left = [pygame.image.load('sprite_5.png'), pygame.image.load('sprite_6.png')]
walk_right = [pygame.image.load('sprite_3.png'), pygame.image.load('sprite_4.png')]
standing = [pygame.image.load('sprite_0.png'), pygame.image.load('sprite_1.png'), pygame.image.load('sprite_2.png')]
class Player:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 4
self.left = False
self.right = False
self.standing = True
self.walk_count = 0
self.hitbox = (self.x + 2, self.y + 26, 123, 45)
def draw(self, win):
if self.walk_count + 1 >= 12:
self.walk_count = 0
if not self.standing:
if self.left:
win.blit(walk_left[self.walk_count // 6], (self.x, self.y))
self.walk_count += 1
elif self.right:
win.blit(walk_right[self.walk_count // 6], (self.x, self.y))
self.walk_count += 1
else:
win.blit(standing[self.walk_count // 4], (self.x, self.y))
self.walk_count += 1
self.hitbox = (self.x + 2, self.y + 31, 123, 40)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
def move():
if keys[pygame.K_LEFT] and man.x > man.vel or keys[pygame.K_a] and man.x > man.vel:
man.x -= man.vel
man.left = True
man.right = False
man.standing = False
elif keys[pygame.K_RIGHT] and man.x < 500 - man.width - man.vel:
man.x += man.vel
man.left = False
man.right = True
man.standing = False
else:
man.standing = True
class Enemy:
def __init__(self, y, width, height):
self.width = width
self.height = height
self.vel = 1.5
self.y = y
x = random.randrange(screen_width - 64 * 2)
self.hitbox = (x, self.y, self.width, self.height)
def draw(self, win):
win.blit(asteroids[index], (x, self.y))
if index == 0:
self.hitbox = (x + 68, self.y + 68, self.width - 10, self.height - 14)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif index == 1:
self.hitbox = (x + 38, self.y + 47, self.width + 20, self.height - 5)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif index == 2:
self.hitbox = (x + 18, self.y + 12, self.width + 32, self.height + 30)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif index == 3:
self.hitbox = (x + 20, self.y + 32, self.width + 16, self.height + 5)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
else:
self.hitbox = (x + 4, self.y + 7, self.width - 24, self.height - 31)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
my_event_id = pygame.USEREVENT + 1
pygame.time.set_timer(my_event_id, 1000)
class Projectile:
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
self.vel = 5
def draw(self, win):
pygame.draw.rect(win, self.color, (self.x, self.y, self.height, self. width))
class Unit:
def __init__(self):
self.last = pygame.time.get_ticks()
self.cooldown = 200
def fire(self):
now = pygame.time.get_ticks()
if now - self.last >= self.cooldown:
self.last = now
spawn_bullet()
def spawn_bullet():
if keys[pygame.K_SPACE]:
bullets.append(Projectile((man.x + 126 // 2), (man.y + 5), 7, 3, (255, 0, 0)))
def re_draw():
win.fill((0, 0, 0))
man.draw(win)
for bullet in bullets:
bullet.draw(win)
for a in asteroids_on_screen:
a.draw(win)
pygame.display.update()
asteroids = [pygame.image.load('rock0.png'), pygame.image.load('rock1.png'), pygame.image.load('rock2.png'),
pygame.image.load('rock3.png'), pygame.image.load('rock4.png')]
number = [0, 1, 2, 3, 4]
delay = Unit()
man = Player(186, 400, 128, 128)
bullets = []
asteroids_on_screen = []
rock = Enemy(-140, 64, 64)
run = True
clock = pygame.time.Clock()
while run:
last = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == my_event_id:
x = random.randrange(screen_width - 64 * 2)
index = random.choice(number)
asteroids_on_screen.append(Enemy(rock.y, rock.width, rock.height))
for a in asteroids_on_screen:
if -141 < a.y < 500:
a.y += a.vel
else:
asteroids_on_screen.pop(asteroids_on_screen.index(a))
for bullet in bullets:
if 0 < bullet.y < 500:
bullet.y -= bullet.vel
else:
bullets.pop(bullets.index(bullet))
keys = pygame.key.get_pressed()
move()
delay.fire()
clock.tick(60)
re_draw()
pygame.quit()
If anybody would want the frames/sprites used please let me know
You've to make x an instance variable (attribute) of the class Enemy. So each enemy gets it's "own" x coordinate.
This means, substitute x by self.x:
class Enemy:
def __init__(self, y, width, height):
self.width = width
self.height = height
self.vel = 1.5
self.y = y
self.x = random.randrange(screen_width - 64 * 2)
self.hitbox = (self.x, self.y, self.width, self.height)
def draw(self, win):
win.blit(asteroids[index], (self.x, self.y))
if index == 0:
self.hitbox = (self.x + 68, self.y + 68, self.width - 10, self.height - 14)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif index == 1:
self.hitbox = (self.x + 38, self.y + 47, self.width + 20, self.height - 5)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif index == 2:
self.hitbox = (self.x + 18, self.y + 12, self.width + 32, self.height + 30)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif index == 3:
self.hitbox = (self.x + 20, self.y + 32, self.width + 16, self.height + 5)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
else:
self.hitbox = (self.x + 4, self.y + 7, self.width - 24, self.height - 31)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
I need help with a TypeError: '<' not supported between instances of 'int' and 'tuple' error in line 158
The program is basicly a game in pygame where you shoot at objects coming towards you (this is pretty much irrelevant, but if anyone wants to be able to understand the variables I think it can help).
The problem is located in the main loop. What am I doing wrong?
while run:
last = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == my_event_id:
x = random.randrange(screen_width - 64 * 2)
index = random.choice(number)
asteroids_on_screen.append(Enemy((x, rock.y), rock.width, rock.height))
for a in asteroids_on_screen:
if 0 < a.y < 500: # right here is the error
a.y -= a.vel
else:
asteroids_on_screen.pop(asteroids_on_screen.index(a))
Here is the full code if you want to see all the details.
import pygame
import random
pygame.init()
screen_width = 500
screen_height = 500
win = pygame.display.set_mode((screen_width, screen_height))
walk_left = [pygame.image.load('sprite_5.png'), pygame.image.load('sprite_6.png')]
walk_right = [pygame.image.load('sprite_3.png'), pygame.image.load('sprite_4.png')]
standing = [pygame.image.load('sprite_0.png'), pygame.image.load('sprite_1.png'), pygame.image.load('sprite_2.png')]
class Player:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 4
self.left = False
self.right = False
self.standing = True
self.walk_count = 0
self.hitbox = (self.x + 2, self.y + 26, 123, 45)
def draw(self, win):
if self.walk_count + 1 >= 12:
self.walk_count = 0
if not self.standing:
if self.left:
win.blit(walk_left[self.walk_count // 6], (self.x, self.y))
self.walk_count += 1
elif self.right:
win.blit(walk_right[self.walk_count // 6], (self.x, self.y))
self.walk_count += 1
else:
win.blit(standing[self.walk_count // 4], (self.x, self.y))
self.walk_count += 1
self.hitbox = (self.x + 2, self.y + 31, 123, 40)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
def move():
if keys[pygame.K_LEFT] and man.x > man.vel or keys[pygame.K_a] and man.x > man.vel:
man.x -= man.vel
man.left = True
man.right = False
man.standing = False
elif keys[pygame.K_RIGHT] and man.x < 500 - man.width - man.vel:
man.x += man.vel
man.left = False
man.right = True
man.standing = False
else:
man.standing = True
class Enemy:
def __init__(self, y, width, height):
self.width = width
self.height = height
self.vel = 1.5
self.y = y
x = random.randrange(screen_width - 64 * 2)
self.hitbox = (x, self.y, self.width, self.height)
def draw(self, win):
if index == 0:
self.hitbox = (x + 68, self.y + 68, self.width - 10, self.height - 14)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif index == 1:
self.hitbox = (x + 38, self.y + 47, self.width + 20, self.height - 5)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif index == 2:
self.hitbox = (x + 18, self.y + 12, self.width + 32, self.height + 30)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
elif index == 3:
self.hitbox = (x + 20, self.y + 32, self.width + 16, self.height + 5)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
else:
self.hitbox = (x + 4, self.y + 7, self.width - 24, self.height - 31)
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
my_event_id = pygame.USEREVENT + 1
pygame.time.set_timer(my_event_id, 1000)
class Projectile:
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
self.vel = 5
def draw(self, win):
pygame.draw.rect(win, self.color, (self.x, self.y, self.height, self. width))
class Unit:
def __init__(self):
self.last = pygame.time.get_ticks()
self.cooldown = 200
def fire(self):
now = pygame.time.get_ticks()
if now - self.last >= self.cooldown:
self.last = now
spawn_bullet()
def spawn_bullet():
if keys[pygame.K_SPACE]:
bullets.append(Projectile((man.x + 126 // 2), (man.y + 5), 7, 3, (255, 0, 0)))
def re_draw():
win.fill((0, 0, 0))
man.draw(win)
for bullet in bullets:
bullet.draw(win)
for a in asteroids_on_screen:
a.draw(win)
pygame.display.update()
asteroids = [pygame.image.load('rock0.png'), pygame.image.load('rock1.png'), pygame.image.load('rock2.png'),
pygame.image.load('rock3.png'), pygame.image.load('rock4.png')]
number = [0, 1, 2, 3, 4]
delay = Unit()
man = Player(186, 400, 128, 128)
bullets = []
asteroids_on_screen = []
rock = Enemy(10, 64, 64)
run = True
clock = pygame.time.Clock()
while run:
last = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == my_event_id:
x = random.randrange(screen_width - 64 * 2)
index = random.choice(number)
asteroids_on_screen.append(Enemy((x, rock.y), rock.width, rock.height))
for a in asteroids_on_screen:
if 0 < a.y < 500:
a.y -= a.vel
else:
asteroids_on_screen.pop(asteroids_on_screen.index(a))
for bullet in bullets:
if 0 < bullet.y < 500:
bullet.y -= bullet.vel
else:
bullets.pop(bullets.index(bullet))
keys = pygame.key.get_pressed()
move()
delay.fire()
clock.tick(60)
re_draw()
pygame.quit()
Enemy initializer: def __init__(self, y, width, height)
You passed: Enemy((x, rock.y), rock.width, rock.height))
So your enemy.y is actually a tuple of (x, rock.y). Surely you can't compare this with an int.
Next time, print out the things you are comparing and bugs like this will be obvious.
The issue is caused, because the first parameter to the constructor of Enemy has to be a single y coordiante:
class Enemy:
def __init__(self, y, width, height)
# [...]
self.y = y
But when you crate the Enemy object, then the 1st parameter is a tuple ((x, rock.y)):
asteroids_on_screen.append(Enemy((x, rock.y), rock.width, rock.height))
So a.y is a tuple rather than a single value. Probably it has to be:
asteroids_on_screen.append(Enemy(rock.y, rock.width, rock.height))