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:
repl.it/@Rabbid76/PyGame-TimerSpawnObjects
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:
repl.it/@Rabbid76/PyGame-TimerEventSpawn
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)
# [...]
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:
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
Why Do Python Classes Inherit Object
How to Do Fuzzy Match Merge With Python Pandas
Combine Two Columns of Text in Pandas Dataframe
Create a Single Executable from a Python Project
Can't Send Input to Running Program in Sublime Text
Pandas Groupby With Delimiter Join
Can't Pickle ≪Type 'Instancemethod'≫ When Using Multiprocessing Pool.Map()
How to Get Json to Load into an Ordereddict
Finding and Replacing Elements in a List
What Is an Alternative to Execfile in Python 3
About the Changing Id of an Immutable String
Select Rows in Pandas Multiindex Dataframe
Pretty-Print an Entire Pandas Series/Dataframe
Variable Scopes in Python Classes