I know this is already technically "asked" on this forums but this question is based around a different concept that I couldn't find.
While using time.sleep(whatever) it obviously sleeps, I get that but while using Pygame it will lock up the program. Is there any real method of using a sleep or a pause in the code other than an input that doesn't lock up pygame? I've tried;
time.sleep
pygame.wait and
pygame.delay
these all do the exact same thing. I'm working on a game for a Computer Science class that involves a small animation of 13 photos I have that are slightly different, but when played 0.12seconds apart, it makes it look good, sadly the whole freezing up of the window from wait statements makes it skip and look very bad.
Thanks to whoever can figure out this mystery.
I think you may want to try using the method that is shown here.
an example of what I mean is this:
class Unit():
def __init__(self):
self.last = pygame.time.get_ticks()
self.cooldown = 300
def fire(self):
# fire gun, only if cooldown has been 0.3 seconds since last
now = pygame.time.get_ticks()
if now - self.last >= self.cooldown:
self.last = now
spawn_bullet()
notice he uses pygame.time.get_ticks() to check if a variable is less than that and if it is, he passes in the if statement and spawns the bullet.
a simpler way to view this concept is the following
curtime = pygame.time.get_ticks() + 10 # the 10 is what we add to make it sleep
while True: # Create a loop that checks its value each step
if curtime < pygame.time.get_ticks(): # check if curtime is less than get_ticks()
print("Sleep is over now!")
break # exit the loop
I hope that helps you in any way, it seems like it might be a viable option since it keeps freezing when you use normal functions.
ONE MORE THING
do note that pygame.time.get_ticks() will give you the current time in milliseconds, for full documentation on this go here.
Related
This is my code currently.
def attack(self, surface, target):
attacking_rect = pygame.Rect(self.rect.centerx - (2*self.rect.width*self.flip), self.rect.y, 2*self.rect.width, self.rect.height)
if attacking_rect.colliderect(target.rect) and self.char_type == 'Player':
time.sleep(.9) #wait for animation to finish
target.health -= random.randint(7, 15)
target.hit = True
elif attacking_rect.colliderect(target.rect) and self.char_type == 'Skeleton':
time.sleep(1.4) #wait for animation to finish
target.health -= random.randint(6, 10)
target.hit = True
Doing my first pygame project this is another issue I ran into. The issue here is that time pauses my whole program whereas I only want to pause this function from moving to the next line.
You can consider multithreading,.
Why don't you open a new thread for every time you run attack(self, surface, target), and then pause just the thread instead of making your whole program hang?
Check out the official documentation for the threading library as well as this guide for more information on how to actually use threading.
It is correct behaviour and it will freeze your whole application because there is only one main thread that is handling your code. In this case the only way will be as Ryan suggested, will be using the multithreading
So I am trying to make a game, in this game I call upon a function that I want to slowly execute, but when I use "time.sleep(x)" it pauses everything in the file instead of just pausing the process of the function. I am trying to add a jump feature to a 2-d game, so if there is a better way to do it then I would be grateful for any advice but this is just the first idea that game to me.
for n in range(15):
Dino.rect.bottom -= 5
update_screen(Dino, screen, cactus)
time.sleep(0.01)
time.sleep(0.25)
inair = False
for n in range(15):
Dino.rect.bottom += 5
update_screen(Dino, screen, cactus)
time.sleep(0.01)
so I have it so that when I jump, it gives me a slow jump instead of just teleporting but like I said, it pauses everything while jumping.
This is not a good approach to timing. As you say, this sleeps the entire program. Multi-threading is a bit complex for simply moving a sprite.
A simple way to solve this problem is to use the PyGame function time.get_ticks() which returns an ever-increasing time in milliseconds.
Using this time-stamp, record the previous-time of an operation, but then do not update again until enough time has elapsed.
For example:
DINO_UPDATE_DELAY = 100 # milliseconds
next_dino_update = 0 # time next move due
[...]
# Move dino, if necessary
time_now = pygame.time.get_ticks()
if ( time_now > next_dino_update ):
Dino.rect.bottom += 5
next_dino_update = time_now + DINO_UPDATE_DELAY # in the future
# Paint the dino, wherever it is
update_screen(Dino, screen, cactus)
It's also possible to request a timer to send the event-loop a message in the future.
MOVE_DINO_EVENT = pygame.USEREVENT + 1
[...]
pygame.time.set_timer( MOVE_DINO_EVENT, DINO_UPDATE_DELAY )
EDIT: More in-depth explanation.
So basically you're implementing an animation, like an anime/cartoon. The thing on the display moves at some speed. In the above code, you seem to be moving a "dino" in the direction of y + 5 every 0.01 seconds (10 milliseconds).
Right now, you paint the dino, then sleep(), then move & paint again. When it hits the apogee of the jump, you wait 250 milliseconds, then repeat the previous phase for the down-jump.
So the problem with using sleep() is that it holds up the whole program (including the event loop, which can cause bad stuff to happen).
So instead of sleeping for some time period, the above example simply looks at the PyGame clock to determine if that same time-period has past. This allows the program to know if the animation needs to be updated.
The whole idea is to keep looking at the clock, using the time as the arbiter of what/where to draw next, rather than multiple calls to sleep().
Is there a library or a simple way to only loop something every 0.5 seconds without interrupting the rest of the program?
I have just started using pygame and have made a simple platformer and a Pong replica so far. I decided to try and make a Snake replica (I only currently have the head) and I need the snake to only move every 0.5 seconds while inputs can be registered at the 30 fps which I have the rest of the game running at. This is my current workaround:
while running: #this is tabbed back in my code
# keep loop running at the right speed
clock.tick(FPS)
# get time at each iteration
currentTime = str(time.time()).split(".")[0]
gameTime = int (currentTime) - int (startTime)
# this is used to check for something every 0.5 second (500 ms)
currentTimeMs = str(time.time()).split(".")[1]
# snake will move evry 0.5 second in a direction
if currentTimeMs[0] in ["5","0"] and moveDone == False:
moveDone = True
player1.move(direction)
elif currentTimeMs[0] not in ["5","0"]:
moveDone = False
There is more code within the while running: loop to get the direction and display the sprites but its not necessary for this. My current code works fine and will repeat the move function for my player1 every time that x in mm:ss:x is 0 or 5 (0.5 seconds apart) and will not repeat if it is that multiple times over a few frames.
This code needs to work within the running loop and not stop the program so time.sleep() doesn't work. I have also tried using the schedule library but it will not work as it cannot seem to allow the direction variable to change when passing it into the function.
My question therefore is; Is there a library or a shorter way to accomplish what I need?
Thanks in advance and I can message you the whole code if you need.
I suggest using pygames event mechanics and pygame.time.set_timer() (see here for docs).
You would do something like this:
pygame.time.set_timer(pygame.USEREVENT, 500)
and in the event loop look for the event type.
if event.type == pygame.USEREVENT:
If this is the only user defined event that you are using in your program you can just use USEREVENT.
When you detect the event the timer has expired and you move your snake or whatever. A new timer can be set for another 1/2 second. If you need more accuracy you can keep tabs on the time and set the timer for the right amount of time, but for you situation just setting it for 1/2 sec each time is okay.
If you need multiple timers going and need to tell them apart, you can create an event with an attribute that you can set to different values to track them. Something like this (though I have not run this particular code snippet, so there could be a typo):
my_event = pygame.event.Event(pygame.USEREVENT, {"tracker": something})
pygame.time.set_timer(my_event , 500)
You can store the moment of last move and then compare to actual time. To do it you can use perf_counter from time. Here is an example
last_move_time = perf_counter()
while running:
if perf_counter() - last_move_time > 0.5:
# move player
last_move_time = perf_counter()
One of the actions I want the agent to do needs to have a delay between every action. For context, in pygame I have the following code for shooting a bullet:
if keys[pygame.K_SPACE]:
current_time = pygame.time.get_ticks()
# ready to fire when 600 ms have passed.
if current_time - previous_time > 600:
previous_time = current_time
bullets.append([x + 25, y + 24])
I've set a timer to prevent bullet spamming, how would I construct this to work with the step() method? My other actions are moving up, down, left, right.
This is my first time creating a project with OpenAI-gym so I'm not sure what the capabilities of the toolkit are, any help would be greatly appreciated.
You can use whatever method of tracking time you like (other than pygame.time.get_ticks() I suppose), and use a similar approach as in that pygame code. You'd want to store previous_time as a member of the environment instead of just a local variable, because you want it to persist across function calls.
It's not easy to actually prevent your Reinforcement Learning agent (assuming you're using gym for RL) from selecting the fire action altogether, but you can simply implement the step() function in such a way that the agent does not do anything at all if they select the fire action too quickly.
As for measuring time, you could measure wall clock time, but then the power of your CPU is going to influence how often your agent is allowed to shoot (it might be able to shoot a new bullet every step on very old hardware, but only be allowed to shoot one bullet every 100 steps on powerful hardware), that's probably a bad idea. Instead, I'd recommend measuring "time" simply by counting the step() calls. For example, using only the code from your question above, the step() function could look like:
def step(action):
self.step_counter += 1
# other step() code here
if action == FIRE:
if self.step_counter - self.previous_time > 10: # or any other number
self.previous_time = self.step_counter
bullets.append([x + 25, y + 24])
# other step() code here
Don't forget that you'll also want to reset your newly added member variables in reset():
def reset():
self.step_counter = 0
self.previous_time = -100 # some negative number such that your agent can fire at start
# other reset() code here
I want to display a message for two seconds.
The logic that im using right now is making the code wait using pygame.time.delay(2000) after pygame.display.flip.
A short example: (I use this flip-delay on my code a lot)
write_gui("{0} has appeared".format(monster.name), BLUE, 24, TEXT_CORNER_X, TEXT_CORNER_Y)
pygame.display.flip()
pygame.time.delay(2000)
This does work but it tends to "hang" the entire process, so when this happens I get some bugs because of this, mainly some frame loss because the program can keep up with the sleep-awake cycle.
So what I'm thinking right now is to draw the same frame for two seconds.
So what do you guys recommend I should do?
Because one of my answers was to put every flip on a while loop, so there has to be a better line-conservative approach to solve this.
your program "hangs" because you are not calling pygame.event.get() when you are sleeping, pygame.event.get()lets pygame handle its internal events.
The simplest way to solve is to use the return value from dt = clock.tick(), in this case dtwill be the time since your last call to clock.tick(), this value will be in milliseconds.
You could then use that value to increment a counter on how long to show the message.
You could write a function like this if you wanted and call it with how long to wait:
def waitFor(waitTime): # waitTime in milliseconds
screenCopy = screen.copy()
waitCount = 0
while waitCount < waitTime:
dt = clock.tick(60) # 60 is your FPS here
waitCount += dt
pygame.event.pump() # Tells pygame to handle it's event, instead of pygame.event.get()
screen.blit(screenCopy, (0,0))
pygame.display.flip()
This function will wait for the specified time and keep the screen as it was before the call.