How to create timers in pygame efficiently? [duplicate] - python

This question already has answers here:
Spawning multiple instances of the same object concurrently in python
(1 answer)
How do I use a PyGame timer event? How to add a clock to a pygame screen using a timer?
(1 answer)
Closed 2 years ago.
I want to spawn an enemy every (some number) seconds, say 5.
I could do:
start_time = pygame.time.get_ticks()
if pygame.time.get_ticks() - start_time >= (some number):
spawn_enemy()
But there's one problem with that: when I change the FPS (clock.tick()) from 120 to say 60 then the enemy spawn rate will remain the same.
I could also just make a variable:
var = 0
while True:
var += 1
if var >= (some number):
spawn_enemy()
But that seems like bad practice to me.

pygame.time.get_ticks() measures the time. It doesn't relay on the frames per second.
You can define a time span. When the time span is exceeded then spawn an enemy and increment the time:
next_enemy_time = 0
run = True
while run:
# [...]
if pygame.time.get_ticks() > next_enemy_time:
next_enemy_time += 5000 # 5 seconds
spawn_enemy()
# [...]
Alternatively you can use a timer event. Use pygame.time.set_timer() to repeatedly create an USEREVENT. e.g.:
time_delay = 5000 # 5 seconds
timer_event = pygame.USEREVENT + 1
pygame.time.set_timer(timer_event, time_delay)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == timer_event:
spawn_enemy()
# [...]

Related

How to limit random.choice() to 1 per x seconds? [duplicate]

This question already has an answer here:
Spawning multiple instances of the same object concurrently in python
(1 answer)
Closed 1 year ago.
I'm new to programming and I need a way to limit the randomization of the variable food_choice so it doesn't print tons of images on my pygame screen.
Here is a section of my code. I believe the problem is because my function draw_food() is inside of the main loop, meaning that everytime the game runs with the determined tick, the function draw_food() is then run again, picking another food, causing an endless randomization. I need to way to limit this randomization to once per x seconds.
def draw_food():
food_choice = random.choice(food_types)
for object in object_list[:]:
screen.blit(food_choice, (foodx, foody))
object.deter -= 0.035
if object.deter < 1:
object_list.remove(object)
def draw_game_window():
screen.blit(bg, (0, 0))
draw_food()
cat.draw(screen)
pygame.display.update()
# main loop
cat = player(200, 355, 67, 65)
run = True
object_list = []
time_interval = 5000 # 200 milliseconds == 0.2 seconds
next_object_time = 0
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
draw_game_window()
current_time = pygame.time.get_ticks()
if current_time > next_object_time:
next_object_time += time_interval
object_list.append(Object())
pygame.quit()```
You can create a randomizeFood event every second like this.
randomizeFood = pygame.USEREVENT + 0
pygame.time.set_timer(randomizeFood, 1000) #time in ms
Then in your event loop, create random food when this event is generated.
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == randomizeFood:
food_choice = random.choice(food_types)
Eiter use a timer event as already explained in an answer, or use pygame.time.get_ticks. This function returns the number of milliseconds since the application was started.
Use global variables to store the time and the food. You have to use the global statement to change a global variable within a function
food_choice = None
next_choice_time = 0
def draw_food():
current_time = pygame.time.get_ticks()
if current_time >= next_choice_time:
next_choice_time = current_time + 1000 # in 1 second (1000 millisecodns)
food_choice = random.choice(food_types)

How can I make it so that something happens in pygame when I type a number [duplicate]

This question already has answers here:
How to get keyboard input in pygame?
(11 answers)
Closed 1 year ago.
My code is:
keys = pygame.key.get_pressed()
if keys[pygame.K_1]:
if money >= worker_cost:
money -= worker_cost
workers += 1
worker_cost = round(worker_cost*1.1, 2)
So I am trying to make a game in pygame, but when I put this in to detect when the user wants to buy something, it won't detect that I pressed anything. I have tried putting print commands inside the if loop to see if the loop starts, but it doesn't. Any tips would be appreciated
Use the keyboard events instead of pygame.key.get_pressed().
pygame.key.get_pressed() returns a list with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a key and get continuous movement.
The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action:
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_1:
# do action
if money >= worker_cost:
money -= worker_cost
workers += 1
worker_cost = round(worker_cost*1.1, 2)
# [...]

pygame.midi High CPU usage

While using pygame.midi, Python consumes 20-25% of my CPU.
I guess it's because of the "While" loop which waits for MIDI input...
Any ideas? I'd appreciate any advice you might have...
Here is the loop:
going = True
while going:
events = event_get()
for e in events:
if e.type in [pg.QUIT]:
going = False
if e.type in [pg.KEYDOWN]:
going = False
if e.type in [pygame.midi.MIDIIN]:
if e.data2 == 127:
shortcuts(e.data1)
if i.poll():
midi_events = i.read(10)
# convert them into pygame events.
midi_evs = pygame.midi.midis2events(midi_events, i.device_id)
for m_e in midi_evs:
event_post(m_e)
You can limit the CPU usage with pygame.time.Clock.tick:
clock = pygame.time.Clock()
going = True
while going:
clock.tick(60)
# [...]
The method tick() of a pygame.time.Clock object, delays the game in that way, that every iteration of the loop consumes the same period of time.

Pygame + python: 1 part of code has pygame.wait while rest of code runs

I am making a game in which u have to carry move objects from one place to another. I can move my character to the zone in which I need to put something. I want the player to wait in the zone for 5 secs before the object is placed there, however, if i do this you cannot move anymore if u decide u dont want to place the object in the zone as the whole script would be paused.
Is there a way to make one part of the script wait while the rest of it runs?
Every game needs one clock to keep the game loop in sync and to control timing. Pygame has a pygame.time.Clock object with a tick() method. Here's what a game loop could look like to get the behaviour you want (not complete code, just an example).
clock = pygame.time.Clock()
wait_time = 0
have_visited_zone = False
waiting_for_block_placement = False
# Game loop.
while True:
# Get the time (in milliseconds) since last loop (and lock framerate at 60 FPS).
dt = clock.tick(60)
# Move the player.
player.position += player.velocity * dt
# Player enters the zone for the first time.
if player.rect.colliderect(zone.rect) and not have_visited_zone:
have_visited_zone = True # Remember to set this to True!
waiting_for_block_placement = True # We're now waiting.
wait_time = 5000 # We'll wait 5000 milliseconds.
# Check if we're currently waiting for the block-placing action.
if waiting_for_block_placement:
wait_time -= dt # Decrease the time if we're waiting.
if wait_time <= 0: # If the time has gone to 0 (or past 0)
waiting_for_block_placement = False # stop waiting
place_block() # and place the block.
Example with threading:
from threading import Thread
def threaded_function(arg):
# check if it's been 5 seconds or user has left
thread = Thread(target = threaded_function, args = (10, ))
if user is in zone:
thread.start()
# continue normal code
Another potential solution is to check the time the user went into the zone and continuously check the current time to see if it's been 5 seconds
Time check example:
import time
entered = false
while true:
if user has entered zone:
entered_time = time.time()
entered = true
if entered and time.time() - entered_time >= 5: # i believe time.time() is in seconds not milliseconds
# it has been 5 seconds
if user has left:
entered=false
#other game code

How do you delay specific events in a while loop?

Recently I've been working with a simple and straightforward RPG in python with pygame, but I'm having some problems delaying specific events. Running the code below, everything happens at once.
if event.key == pygame.K_SPACE and buttonHighlight == 0:
FireAnimation() #displays a fire image
#DELAY HERE
if player[6] == 'Magic': #you deal damage to the enemy
enemy[0] = enemy[0]-(((player[1])+((player[1])*1)-enemy[4]))
else:
enemy[0] = enemy[0]-(((player[1])+((player[1])*1)-enemy[3]))
#DELAY HERE
StarAnimation() #displays a star image
#DELAY HERE
if enemy[6] == 'Magic': #enemy deals damage to you
player[0] = player[0]-(((enemy[1])+((enemy[1])*1)-player[4]))
else:
player[0] = player[0]-(((enemy[1])+((enemy[1])*1)-player[3]))
The rest of the code isn't really relevant, I just wanted to point out where I want to delay. Running this, both images displays, the player and the enemy takes damage at the same time. Thanks!
EDIT: I forgot to mention that I already have tried pygame.time.delay/wait and time.sleep, but all those delays the whole operation! It simply pushes everything forward when I use it, so everything happens at the same time several seconds later
You can create two new events (FIRE_ANIMATION_START, STAR_ANIMATION_START) which you post to the event queue with a delay (with pygame.time.set_timer(eventid, milliseconds)). Then in your event loop you just check for it.
FIRE_ANIMATION_START = pygame.USEREVENT + 1
STAR_ANIMATION_START = pygame.USEREVENT + 2
# ... Your code ...
for event in pygame.event.get():
if event.key == pygame.K_SPACE and buttonHighlight == 0:
pygame.time.set_timer(FIRE_ANIMATION_START, 10) # Post the event every 10 ms.
pygame.time.set_timer(STAR_ANIMATION_START, 1000) # Post the event every 1000 ms.
elif event.code == FIRE_ANIMATION_START:
pygame.time.set_timer(FIRE_ANIMATION_START, 0) # Don't post the event anymore.
FireAnimation() #displays a fire image
if player[6] == 'Magic': #you deal damage to the enemy
enemy[0] = enemy[0]-(((player[1])+((player[1])*1)-enemy[4]))
else:
enemy[0] = enemy[0]-(((player[1])+((player[1])*1)-enemy[3]))
elif event.code == STAR_ANIMATION_START:
pygame.time.set_timer(STAR_ANIMATION_START, 0) # Don't post the event anymore.
StarAnimation() #displays a star image
if enemy[6] == 'Magic': #enemy deals damage to you
player[0] = player[0]-(((enemy[1])+((enemy[1])*1)-player[4]))
else:
player[0] = player[0]-(((enemy[1])+((enemy[1])*1)-player[3]))
Documentation for pygame.time.set_timer(eventid, milliseconds). Also, as the code is right now it has bugs in it. The attributes for the events differs between different event types, so always make sure to check whether an event is KEYDOWN or USEREVENT before accessing the attributes event.key or event.code. The different types and attributes can be found here.
If you know how much time you need, you can simply add:
from time import sleep
...
sleep(0.1)
This will add a 100 milliseconds delay
You can use
pygame.time.delay(n)
or
pygame.time.wait(n)
to pause the program for n milliseconds. delay is a little more accurate but wait frees the processor for other programs to use while pygame is waiting. More details in pygame docs.

Categories

Resources