Pausing game and user input in pygame - python

I am currently making a game in Pygame, I have the basic game made as I followed a tutorial however know I want to improve it and make it so that after a certain amount of time (e.g. every 30 seconds or so) the game pauses and the user is asked a question and they must enter the right answer to continue playing the game. I have been looking on-line and experimenting a bit however struggling quite a lot! Here is the relevant code:
class question():
pressKeySurf, pressKeyRect = makeTextObjs('9+1.', BASICFONT, WHITE)
pressKeySurf, pressKeyRect = makeTextObjs('Press A for 10.', BASICFONT, WHITE)
pressKeySurf, pressKeyRect = makeTextObjs('Press B for 15.', BASICFONT, WHITE)
for event in pygame.event.get():
if (event.key==K_a):
showTextScreen('Correct answer') # pause until a key press
lastFallTime = time.time()
lastMoveDownTime = time.time()
lastMoveSidewaysTime = time.time()
else:
showTextScreen("Wrong")
pygame.time.set_timer(question,1000)
I have no idea if this is even going in the right direction at the moment as I can't get it to work because I think I understand the error and that I shouldn't be using a class in the timer, however not sure?

You want this to be a function, not a class. Replace class with def
The bigger problem is that your use of set_timer is incorrect. It is:
set_timer(eventid, timems)
where eventid and timems are both integers. set_timer puts an event into the event queue at the specified interval, it doesn't call any function. You have to put code in the main loop to check for that eventid and then execute the appropriate code.
Example code from this SO answer::
pygame.init()
pygame.time.set_timer(USEREVENT + 1, 100)
while True:
for event in pygame.event.get():
if event.type == USEREVENT + 1:
functionName()
if event.type == QUIT:
pygame.quite()
sys.exit()

Related

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.

Wait for keydown before continuing in Pygame

I want my program to cycle through each player, and this code does that. However, on the last player, it displays the info then instantly clears it. I want it to wait for a user to press a keydown (like space or enter) before clearing the screen. I tried implementing this with event = pygame.event.wait() but now my program just hangs when it reaches that declaration.
players = {}
for player in range(1, int(num_players)+1):
name = ask(DISPLAYSURF, "Player " + str(player) + "'s name")
player_roll = None
while player_roll is None:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
pygame.event.clear()
while event.type != pygame.KEYDOWN:
event = pygame.event.wait()
DISPLAYSURF.fill(WHITE)
FIRST_DICE = roll_a_dice()
SECOND_DICE = roll_a_dice()
player_roll = FIRST_DICE + SECOND_DICE
players[name] = player_roll
display_dice(FIRST_DICE, SECOND_DICE)
our_roll(name)
My full code is here: https://github.com/Legitimate/Street-Dice/blob/master/main.py
Here is a video of the issue: https://youtu.be/ChxZq0bY4wk
It took a few minutes to understand what you meant, but after refactoring the list of players as the code below shows, the rest sort of rolled itself: https://github.cm/rebelclause/python_legs/blob/master/pygame/roll_the_dice.py. If it works for you, buy me a beer ;)
players = {'you': {'rolltotal': None, 'otherstuff': None }, 'me': {'rolltotal': None, 'otherstuff': None}}
def reviewvals():
print('Number of players: ', len(players)) # only counts the primary keys, the players
for player, attribdict in players.items():
for key, value in attribdict.items():
print(player, key, value)
reviewvals()
I haven't used Pygame before so I don't know if there is more efficient/ or right way of doing what you have asked for but anyway try this
Check this out
Pygame waiting the user to keypress a key
Also from docs -- this is why your program doesn't respond( which seem like it got hanged/ stuck but its not)..when it reaches event.wait()
https://www.pygame.org/docs/ref/event.html#comment_pygame_event_wait
pygame.event.wait()
wait for a single event from the queue
wait() -> EventType instance
Returns a single event from the queue. If the queue is empty this function will wait until one is created. The event is removed from the queue once it has been returned. While the program is waiting it will sleep in an idle state. This is important for programs that want to share the system with other applications.

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.

Python getting back to the first function

I am doing a Photobooth with printing (and uploading) option.
This project presents itself like this:
Start screen with instructions --> preview results screen --> user chooses either to print or restart (or the timeout chooses restart for the user)
If the user chooses print then, printing is done, and a message is displayed (sleep method) before going back to the start screen.
Now, i have one main issue:
Getting back to the start screen...
The simplified code is here:
def PreviewMontage(MontageFile):
global LastTap
LastTap = time.time()
print("Session ID:", SessionID)
print("Show something.")
preview = pygame.image.load(MontageFile)
PILpreview = Image.open(MontageFile)
previewSize = PILpreview.size # returns (width, height) tuple
#added /1.5
ScaleW = AspectRatioCalc(previewSize[0]/1.5, previewSize[1]/1.5, SCREEN_HEIGHT)
preview = pygame.transform.scale(preview, (ScaleW, SCREEN_HEIGHT))
SetBlankScreen()
background.blit(preview, (SCREEN_WIDTH/2-ScaleW/2, 0))
PrintScreen()
#inserting conditions here - get mouse
camera.stop_preview()
UpdateDisplay()
Wait()
return
def Wait():
clock = pygame.time.Clock()
waiting = True
while waiting:
time = 60
time = time -1
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFTMOUSEBUTTON:
x, y = event.pos
print("You pressed the left mouse button at (%d, %d)" % event.pos)
LeftMouseButtonDown(x, y)
if time == 0:
waiting = False
return
I encounter the problem of getting back to the main screen, it seems that the Wait() function never ends...
You are setting the time to 59 on every iteration of your while loop. It means time never reaches 0 and the loop is infinite.
Fix it by declaring time = 60 outside the while()

How to get an input from user in Pygame and save it as a variable? [duplicate]

This question already has an answer here:
How to get text input from user in Pygame? [duplicate]
(1 answer)
Closed 5 years ago.
I want to take input from the user in the game (e.g. their name), then put it on the screen.
I tried modules (including InputBox), but none of them are working. They just displayed my text on screen.
I want to save that input to a variable. Is there any way to do this?
Example:
font1 = pygame.font.SysFont("None", 30)
score = 0
text = font1.render("{}".format(score), True,(255,255,255))
...
...
if sneakeattheapple:
score += 1
text = font1.render("{}".format(score), True,(255,255,255))
...
...
screen.blit(text,(275,6))
This is going to put the score variable on the screen. But score is already defined, I want to do this with a variable given by user.
EDIT: Let me be more clear. In Python we can do this:
x = input("Do you want to do this? (y/n): ")
if x == "y":
#do something
if x == "n":
#do something
This is what I want to do in Pygame.
There's nothing baked into Pygame for this. You will either need to use a 3rd-party GUI library, or build it yourself. Example: if the textbox has focus, take all keydown events, and append to a string. Each frame, draw a box, then draw the string on top.
Building a simple one shouldn't be that hard, but if you want a more full-featured one, it will likely be easier to use a library.
I am currently assuming this function worked with your program is successful and raises no errors. Here is a function in the link you gave us:
def ask(screen, question):
"ask(screen, question) -> answer"
pygame.font.init()
current_string = []
display_box(screen, question + ": " + string.join(current_string,""))
while 1:
inkey = get_key()
if inkey == K_BACKSPACE:
current_string = current_string[0:-1]
elif inkey == K_RETURN:
break
elif inkey == K_MINUS:
current_string.append("_")
elif inkey <= 127:
current_string.append(chr(inkey))
display_box(screen, question + ": " + string.join(current_string,""))
return string.join(current_string,"")
It looks like this is how you get input from a user with a pygame screen right? Let's look at line 4 of this function:
current_string = []
The stuff the user types in is stored in this list. Assuming you know how to take a string and put it on the screen, you could save the string like this:
string_input = ''.join(current_string)
If you can make a similar function (if this one doesn't work), you can do the same thing! Just save the first item in the list holding the string in a variable as shown above. If you have any problems, please comment so I can edit my answer. To the next part now. You can simply activate this function at any time. You might want to activate when something happens. An example is when the snake eats the apple. You probaly have a function for that I believe. You can make a variable like this :
Eat = 0
And put in that function. When that variable is equal to 0. Nothing really activate the other function. When the snake eats the apple, reset the variable to 1 and then activate the function like this:
if Eat = 0:
someclassname.ask()
You do this with many other occasions. I hope this is clearer and more helpful!
Have you tried getting key event's and then putting them together to form something? You could put something like this inside of your main game loop.
input = ''
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
playing = False
if event.key == pygame.K_w:
input = input + "w"
if event.key == pygame.K_s:
input = input + "s"
if event.key == pygame.K_a:
input = input + "a"
Then you can check for when the user is done with input(button/enter key), and finalize the variable.
With this you might run into a problem where the key is held longer so you get 3 w's for only pressing the button once. If this is the case you can either 1. not allow another input until a certain time has passed(.25 s maybe?) or 2. use pygame.KEYUP instead of pygame.KEYDOWN and exclusively check for strokes.
Here is a possible solution, it may be really useful for future visitors that are also suffering with this problem like me. Which is;
First making a text-render function
def text1(word,x,y):
font = pygame.font.SysFont(None, 25)
text = font.render("{}".format(word), True, RED)
return screen.blit(text,(x,y))
Then a function works like input;
def inpt():
word=""
text1("Please enter your name: ",300,400) #example asking name
pygame.display.flip()
done = True
while done:
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
word+=str(chr(event.key))
if event.key == pygame.K_b:
word+=chr(event.key)
if event.key == pygame.K_c:
word+=chr(event.key)
if event.key == pygame.K_d:
word+=chr(event.key)
if event.key == pygame.K_RETURN:
done=False
#events...
return text1(word,700,30)
As you see, this function catching keyboard events, it has its own while loop also, it's important. I break the loop when pressing Enter button which is if event.key == pygame.K_RETURN: done=False . Then returning our word with text1 function, displaying. Other events can be set of course opinion-based, like space button for a gap etc.
Then, actually we have to make an intro function and we will ask the user's name in there for example, when intro function is done, name goes to set on the screen until game is over.
def game_intro():
intro=True
while intro:
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
quit()
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_RETURN:
intro=False
inpt() #Here we are calling our function
screen.fill(white)
pygame.display.update()
clock.tick(15)
See that we break this loop with the Enter button again. So we will ask the user our question in intro, then we will set it on the screen, top-right corner for example.

Categories

Resources