event IDs and pygame.time.set_timer - python

i'm trying to make a simple basic program with pygame. one of the things im trying to make it do is create a randomly sized rectangle somewhere random on the screen in 6 seconds.
i'm very new to all of this, and I am also pretty new to python 2.7(I was learning 3.5 in class)
so far I have:
mob2_spawn = 0
def create_mob():
mob2 = pygame.Rect(random.randint(0,800), random.randint(0,600), random.randint(20,60), random.randint(30,50))
pygame.draw.rect(window_surface, mob_color, mob2)
pygame.display.update()
mob2_spawn = 1
if mob_spawn == 0:
pygame.time.set_timer(25,6000)
mob2_spawn = 1
how do I attach an event ID to something? I understand that the first variable in pygame.time.set_timer is the event id, and should be an integer between 25 and 32. and essentially the timer should run that function associated with that event id every X milliseconds correct

You need to check for an event, for example a button press. First you need to make pygame check for any button presses:
keystate = pygame.key.get_pressed()
Then you need to put this in your code, so that you have an event in your timer:
pygame.time.set_timer(keystate[pygame.K_SPACE], (25, 6000))
That's at least what I think should happen, by the way i'm using the example with the space button.
I hope it works for you!

Related

What is the best way to make a player move at every interval in pygame?

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()

ball animation is really junky while running pong game

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'm trying to use pygame and need and interface [duplicate]

This question already has answers here:
How can I create a text input box with Pygame?
(5 answers)
Closed 2 years ago.
I'm making a program and I'd like to run it using pygame. The key thing is that I need an space for the user to input numbers and at the screen I need to print an array, which may vary in size (always 3 columns, but the user controls the number of lines), but I want it to always been fully shown in the screen. How can I make both things?
You can't expect people to write down code from scratch to accomplish what you want. If it is just advice you need, I can give some.
Pygame has no formal input field or input box introduced. If you want to implement such thing in pygame specifically, you have to keep record of the input keys(keyboard input). And insert the inputs in a string or list, then display them yourself. But you have to implement the methodology of the inputbox you designed, like you should have a condition for backspace where it deletes the last element of the list. Pygame does not do this formally, but you can write something like this.
However, what you need seems like a gui. Which gives a better way of taking inputs, making input fields, giving properties to the program window and the input boxes, making buttons etc.. In this case, if you change your mind, I would suggest Wxpython.
If you want to create a very simple text input box on your own, you could try to define an area with a pygame.Rect, then check if the mouse collides with the rect to select it and use the .unicode attribute of the keyboard events to attach the characters to a list or string which you can display on the screen.
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
your_input_string += event.unicode
There are also some GUI libraries for Pygame like SGC which is pretty easy to use. Alternatives (on pygame.org) are Albow, PGU and OcempGUI (the latter works only with Python 2.7).
This can be done using pygame_gui. Text input is available through a UITextEntryLine instance, while printing an array could be done using a UITextBox. You'll first need to set up the environment as in the quick start guide.
Create a UITextEntryLine instance:
from pygame.rect import Rect
from pygame_gui.elements.ui_text_entry_line import UITextEntryLine
text_input = UITextEntryLine(relative_rect=Rect(0, 0, 100, 100), manager=manager)
Input can be restricted to only numbers using set_allowed_characters:
text_input.set_allowed_characters('numbers')
Adding a check to the event queue allows for getting text input when enter is pressed:
for event in pygame.event.get():
if event.type == pygame.USEREVENT:
if event.user_type == pygame_gui.UI_TEXT_ENTRY_FINISHED:
if event.ui_element == text_input:
entered_text = text
A UITextBox can be set to a custom height by setting the height to -1:
from pygame_gui.elements.ui_text_box import UITextBox
text_box = UITextBox(relative_rect=Rect(0, 0, 100, -1), manager=manager, text='foobar')

Pygame, Draw the same frame for a set amount of time

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.

Using pygame.time.set_timer

I am making a game in Pygame and Python, a form of Asteroids. Every 10 seconds, the game should make another asteroid if there isn't more than 10 on the screen at a time. As I was looking over the example code on how to implement it, I saw that I have to use USEREVENT in the code. Could I replace this with a function? For example:
def testfunc():
print "Test"
pygame.time.set_timer(testfunc, 100)
If I can't use the function this way, how would I use the USEREVENT with this code?
Thanks
EDIT: Could the Python Timer object with Pygame instead of using the Pygame set_timer()?
http://docs.python.org/2/library/threading.html#timer-objects
Looking at the documentation, it seems that events are not functions, but numeric IDs representing the type of event. Thus, you want to declare your own event type, something like this:
SPAWNASTEROID = USEREVENT + 1
Then you can queue it up like this:
pygame.time.set_timer(SPAWNASTEROID, 100)
Finally add a handler to main loop, just like for button clicks and whatnot:
elif event.type == SPAWNASTEROID:
if len(asteroids) < 10:
asteroids.append(Asteroid())
EDIT (example code):
Custom event types declared
set_timer
Checking event type

Categories