Reputation: 21
Im a newbie in the pygame community and python in general. For my computers assignment we have to make a video game. What Im trying to do is have a loop where it starts off by getting user input, then it takes that user input and then displays a new view based on that. Im trying to make a main menu screen where the user can press the up and down arrows to select options on the screen. However, when I run it, its very glitchy and its slow to respond to user input. Only when you hold it down it will move, but it goes all the way to the 4th option.
while Loop_2 == True:
pygame.time.delay(75)
#Checks what keys have been pressed
keys = pygame.key.get_pressed()
#Checks if player has pressed the exit button
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
#Checks if the player has pressed escape and will end python
if keys[pygame.K_ESCAPE]:
pygame.quit()
#The second main menu if statements for moving the curser and selecting
if keys[pygame.K_DOWN] and Position < 4:
#If the player presses the down arrow to move the pointer and its not in the 4th position at the bottom. it will move down
Position = Position + 1
else:
if keys[pygame.K_UP] and Position > 1:
#If the player presses the up arrow to move the pointer and its not in the 1st position at the top. it will move up
Position = Position - 1
else:
if keys[pygame.K_LSHIFT]:
Loop_3 = True
if Position == 1:
Play_Menu()
elif Position == 2:
Saves_Menu()
elif Position == 3:
Settings_Menu()
elif Position == 4:
Quit_Menu()
#Loads the second main Menu
window.blit(main_menu_2,(0,0))
if Position == 1:
window.blit(main_menu_pointer,(650,-525))
elif Position == 2:
window.blit(main_menu_pointer,(650,-385))
elif Position == 3:
window.blit(main_menu_pointer,(650,-240))
elif Position == 4:
window.blit(main_menu_pointer,(650,-100))
pygame.display.update()
I’ve tried changing the pygame.delay to different values and it will either make it too responsive or non-responsive. I just want it so that I can press down or up and when its released it will move onto the next option, not all the way down the bottom. I cant find anything about this on this website here. Thanks
Upvotes: 2
Views: 486
Reputation: 14926
The core issue is that you want to be able to cycle through menu options without it being too-fast for a normal human.
Currently the code is delaying the entire program to maintain a speed. But what is really needed here is a restriction on the time between selecting a next item.
One way of doing this is looking at the Pygame Time when the selection "event" happens. That is, at what time did the user highlight the menu item. A handy function of the time is .get_ticks()
, which returns a millisecond relative time.
# user just changed selection
time_now = pygame.time.get_ticks()
This allows us to create a future-time, after which a new selection is allowed.
# user just changed selection
time_now = pygame.time.get_ticks()
next_allowed = time_now + 300 # 300 milliseconds in the future
Now when the user attempts to change the selection, don't allow it until the current time is past this time in the future. By our example, that wont be for another 300 milliseconds (which is quite slow).
# user has tried to change selection
time_now = pygame.time.get_ticks()
if ( time_now > next_allowed ):
# ( TODO - menu selection change code )
next_allowed = time_now + 300 # future time next change allowed
Of course, we should set a constant for the time delay, so it's easily changed without having to hunt through the code for constants
MENU_DELAY = 300
...
next_allowed = time_now + MENU_DELAY # future time next change allowed
Here's some example code which implements such a menu:
import pygame
WIDTH = 500
HEIGHT= 500
FPS = 60
MENU_DELAY = 200
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
GREEN = ( 20, 180, 20)
pygame.init()
screen = pygame.display.set_mode( ( WIDTH, HEIGHT ) )
pygame.display.set_caption( "Menuosity" )
font = pygame.font.Font( None, 32 ) # font used to make menu Items
class MenuItem:
""" Simple sprite-like object representing a Menu Button """
def __init__( self, label ):
super().__init__()
self.image = pygame.Surface( ( 200, 50 ) ) # long rectangle
self.rect = self.image.get_rect()
self.label = label.strip().capitalize()
self.selected = False
self.fore = BLACK
self.back = WHITE
self.makeImage( self.fore, self.back )
def makeImage( self, foreground, background ):
""" Make the image used for the menu item, in the given colour """
self.image.fill( background )
pygame.draw.rect( self.image, foreground, [0, 0, self.image.get_width(), self.image.get_height()], 3 )
# centred text for Label
text = font.render( self.label, True, foreground )
text_centre_rect = text.get_rect( center = self.image.get_rect().center )
self.image.blit( text, text_centre_rect )
def moveTo( self, x, y ):
""" Reposition the menu item """
self.rect.x = x
self.rect.y = y
def makeSelected( self, selected=True ):
""" If the button is selected, invert it's colours """
if ( self.selected != selected ):
# Only re-generate if different
if ( selected ):
self.makeImage( self.back, self.fore ) # inverted colours on selection
else:
self.makeImage( self.fore, self.back ) # non-selected, normal colours
self.selected = selected
def draw( self, surface ):
""" Paint the item on the given surface """
surface.blit( self.image, self.rect )
### Create a Menu of Items
menu = []
menu.append( MenuItem( "First Item" ) )
menu.append( MenuItem( "Second Item" ) )
menu.append( MenuItem( "Third Item" ) )
menu.append( MenuItem( "Fourth Item" ) )
### Highlight the first item
current_option = 0
menu[0].makeSelected()
### Lay-out the menu
for i,item in enumerate( menu ):
item.moveTo( 150, 50 + ( i * 80 ) ) # spread out in a single column
### Used to slow down the UI Changes
clock = pygame.time.Clock()
next_change_time = 0
###
### MAIN
###
while True:
time_now = pygame.time.get_ticks() # milliseconds elapsed time
# Handle events
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
# if the user pressed [space]
if ( keys[pygame.K_SPACE] ):
# When space is pushed move to the next item, but not more than every 300ms
# So, has enough time elapsed since the last keypress?
if ( time_now > next_change_time ):
# enough time elapsed, un-select the current, and select the next menu-item
menu[current_option].makeSelected( False )
current_option += 1
if ( current_option >= len( menu ) ):
current_option = 0
menu[current_option].makeSelected( True )
next_change_time = time_now + MENU_DELAY # remember the time of the change
# paint the screen
screen.fill( GREEN ) # paint background
# Paint the menu
for item in menu:
item.draw( screen );
pygame.display.flip()
clock.tick( FPS ) # keep a sane frame-rate
Upvotes: 2