Every pygame has a game loop that looks like this:
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.flip()
print("tick " + str(pygame.time.get_ticks()))
clock.tick(1)
According to the api forget_ticks():
Returns the number of millisconds since pygame.init() was called.
Before pygame is initialized this will always be 0.
But clock.tick() :
This method should be called once per frame. It will compute how many . milliseconds have passed since the previous call.
If you pass the optional framerate argument the function will delay to keep the game running slower than the given ticks per second. This can be used to help limit the runtime speed of a game. By calling Clock.tick(40) once per frame, the program will never run at more than 40 frames per second.
I'm a bit confused, does it mean that clock.tick() directly affects how many milliseconds have passed since the start of the game?
So clock.tick(40) means I "issue" 40 frames per second and the while loop runs 40 times per second?
I don't see the relation between fps and ticks.
UPDATE:
I actually just tested it and get_ticks() still returns REAL time in mls no matter what fps you give to tick() - 0.1 or 30 or 60.
So it seems clock.tick() just sets up how fast game should run or how often while loop should update itself, run through itself.
However I m still a bit confused, other answers are welcome.
FPS, Frames Per Second, is the number of frames shown per unit of time.
1 / FPS is the amount of time should pass between each frame.
Tick is just a measure of time in PyGame.
clock.tick(40) means that for every second at most 40 frames should pass.
I set up high fps - clock.tick(30) or 60,
and game runs fast, and get_ticks() prints out elapsed time very fast however actual runtime from pygame.init() did not change!
I thought time runs faster because of high FPS! It does not, I tried clock.tick(0.1) - aka 1 frame per 10 seconds, and get_ticks() printed out its elapsed time only ONCE PER 10 seconds! Because the while loop was running through itself at fps = 0.1.
But if fps was higher, the update rate would be higher -not the total elapsed time
Now I figured it out.
I know it is already answered but I wanted to explain something I tried:
import pygame
pygame.init()
gameDisplay = pygame.display.set_mode((800,600))
clock = pygame.time.Clock()
crashed = False
counter = 1
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
pygame.display.update()
print(counter)
counter += 1
clock.tick(1) # will be 10 in the next run
So what we will do is make two runs — one where frame per second (fps) equals 1 and the other where fps equals 10. And we will run the code for 10 seconds. I used my phone stopwatch to do this.
So, mathematically 1 fps for 10sec is 10 right and 10 fps in 10sec is 100, clearly.
So, running the first run, 1 fps, the counter variable should be around 10, depending on your timing. For the second run, at the end of 10 seconds the counter variable in your console should be around 100.
So, in brief, we could say the loop is controlling your game display
and clock.tick() specifies how fast you want to change the game display or, in other words, how fast the loop runs.
I am a bit late, but I have an answer that I have come across based on my research. This is geared towards beginners like myself, please feel free to correct me if I am wrong.
Lets assume the following loop:
while True:
# ... Some code which deals with the objects and surfaces
pygame.display.flip() # displays all the changes we've made to the user
pygame.clock.tick(60) # code that prevents our framerate from exceeding 60 fps
Ok, considering the code above, assume pygame.clock.tick measures the time elapsed from when it was last called
So lets say it is now the 2nd iteration of this loop, and the tick method has measured that 0.0157 seconds has elapsed since it was last called in the first iteration. If we calculate the FPS for this, it would be 1 frame/0.0157 seconds (if you are confused about why it is 1 frame, it is because we have only "updated" the user's screen once since the last calling of the tick method. 1/0.0157 = 63.69 frames per second. Dang it! Thats more than 60 frames per second. Thus, the method tick will automatically "slow" down your code, preventing it from "updating" again UNTIL enough time has elapsed such that the frame rate = 60 fps.
So how will it do that? Well, using basic math we can calculate that an FPS of 60 translates to 1 frame every 0.016 seconds. So, if we make the code wait an extra 0.0003 seconds, we will achieve a frame rate of 60! This is because 0.0157 seconds has already elapsed as described in the scenario above, so we need to wait an extra 0.0003 seconds on this frame to achieve our desired frame rate.
I hope this helps anyone who is confused on this subject.
Related
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()
the ball animation while running the program is very stuttery and i can't figure out why.
this is just the beggining of the program so ignore the fact that the game isn't ready to play yet.
this is the code: https://www.codepile.net/pile/dqKZa8OG
i want the ball to move smoothly without stuttering.
and in addition how do i make it so the program deletes the last location of the ball after each update?
It's not "junky" because of the timer, you can only see updates since you're probably moving the mouse in the meantime, then you're updating the ball position everytime you move it (which is wrong, as you're updating the position anytime any events is processed).
The problem is that you're using Clock in the wrong way: the pygame.time.Clock class creates an «object that can be used to track an amount of time», meaning that it is not a timer that can "react" once it times out. The tick method you're calling only updates the current Clock returning how many milliseconds have passed since the last call to tick itself, based on the fps argument you are providing.
What you need is to set a timer, possibly by using a specific eventid just for the updates. Also, since you're updating the ball position based on the events, you'll get more movements if you move the mouse (or any other event is called) making the ball move faster even if it shouldn't - and that's what you'll need the Clock object for.
# I'm using a smaller speed, otherwise it'll be too fast; it should
# depend on the window size to ensure that bigger windows don't get
# slower speeds
ball_velocity = .2
fps = 60
UPDATEEVENT = 24
[...]
def start_move_ball(angle):
ticks = clock.tick()
speed = ball_velocity * ticks
# "global ball" is not required, as you are assigning values to an
# existing object, not declaring it everytime.
# You can also use in-place operations to set the speeds, which is
# better for readability too.
ball[0] += angle[0] * speed
ball[1] += angle[1] * speed
def main_loop():
angle = choose_angle()
pygame.time.set_timer(UPDATEEVENT, 1000 // fps)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return False
# update only if necessary!
elif event.type == UPDATEEVENT:
window.fill((0,0,0))
start_move_ball(angle)
draw_mid(window)
draw_players(window)
draw_ball(window)
pygame.display.flip()
The timer is set outside the while cycle, as it automatically sends the event each time interval. You could also leave it within the while (the once=True argument is not really required in this case, as it automatically updates the timer based on the same eventid), but wouldn't make much sense, since set_timer always fires the event after the first time it's set.
I'm using 24 (pygame.USEREVENT) as an eventid, but you can set its id up to pygame.NUMEVENTS - 1, as suggested in the event documentation. If for any reason you want to stop the timer, just use pygame.time.set_timer(eventid, 0) and the timer for that eventid (24 in this case) will not be fired up again.
A couple of suggestions, besides all:
Remove the pygame.display.update() line in draw_mid(window), since it causes flickering while painting.
Avoid using external services to link code (it's not your case, but if the code is too long, first try to reduce it to minimal, reproducible example, eventually leaving all the relevant parts), as they could become unavailable sometime in the future, making your question hard to understand to somebody that is facing a similar issue and reads your answer some time after you posted it.
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.
I am trying to learn pygame sprite animation. I have tried to follow a tutorial and everything is fine. There is just one problem that I can't get my head around.
In the below code I am trying to run a sprite animation of simples square.
This is the sprite:
https://www.dropbox.com/s/xa39gb6m3k8085c/playersprites.png
I can get it working, but the animation is too fast. I want it to be little smoother so that the fading effect can be seen. I saw some solutions using clock.tick but that would slow down the whole game I guess.
how can I get the animation slower while maintaing the usual frame rate for my window?
Below is my code:
lightblue=(0,174,255)
import pygame
pygame.init()
screen = pygame.display.set_mode((400, 300))
done = False
screen.fill(lightblue)
images=pygame.image.load('playersprites.png')
noi=16
current_image=0
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.quit()
quit()
if(current_image>noi-1):
current_image=0
else:
current_image+=1
screen.blit(images,(50,100),(current_image*32,0,32,32))
pygame.display.flip()
Use an integer value which you add to in the loop and check for divisibility. See here for more.
You could use time
So your sprite has 16 frames and lets say it wants to run at ten frames a second regardless of the frame rate of the game.
You could do something along the lines of
import time
start_frame = time.time()
noi = 16
frames_per_second = 10
Then in your loop put
current_image = int((time.time() - start_frame) * frames_per_second % noi)
time.time() counts up in seconds and has several decimal places after it. If you leave leave out frames_per_second then just use the modulo operator on noi (presumably "number of images"), every time a second has gone by, the result will tick up one until it reaches 16 and return to 0.
When you multiply the difference between start_frame and current time (time.time()), you force the "seconds" to go by frames_per_second times as fast.
Coercing the result which will be a float to an int, will allow you to use it as an index.
Now if the frame rate of the game fluctuates, it won't matter because the frame rate of the sprite is tied to the system clock. The sprites should still run at as close to exactly the chosen frame rate as can be rendered.
Clock.tick() is the correct solution.
While you may want to run the code of your game as fast as possible, you don't want the animation to run at an arbitrary speed. Animation is always "frames per second" and this value should be stable; otherwise it will look ugly or confusing. A good value is the same as the refresh rate of your monitor (usually 60 FPS) to avoid tearing.