This question already has an answer here:
Spawning multiple instances of the same object concurrently in python
(1 answer)
Closed 1 year ago.
I have a game where I need an enemy to spawn after every 1350 milliseconds in a path. And the enemies only start coming after I click on the start button.
Time interval code:
sendEnemy = 0
def enemy_object_creation(sprite_list, health):
global sendEnemy
if pygame.time.get_ticks() >= sendEnemy:
foe = Enemy(sprite_list, health)
enemies.add(foe)
sendEnemy += 1350
Now here, when I click on the start button after around 5 secs, a bulk of of enemies come flooding at the beginning and then afterwards they again start coming smoothly. The more time I take to click on the start button, the more enemies spawn together at start. I'm not sure what to do about it.
Help will be appreciated.
In pygame the system time can be obtained by calling pygame.time.get_ticks(), which returns the number of milliseconds since pygame.init() was called. See pygame.time module.
You need to calculate the time when the first enemy will have to spawn, when the start button is clicked.
Specify the sendEnemy variable in global namespace:
sendEnemy = 0
Set the initial time value, when the button is clicked:
if clicked:
global sendEnemy
current_time = pygame.time.get_ticks()
sendEnemy = current_time
Spawn new enemies if sendEnemy is greater than 0, but less than the current time:
def enemy_object_creation(sprite_list, health):
global sendEnemy
if 0 < sendEnemy <= pygame.time.get_ticks():
foe = Enemy(sprite_list, health)
enemies.add(foe)
sendEnemy += 1350
If you want to stop enemies from spawning, set sendEnemy = 0.
Alternatively you can compute the time when the next enemy will have to spawn, depending on the current time. However, this leads to a slight inaccuracy as the code does not run on time, but a few milliseconds later:
def enemy_object_creation(sprite_list, health):
global sendEnemy
current_time = pygame.time.get_ticks()
if sendEnemy <= current_time:
foe = Enemy(sprite_list, health)
enemies.add(foe)
sendEnemy = current_time + 1350
Related
I'm relatively new to Python, and have been using pygame to make a simple idle clicker game. I'm a second year engineering student in high school, so I have a small background in C, but Python is a new territory for me.
I've made it to the point where I have two buttons, one for a multiplier, and one, which is the main button you click. I'm trying to figure out how I can make some kind of upgrade path that allows money to be passively generated over time. (i.e. when the button is pressed the first time, the cost is subtracted, and 1 money is added every second, then 2 when the button is pressed again, then 4, and so on).
I've tried to use some kind of while loop, but that results in the game dramatically slowing down, and the other buttons not responding. I've tried placing this code in different places, and have swapped out while and if multiple times, but that either doesn't work, or it yields the same result, mentioned above. Here is the loop for adding the money. The code below uses import time.
while autocost == 1:
time.sleep(1)
money = money + autolvl
Here is an example button for the multiplier.
class multiplier:
def __init__(self, x, y, image, scale):
width = image.get_width()
height = image.get_height()
self.image = pg.transform.scale(image, (int(width * scale), int(height * scale)))
self.rect = self.image.get_rect()
self.rect.topleft = (x, y)
self.clicked = False
def draw(self):
global money
global multlvl
global multcost
pos = pg.mouse.get_pos()
if self.rect.collidepoint(pos):
if pg.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
if money >= multcost:
money = money - multcost
multcost = multcost * 3
multlvl = multlvl *2
if pg.mouse.get_pressed()[0] == 0:
self.clicked = False
screen.blit(self.image, (self.rect.x, self.rect.y))
Values would be changed based on the needs of the button (in this case, swapping out variables like, autolvl and autocost.
If a loop is very fast, in your case you will get money faster.
Therefore you have to multiply the autolevel with the time that the loop took. If a loop takes 5 seconds, you need to add 5 times the money. If a loop takes 0.001 seconds, you need to add 0.001 times the money.
You added a sleep function that slows down the loops, but doesn't time the loop to exactly 1 second (or 1 millisecond, i am not sure)
import time
loop_start = time.time()
while True:
if autocost == 1:
money += autolevel * loop dt
loop_dt = time.time() - loop_start
loop_start = time.time()
This question already has answers here:
How do I continuously trigger an action at certain time intervals? Enemy shoots constant beam instead of bullets in pygame [duplicate]
(1 answer)
How can one continuously generate and track several random objects with a time delay in pygame? [duplicate]
(1 answer)
Closed 2 years ago.
I've tried using threading.timer to solve the issue, but can't seem to get it to work for what i want to do. No error messages. Either it doesn't even subtract from the players health, or it just drains the health instantly defeating the whole point, and time.sleep just freezes the whole program.
Here is the code i can't get to work properly
from threading import Timer
import pygame
playerhealth = ['<3', '<3', '<3', '<3', '<3'] # just a list of player health symbolized by hearts
running = True
while running:
def removehealth():
playerhealth.__delitem__(-1) # deletes the last item in the list of hearts
t = Timer(1, removehealth)
t.start()
# id display the hearts on screen down here
The way to do that using pygame, is to usee pygame.time.set_timer() to repeatedly create an USEREVENT. e.g.:
milliseconds_delay = 1000 # 1 seconds
timer_event = pygame.USEREVENT + 1
pygame.time.set_timer(timer_event, milliseconds_delay)
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, which drains the health.
Remove a heart when the event occurs in the event loop:
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == timer_event:
del playerhealth[-1]
A timer event can be stopped by passing 0 to the time parameter (pygame.time.set_timer(timer_event, 0)).
You can use the module time and wait for a certain amount of seconds.
import time
start = time.time() # gets current time
while running:
if time.time() - start > 1: # if its been 1 second
removehealth()
start = time.time()
Also to delete the last item in a list, you can do del playerhealth[-1]
I'm making a game, where when a player steps outside the screen, the level starts. I wish to show an image of "LEVEL 1" before the game starts, yet the program shows the image too quickly. My framerate is at 60.
I am wondering if there is a way to delay time for about 5 seconds during the screen blitting but after it resumes to it's normal pace. The problem for me with the pygame.time.delay() and wait stuff is that is slows the entire program down.
Is there a easier way?
EDIT______ CODE
#START OF LEVEL 1
if level1:
screen.blit(level1_image,background_position)
pygame.time.delay(500)
level1yay = True
if level1yay:
screen.blit(background,background_position)
#Flip the Display
pygame.display.flip()
clock.tick(time)
#Quit
pygame.quit()
The first image is not displayed and goes directly to the second image
You could set up a timer and keep track of the time. when the timer reaches the delay you want you can do something.
In the example below I set up the timer and I set the level1yay to true only once the timer has reached the delay value.
I added a condition canexit so the game doesn't terminate on the first loop. I assumed that the condition for "if level1: " has been set somewhere else.
mytimer = pygame.time.Clock() #creates timer
time_count = 0 #will be used to keep track of the time
mydelay = 5000 # will delay the main game by 5 seconds
canexit = False
#updates timer
mytimer.tick()
time_count += mytimer.get_time()
#check if mydelay has been reached
if time_count >= mydelay:
level1yay = True
canexit = True
#START OF LEVEL 1
if level1:
screen.blit(level1_image,background_position)
if level1yay:
screen.blit(background,background_position)
#Flip the Display
pygame.display.flip()
clock.tick(time)
#Quit
if canexit == True:
pygame.quit()
EDIT TO INCLUDE ACTIONS PRIOR TO ENTERING LEVEL1:
mytimer = pygame.time.Clock() #creates timer
time_count = 0 #will be used to keep track of the time
mydelay = 5000 # will delay the main game by 5 seconds
canexit = False
start_menu = True # to start with the start menu rather than level1
#Do something before level 1
if start_menu == True:
... do stuff
if start_menu end:
level1 = True
time_count = 0
#START OF LEVEL 1
if level1 == True:
#updates timer
mytimer.tick() # start's ticking the timer
time_count += mytimer.get_time() # add's the time since the last tick to time_count
#check if mydelay has been reached
if time_count >= mydelay:
level1 = False # so that you do not enter level1 again (even though this is redundant here since you will exit the game at the end of the loop... see canexit comment)
level1yay = True # so that you can enter level1yay
canexit = True # so that the game terminates at the end of the game loop
screen.blit(level1_image,background_position)
if level1yay:
screen.blit(background,background_position)
#Flip the Display
pygame.display.flip()
clock.tick(time)
#Quit
if canexit == True:
pygame.quit()
I don't know if this will really work, but you could try displaying the same image of "LEVEL 1" multiple times in a row, so it appears to stay around longer, but the program itself is never actually delayed.
pygame.time.wait(5000) is a delay of 5 seconds, 500 is half a second. You did set the level1 variable to True just before this code fragment, right?
I am making a small game using pygame in python 3.3 and I have a class for the enemies (see below). If I want to spawn in a new enemy every 10 or so seconds, so that theoretically, the game could go on forever. This however required the instances of the classes to be generated automatically every 10 seconds. Is there a way to create a new instance of the enemy class automatically?
Enemy class:
class Enemy(object):
def __init__(self, image, posx, posy, speed, damage):
self.image = image
self.posx = posx
self.posy = posy
self.speed = speed
self.damage = damage
def move(self):
self.posy = self.posy + self.speed
def draw(self):
screen.blit(self.image, (self.posx, self.posy))
Edit: Sorry! I posted this, not realising I didn't finish writing my explanation. My apologies!
You need to store spawned enemies somewhere. Suppose it's a list:
enemies = []
...
# every 10 secs
enemy = Enemy()
enemies.add(enemy)
Then, when enemy is killed, you'd like to remove it from that list.
look into a module called time
you would need to do something like this:
import time
nSeconds = 11
t0 = time.clock()
dt = 0
enemyCount = 0
# the following would be in an appropriate
# place in the main game loop
if dt < nSeconds:
t1 = time.clock()
dt = t1 - t0
else:
enemyInstance = Enemy()
enemyCount += 1
t0 = time.clock()
dt = 0
you can keep track of the enemy count just in case you want to send a special wave after a number of enemies or give bonus points or whatever.
also have a look at this book about game development in python for other ideas: http://inventwithpython.com/makinggames.pdf
In a game usually there is a main game loop, where inputs are read, the simulation is advanced and the screen is rendered. In that loop, you can check how much time has passed since the last time a enemy was created, and you can create one if enough time flew by.
# before mainloop
enemies = []
time_for_next_enemy = pygame.time.get_ticks() + 10000 # 10 seconds , 1000ms = 1s
# in mainloop
if pygame.time.get_ticks() >= time_for_next_enemy:
enemy = Enemy()
enemies.add(enemy)
time_for_next_enemy = pygame.time.get_ticks() + 10000
I'm building a menu using pygame and I want to make it navigable using a specific gamepad. Ideally I want to be able to press and hold *down" on the D-pad repeatedly, or get something like on a keyboard where the first button press has a delay before repeatedly entering the same character (seemingly).
I'm trying to emulate the pygame.key.set_repeat(...) function for a Joystick. my approach so far has been
pygame.time.set_timer(pygame.USEREVENT, 10)
DELAY_TIME = 0.250 #ms
y_delay = True
while not done:
for event in pygame.event.get():
y_axis = gamepad.get_axis(1)
if y_axis > 0.5: # pushing down
main_menu.move_down()
redraw() #redraw everything on the surface before sleeping
if y_delay:
time.sleep(DELAY_TIME)
y_delay = False #don't delay the next time the y axis is used
elif y_axis < -0.5: #pushing up
# repetitive I know, but I'm still working on it
main_menu.move_up()
redraw()
if y_delay:
time.sleep(DELAY_TIME)
y_delay = False
else:
y_delay = True # delay the next time
my issue is if someone taps up or down faster than DELAY_TIME they are limited to the DELAY_TIME before they can move again. Also if someone releases and depresses the up/down button within the time.sleep interval, python never sees that it was released at all and doesn't allow for a delay.
Maybe there's a way to do this using events or mapping the joystick to keys somehow? qjoypad doesn't cut it for me, and joy2keys is trash. I would need to do the mapping within the python program.
Sleep causes the program to halt execution, so it's not a viable option. You can also do this without using set_timer and events. I did it using a couple of flags and pygame.time's get_ticks.
import pygame
from pygame.locals import *
def main():
pygame.init()
pygame.display.set_mode((480, 360))
gamepad = pygame.joystick.Joystick(0)
gamepad.init()
delay = 1000
neutral = True
pressed = 0
last_update = pygame.time.get_ticks()
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
move = False
if gamepad.get_axis(1) == 0:
neutral = True
pressed = 0
else:
if neutral:
move = True
neutral = False
else:
pressed += pygame.time.get_ticks() - last_update
if pressed > delay:
move = True
pressed -= delay
if move:
print "move"
last_update = pygame.time.get_ticks()
if __name__ == "__main__":
main()
pygame.quit()
When get_axis indicates no motion, the neutral flag is set, and the pressed timer is reset, causing the move flag to remain unset. When the neutral flag is unset, if it's newly set, the move flag is set. If it's not newly set, the pressed timer increases, and move is set only if the pressed timer is greater than delay.