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
Related
I'm new to python, but I'm trying to learn it by myself for a university course.
Therefore I have to program a pygame window that represents a drone fly zone (The drone should fly automated like a search helicopter in a given area and on the window a visual representation of what happens should be displayed...).
How an older version of the pygame window look's like when the virtual drone flies.
So far so good.
Now I have to link the movements of the game-represented drone with the real Tello drone.
I figured out that using threading for the movement commands to the drone is the way to go when the pygame window should run the whole time.
But now I got stuck because I have to delay the automated movements in pygame in order to wait for the drone flight time and response.
I update the pygame window with a clock set to tick(60) in the loop of the game (therefore I created a Clock clock = pygame.time.Clock() in the initialization of the pygame program), but for the automated drone movements I created an Userevent.
The drone movements is trigger like a Snake game via a Userevent witch should be triggered every second.
Therefor I implemented a timer:
self.SCREEN_UPDATE = pygame.USEREVENT
self.screen_update = pygame.time.set_timer(self.SCREEN_UPDATE, 1000).
But now I have to delay the automated movements in pygame every time the drone should do a movement. Otherwise, the pygame program run's too fast for the real-world problem.
(The User event gets starter again before the real drone has finished his command)
I also tried to implement break functions for that, but they do not work.
def break_rotation(self):
print("start waiting")
self.screen_update.time.wait(2000)
#self.screen_update = pygame.time.set_timer(self.SCREEN_UPDATE, 2000)
#pygame.time.wait(2000)
print("end waiting")``
def break_move(self):
print("start waiting")
self.screen_update.time.wait(3000)
#self.screen_update = pygame.time.set_timer(self.SCREEN_UPDATE, 3000)
#pygame.time.wait(3000)
print("end waiting")
I also tried the comment lines. pygame.time.wait()makes the whole pygame wait. But You should still be able to use your keyboard for emergency landings and other stuff.
So I only have to delay the next Userevent, where the hole flypath for the next block / section get's tested for the automated flypath and the program decides to fly forward or should it turn.
Does anyone have an Idear how I could achieve that delay on the timer?
Sadly I also couldn't find some similar problems.
EDIT 1:
#CmdCoder858
I think this code may hold the update back for a couple of seconds.
When using the and operator combined with a passed variable.
if event.type == SCREEN_UPDATE and x == 1:
I also tried using multiple events but that didn't work probably.
if event.type == SCREEN_UPDATE and event.type == drone_task_finished:
Interestingly it ignored the hole and operator this way.
import pygame
import threading
import _thread
import time
def task():
global x
# placeholder for drone task
print('Test thread start')
time.sleep(5) # simulate delay in the task
x = x+1
print('Thread ends')
pygame.event.post(pygame.event.Event(drone_task_finished))
if __name__ == '__main__':
pygame.init()
x = 0
drone_task_finished = pygame.USEREVENT # an integer representing the first user event
drone_task = threading.Thread(target=task) # create a thread with a task function
drone_task.start()
pygame.display.set_caption("Overview of the Search Area")
window = pygame.display.set_mode((500, 300))
SCREEN_UPDATE = pygame.USEREVENT
screen_update = pygame.time.set_timer(SCREEN_UPDATE, 1000)
print('Start event loop')
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
if event.type == SCREEN_UPDATE and x == 1:
print('Hura Timer paused for 5 sec')
x = 0
drone_task = threading.Thread(target=task) # create a thread with a task function
drone_task.start()
if event.type == SCREEN_UPDATE:
print('Update nach 1sec')
if event.type == drone_task_finished: # detect if a drone_task_finished event has been posted
print('Drone task has finished')
But is there any reason not to use import _thread?
With that, I could call the function multiple times in new threads.
Therefore I have to change the task function to def task(threadName):
Now I could simply use _thread.start_new_thread(task, ("",)) every time the task should be used later on.
From what is sounds like, I would guess you want to wait for an event in another thread whilst keeping the main loop running, in which case I think you should try using pygame.event.post() function. This function is apparently thread safe so as long as it is called from a thread that was started after pygame.init() it should definitely work. You can create a custom event using the pygame.event.custom_type() function, meaning that you can wait for the event to be returned without having to pause the display. Comment below if you have any questions.
Edit:
Adding a code example for clarity:
import pygame
import threading
import time
def task():
# placeholder for drone task
time.sleep(3) # simulate delay in the task
pygame.event.post(pygame.event.Event(drone_task_finished))
if __name__ == '__main__':
pygame.init()
drone_task_finished = pygame.USEREVENT # an integer representing the first user event
drone_task = threading.Thread(target=task) # create a thread with a task function
drone_task.start()
print('Start event loop')
while 1:
for event in pygame.event.get():
if event.type == drone_task_finished: # detect if a drone_task_finished event has been posted
print('Drone task has finished')
Essentially what is happening here is we are creating a task to be completed, then starting the task and continuing with the event loop. Once the task has finished, it will use the pygame.event.post function to tell the main thread that its task has been completed. The main thread can then detect this and proceed accordingly.
Edit 2:
To answer your questions, firstly don't worry about creating an answer to your own question, just editing your original question and mentioning that you have done so in a comment is perfectly fine.
As for using the variable x as a way to delay the execution of another event, this can work but I would recommend keeping x out of the threaded task function, and instead modify it when your main thread detects the drone_task_finished event. This is much better because you don't have to deal with cross-thread variables, so just put that under the if event.type == drone_task_finished: block.
As for using multiple custom events, the reason your code event.type == SCREEN_UPDATE and event.type == drone_task_finished returned True is because that statement is correct, and this is due to how you initialised each event type. You essentially used this code:
drone_task_finished = pygame.USEREVENT
SCREEN_UPDATE = pygame.USEREVENT
However the actual way this should be done is like this:
drone_task_finished = pygame.USEREVENT
SCREEN_UPDATE = pygame.USEREVENT + 1
This is due to how pygame handles event types. Each type is given an integer value, and the built in types get the values up until a certain point. The pygame.USEREVENT constant is an integer representing the first number that is not reserved for other events. This means that creating a new variable to represent an event in pygame should be the integer pygame.USEREVENT, and the second would have the value pygame.USEREVENT + 1. All of this is explained in more detail here.
Finally, as explained in this answer, _thread is an implementation module that should generally be avoided in favour of the threading module, which is what I used in my code. With the threading module, you can create and start a new thread using this code:
drone_task = threading.Thread(target=task)
drone_task.start()
The threading.Thread class also has an optional arguments parameter, meaning it is easy to pass in all the arguments you need to the new thread. You can find more information on how this all works in the documentation.
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()
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')
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!
I'm making a simple game using Pygame where you have to get (by moving the cursor over them) all the circles that will appear on the screen (more will appear every second). The code is quite long so I made an example code. This code works fine, the Pygame window doesn't become unresponsive at all:
import pygame, random, sys
pygame.init()
window=pygame.display.set_mode((480,360))
end_program=False
while not end_program:
for event in pygame.event.get():
if event.type==pygame.QUIT or pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
end_program=True
pass
pygame.quit()
sys.exit()
However, in my game, in order to give the user the choice to play again, I need to wrap everything inside end_program in another loop. In the example shown, this is break_from_second_loop:
import pygame, random, sys
pygame.init()
window=pygame.display.set_mode((480,360))
end_program=False
while not end_program:
for event in pygame.event.get():
if event.type==pygame.QUIT or pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
end_program=True
break_from_second_loop=False
while not break_from_second_loop:
pass
pygame.quit()
sys.exit()
Now if this is run, the window becomes unresponsive! Anyone know why something as simple as wrapping the code in another loop (without altering the code at all) does this?
The problem is that the game can't respond, or do anything at all, if you're not running the event loop. And in the other loop, you're not running the event loop.
This is a general problem with event-loop-based programming. You can't do anything that takes a long time, and you can't do anything that has to run across multiple events.
So, you have to break your loop apart into steps, and do just one step (or a few of them) each time through the event loop.
In this particular case, it's actually pretty simple: just change that while to an if (and move the has_got_all_circles=False outside the main loop), and your logic now runs exactly once each time through the event loop.
Alternatively, change it to an if and also move it inside the for, so now it runs exactly once per event, instead of once per event loop iteration.
A third alternative is to factor out the whole thing into a function and set it as an idle or timer function, that runs whenever the event loop is idle, or once every frame, or once every 20ms, or whatever.
It's hard to know which of the three is appropriate in your case, but the basic idea is the same in all of them, so I'll just show the second one:
end_program=False
break_from_second_loop=False
while not end_program:
for event in pygame.event.get():
if event.type==pygame.QUIT or pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
end_program=True
if not break_from_second_loop:
pass
This blog post explains the general problem in more detail—although most of it isn't really appropriate to this specific problem.
The issue you're having is that you're not nesting your event loop code within the while loop that does your game logic. Here's the general structure of what you want:
while not end_program:
while not end_game:
handle_events()
do_one_frame_of_game_logic()
offer_another_game()
It's possible that offer_another_game will also need to be run in its own loop, with its own event handling code.
Indeed, you might want to encapsulate the logic you want to use into a state machine system. You'd have states like PlayingGame, GameOver, and DoYouWantToPlayAgain, each of which would run for a while, then hand off to another state. Your main loop would then be something like:
state = StartState()
while state:
state.handle_events()
state.update()
state.draw()
state = state.next_state() # most of the time, the state will return itself