Spawning Multiple Instances of the Same Object Concurrently in Python

Spawning multiple instances of the same object concurrently in python

It does not work that way. time.sleep, pygame.time.wait() or pygame.time.delay is not the right way to control time and gameplay within an application loop. The game does not respond while you wait. The application loop runs continuously. You have to measure the time in the loop and spawn the objects according to the elapsed time.

pygame.Surface.fill clears the entire screen. Add the newly created objects to a list. Redraw all of the objects and the entire scene in each frame.

See also Time, timer event and clock


You have 2 options. Use pygame.time.get_ticks() to measure the time. Define a time interval after which a new object should appear. Create an object when the point in time is reached and calculate the point in time for the next object:

object_list = []
time_interval = 500 # 500 milliseconds == 0.1 seconds
next_object_time = 0

while run:
# [...]

current_time = pygame.time.get_ticks()
if current_time > next_object_time:
next_object_time += time_interval
object_list.append(Object())

Minimal example:

Sample Image repl.it/@Rabbid76/PyGame-TimerSpawnObjects

Sample Image

import pygame, random
pygame.init()
window = pygame.display.set_mode((300, 300))

class Object:
def __init__(self):
self.radius = 50
self.x = random.randrange(self.radius, window.get_width()-self.radius)
self.y = random.randrange(self.radius, window.get_height()-self.radius)
self.color = pygame.Color(0)
self.color.hsla = (random.randrange(0, 360), 100, 50, 100)

object_list = []
time_interval = 200 # 200 milliseconds == 0.2 seconds
next_object_time = 0

run = True
clock = pygame.time.Clock()
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

current_time = pygame.time.get_ticks()
if current_time > next_object_time:
next_object_time += time_interval
object_list.append(Object())

window.fill(0)
for object in object_list[:]:
pygame.draw.circle(window, object.color, (object.x, object.y), round(object.radius))
object.radius -= 0.2
if object.radius < 1:
object_list.remove(object)
pygame.display.flip()

pygame.quit()
exit()

The other option is to use the pygame.event module. Use pygame.time.set_timer() to repeatedly create a USEREVENT in the event queue. The time has to be set in milliseconds. e.g.:

object_list = []
time_interval = 500 # 500 milliseconds == 0.1 seconds
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, time_interval)

Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to be between pygame.USEREVENT (24) and pygame.NUMEVENTS (32). In this case pygame.USEREVENT+1 is the event id for the timer event.

Receive the event in the event loop:

while run:
for event in pygame.event.get():
if event.type == timer_event:
object_list.append(Object())

The timer event can be stopped by passing 0 to the time argument of pygame.time.set_timer.

Minimal example:

Sample Image repl.it/@Rabbid76/PyGame-TimerEventSpawn

Sample Image

import pygame, random
pygame.init()
window = pygame.display.set_mode((300, 300))

class Object:
def __init__(self):
self.radius = 50
self.x = random.randrange(self.radius, window.get_width()-self.radius)
self.y = random.randrange(self.radius, window.get_height()-self.radius)
self.color = pygame.Color(0)
self.color.hsla = (random.randrange(0, 360), 100, 50, 100)

object_list = []
time_interval = 200 # 200 milliseconds == 0.2 seconds
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, time_interval)

run = True
clock = pygame.time.Clock()
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == timer_event:
object_list.append(Object())

window.fill(0)
for object in object_list[:]:
pygame.draw.circle(window, object.color, (object.x, object.y), round(object.radius))
object.radius -= 0.2
if object.radius < 1:
object_list.remove(object)
pygame.display.flip()

pygame.quit()
exit()

Spawning Multiple of the same sprite

The part of the spawn procedure you posted doesn't create any new instances. Unless redI is declared as a new Red_Infantry somewhere else, you need to modify the code to create a new instance of Red_Infantry every time you want to spawn a soldier.

sprites.append(Red_Infantry())

To update the sprites:

for sprite in sprites:
sprite.update()

Put movement and other state changes in the update method. This is what pygame expects, but you can use a different style if you want. The main point is you must have multiple instances of Red_Infantry in order to see multiple sprites.

You could also use pygame's Group class instead of a simple list to hold the sprites.


Here's a full example that uses Group instead of list. In the example, an Enemy is spawned each time a key is pressed. Each Enemy prints its unique ID to stdout.

import sys
import random

import pygame
from pygame.locals import *

def main():
pygame.init()
screen = pygame.display.set_mode((480, 320))
enemies = pygame.sprite.Group()
while True:
for event in pygame.event.get():
if event.type == KEYDOWN:
enemies.add(Enemy(screen))
elif event.type == QUIT:
sys.exit()
enemies.update()
screen.fill(pygame.Color("black"))
enemies.draw(screen)
pygame.display.update()

class Enemy(pygame.sprite.Sprite):

def __init__(self, screen):
pygame.sprite.Sprite.__init__(self)
print "created a new sprite:", id(self)
self.image = pygame.image.load("sprite.png")
self.rect = self.image.get_rect()
self.rect.move_ip(random.randint(0, screen.get_width()),
random.randint(0, screen.get_height()))

def update(self):
self.rect.move_ip(random.randint(-3, 3), 0)

if __name__ == "__main__":
main()

Trying to spawn multiple enemies for my game in pygame

Remove the variable aliens, but add the enemies to the enemy_list. You can add multiple enemies:

no_of_enemies = 3
for _ in range(no_of_enemies):
x = None
while (x == None or any(enemy_rect.colliderect(e.hitbox) for e in enemy_list)):
x = random.randint(10 , 540)
enemy_rect = pygame.Rect(x , 200, 15 , 25)
enemy_list.append(enemy(x, 200, 15, 25, 550))

Draw all the enemies in the list in a loop:

#animation/character display
def drawing_window():
window.fill((0,0,0))
text = font.render('Score: ' + str(score), 1, (255,0,0))
window.blit(text, (390, 10))
space_ship.draw(window)
for bullet in bullets:
bullet.draw(window)
for aliens in enemy_list:
aliens.draw(window)
pygame.display.update()

Do the collision test in (nested) loops. DO not iterate the list, but shallow cpies of the lists (see How to remove items from a list while iterating?).

Use pygame.Rect.colliderect to detect the collision (see How do I detect collision in pygame?):

while begin:
clock.tick(50)
ship_rect = pygame.Rect(space_ship.hitbox)
for aliens in enemy_list[:]:
if ship_rect.colliderect(aliens.hitbox):
space_ship.hit()

for event in pygame.event.get():
if event.type == pygame.QUIT:
begin = False

for bullet in bullets[:]:
if bullet.y > 0:

for aliens in enemy_list[:]:
bullet_rect = pygame.Rect(0, 0, bullet.radius*2, bullet.radius*2)
bullet_rect.center = (bullet.x, bullet.y)
if bullet_rect.colliderect(aliens.hitbox):
score += 1
bullets.remove(bullet)
aliens.hit()
if aliens.health <= 0:
enemy_list.remove(aliens)

bullet.y = bullet.y - 10

else:
bullets.remove(bullet)

# [...]

Sample Image

How do I add a new enemy every 5 seconds in Pygame

You have a main loop. Before your loop, do:

enemytime = time.time() + 5

Then, in the loop, do something like:

    if time.time() >= enemytime:
# Time to spawn a new enemy.
enemies.append( Enemy3( 100, 500 ) )
# Reset the alarm.
enemytime = time.time() + 5

How to make instances spawn automatically around the player?

I need the Zombies to spawn away from the player at a certain distance.

In the constructor of the class Zombie the center position of the attribute rect has to be set:

class Zombie(pygame.sprite.Sprite):    
def __init__(self2, color, pos, radius, width):
super().__init__()
self2.image = pygame.Surface([radius*2, radius*2])
self2.image.fill(WHITE)
self2.image.set_colorkey(WHITE)
pygame.draw.circle(self2.image, color, [radius, radius], radius, width)
self2.rect = self2.image.get_rect()

self2.rect.center = pos # <-------- add this

Define a list which contains the zombies (zombie_list), a size (radius) zombie_rad of the zombie. Further a range (zombie_dist) for spawn distance of the zombies (minimum and maximum distance) and a time span in milliseconds when the first zombie appears (next_zombie_time).

zombie_list = []
zombie_rad = 10
zombie_dist = (65, 150)
next_zombie_time = pygame.time.get_ticks() + 3000 # first zombie after 3 seconds

Use pygame.time.get_ticks() to get the number of milliseconds since to program start. If the time exceeds next_zombie_time the span a zombie and set the time for the next zombie to spawn:

current_time = pygame.time.get_ticks()
if current_time > next_zombie_time:
next_zombie_time = current_time + 1000 # 1 second interval to the next zombie

# [...] spawn the zombie

Create the outer limit rectangle for the zombie position. This rectangle is the screen rectangle reduced by the radius of a zombie on each side. Each position inside this rectangle is a valid center position of a zombie, so that the zombie is completely on in the bounds of the screen.

Use pygame.Rect.collidepoint to check if a position is inside the rectangle. Repeat creating random position until a position inside the rectangle is found:

on_screen_rect = pygame.Rect(zombie_rad, zombie_rad, size[0]-2*zombie_rad, size[1]-2*zombie_rad)
zombi_pos = (-1, -1)
while not on_screen_rect.collidepoint(zombi_pos):

# [...] create random zombie pos

To get a random position around the player, get an random distance to the player by random.randint(a,b) and a random angle around the player in radiant by random.random() * math.pi * 2:

dist  = random.randint(*zombie_dist)
angle = random.random() * math.pi * 2

Finally calculate the position by converting the Polar coordinate (dist, angle) to a Cartesian coordinate:

p_pos = (playerChar.rect.centerx, playerChar.rect.centery)
zombi_pos = (p_pos[0] + dist * math.sin(angle), p_pos[1] + dist * math.cos(angle))

See the changes to the main loop of the program:

Sample Image

zombie_list = []
zombie_rad = 10
zombie_dist = (65, 150)
next_zombie_time = 3000

while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn=False
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_x:
carryOn=False

keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
playerChar.moveLeft(MOVE)
if keys[pygame.K_d]:
playerChar.moveRight(MOVE)
if keys[pygame.K_w]:
playerChar.moveUp(MOVE)
if keys[pygame.K_s]:
playerChar.moveDown(MOVE)

current_time = pygame.time.get_ticks()
if current_time > next_zombie_time:
next_zombie_time = current_time + 1000 # 1 second interval to the next zombie

on_screen_rect = pygame.Rect(zombie_rad, zombie_rad, size[0]-2*zombie_rad, size[1]-2*zombie_rad)
zombi_pos = (-1, -1)
while not on_screen_rect.collidepoint(zombi_pos):
dist = random.randint(*zombie_dist)
angle = random.random() * math.pi * 2
p_pos = (playerChar.rect.centerx, playerChar.rect.centery)
zombi_pos = (p_pos[0] + dist * math.sin(angle), p_pos[1] + dist * math.cos(angle))

new_pos = (random.randrange(0, size[0]), random.randrange(0, size[1]))
new_zomby = Zombie(RED, zombi_pos, zombie_rad, 0)
zombie_list.append(new_zomby)

screen.fill(BGColor)
screen.blit(playerChar.image,playerChar.rect)
for zombie in zombie_list:
screen.blit(zombie.image,zombie.rect)

pygame.display.flip()
clock.tick(60)


Related Topics



Leave a reply



Submit