I'm trying to make a Pokemon-style text display where when you click a key, the next line of text is displayed one by one. However my code doesn't seem to wait for me to press the key before exiting the sequence, and my entire exit function to the sequence does not run. Is there a way to make this happen?
Is there also a way to do this while accessing the text from a list? EDIT: Apologies, I have recreated the code outside my program to show the problem. Sorry if the first code was a little messy :/
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
text_bg = pygame.Surface((640, 180))
game_messages = pygame.font.Font("fonts/Connection.otf", 30)
text1 = game_messages.render("Text number 1", False, (255,0,0))
text2 = game_messages.render("text number 2", False, (255,0,0))
text_list = [text1, text2]
running = 1
playing_text = 2
quits = 0
current_state = 1
def draw_text(text):
screen.blit(text, (40, 360))
while current_state != quits:
if current_state == running:
screen.fill((255,255,255))
pygame.display.flip()
print("update")
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
current_state = playing_text
screen.blit(text_bg, (0,300))
for text in text_list: # iterates over the list with all the messages
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
quit()
elif event.key == pygame.K_SPACE:
draw_text(text)
pygame.time.delay(50)
screen.blit(text_bg, (0, 300))
pygame.display.flip()
for t in range(len(text_list)):
screen.blit(text_bg, (0, 300))
draw_text(text)
pygame.display.flip()
pygame.event.clear(pygame.KEYDOWN)
if event.type == pygame.KEYDOWN:
print("exit")
if event.key == pygame.K_SPACE:
current_state = 1
pygame.display.flip()
This is an interesting question, there's lots of complexity to this sort of thing - what about font size, word wrapping, legibility, etc. etc.?
I thought about it for a while, and I couldn't come up with a nice way of incorporating this into the main loop without all sorts of state-handling code all over the place. So despite my opposition to multiple event loops, here is an example which implements what you suggest in a similar way. The playMessage() function has its own event loop, handling enough to space-through the message, but also send important events (like Quit) back to the main loop for processing.
Anyway, the key point in handling text in this manner is to count the number of key-presses, space in this case, and only show a new line on each press. ( Also exit after the last line on press. )
Each line of text, when rendered into an image via pygame.font.render() will have its own height, which needs to be summed-up as the drawing proceeds down the screen. So in effect the code has two "cursors": the position in the text-line-list, and the Y-pixel position on the display.
Since the code is blocking the main window event loop, it grabs a copy of the original screen-content and uses it to repaint the background. This background is darkened and smoothed to make the text more legible (despite my choice of font).
import pygame
# Window size
WINDOW_WIDTH = 700
WINDOW_HEIGHT = 300
WINDOW_SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
DARK_BLUE = ( 3, 5, 54 )
WHITE_ISH = ( 230, 230, 197 )
def playMessage( window, font, message, start_point=( 20, 20 ) ):
line_separation = 3 # pixels between lines
line_cursor = 0
# Make a blurred copy of the background for updating, by shrinking then
# expanding the current content of the window, oh and darken it too
# for better constrast
skrinked = pygame.transform.smoothscale( window, ( window.get_width()//4, window.get_height()//4 ) )
dark = pygame.Surface( ( skrinked.get_width(), skrinked.get_height() ), flags=pygame.SRCALPHA )
dark.fill( ( 100, 100, 100, 0 ) )
skrinked.blit( dark, (0,0), special_flags=pygame.BLEND_RGBA_SUB )
background = pygame.transform.smoothscale( skrinked, ( window.get_width(), window.get_height() ) )
# cleanup messages, remove blank lines, et.al
message_lines = []
for line in message.split( '\n' ):
line = line.strip()
if ( len( line ) > 0 ):
message_lines.append( line )
# Make every text line into a bitmap
for i,line in enumerate( message_lines ):
message_lines[i] = font.render( line, True, WHITE_ISH )
# Start the render
clock = pygame.time.Clock()
done = False
while not done:
window.blit( background, ( 0,0 ) )
x_pos, y_pos = start_point
for i in range( 0, line_cursor ):
text_rect = message_lines[i].get_rect()
text_rect.x = x_pos
text_rect.y = y_pos
window.blit( message_lines[i], text_rect )
# offset next line
y_pos += text_rect.height + line_separation
pygame.display.flip()
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
pygame.event.post( pygame.event.Event( pygame.QUIT ) ) # re-post this to handle in the main loop
done = True
elif ( event.type == pygame.KEYDOWN ):
if ( event.key == pygame.K_ESCAPE ):
done = True
elif ( event.key == pygame.K_SPACE ):
line_cursor += 1
if ( line_cursor > len( message_lines ) ):
done = True # space at end to dismiss
clock.tick_busy_loop( 16 ) # don't need big FPS for read
### initialisation
pygame.init()
pygame.mixer.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
pygame.display.set_caption("text player")
### Message Text For Displaying
poke_font = pygame.font.Font( 'Pokemon Solid.ttf', 24 ) # ref: https://fontmeme.com/fonts/pokmon-font/
message = "You were eated all up by a Wild Wampus!\nAnd you never found the Key in the Dark Forest!\nRedo From Start."
### Background image
grassy_background = pygame.image.load( "grassy.jpg" ) # ref: http://www.plaintextures.com/
grassy_background = pygame.transform.smoothscale( grassy_background, ( WINDOW_WIDTH, WINDOW_HEIGHT ) )
### Main Loop
clock = pygame.time.Clock()
done = False
while not done:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
elif ( event.type == pygame.MOUSEBUTTONUP ):
# On mouse-click
playMessage( window, poke_font, message )
# Movement keys
#keys = pygame.key.get_pressed()
#if ( keys[pygame.K_UP] ):
# print("up")
# Update the window, but not more than 60fps
window.blit( grassy_background, ( 0, 0 ) )
pygame.display.flip()
# Clamp FPS
clock.tick_busy_loop(60)
pygame.quit()

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With