Вам не нужно и не нужно новое окно.Просто создайте еще один Surface
/ Sprite
, который отображает текст и обрабатывает события.
Вот простой пример, который я взломал вместе.Обратите внимание на комментарии, поскольку они объясняют, что происходит:
import pygame
import pygame.freetype
# So our game has 2 states.
# Either we're in the world and run around;
# or we're displaying a menu and the player has to make a choice.
WORLD = 0
MENU = 1
# from https://www.pygame.org/docs/ref/freetype.html#pygame.freetype.Font.render_to
def word_wrap(surf, text, font, color=(0, 0, 0)):
font.origin = True
words = text.split(' ')
width, height = surf.get_size()
line_spacing = font.get_sized_height() + 2
x, y = 0, line_spacing
space = font.get_rect(' ')
for word in words:
bounds = font.get_rect(word)
if x + bounds.width + bounds.x >= width:
x, y = 0, y + line_spacing
if x + bounds.width + bounds.x >= width:
raise ValueError("word too wide for the surface")
if y + bounds.height - bounds.y >= height:
raise ValueError("text to long for the surface")
font.render_to(surf, (x, y), None, color)
x += bounds.width + space.width
return x, y
# This sprite handles the menu.
# It renders a box and a text and listens for key presses.
# If a key we're interessed in is pressed, we call the callback function.
class TextMenu(pygame.sprite.Sprite):
def __init__(self, font, text, listen_to, callback):
super().__init__()
self.image = pygame.Surface((400, 400))
self.image.fill(pygame.Color('white'))
self.image.fill(pygame.Color('black'), self.image.get_rect().inflate((-50, -50)))
self.rect = self.image.get_rect(topleft=(50, 50))
word_wrap(self.image.subsurface(self.image.get_rect().inflate((-100, -100))), text, font, pygame.Color('white'))
self.callback = callback
self.listen_to = listen_to
def update(self, events, dt):
for e in events:
if e.type == pygame.KEYDOWN and e.key in self.listen_to:
self.callback(self, e.key)
# This sprite represents a building the player can "walk in" to trigger
# a menu pop up. In this case, we want the user to either press 1 or 2.
# Then we change the color, because why not, something should happen.
class House(pygame.sprite.Sprite):
def __init__(self, pos, player, show_text):
super().__init__()
self.image = pygame.Surface((64, 64))
self.image.fill(pygame.Color('darkred'))
self.rect = self.image.get_rect(center=pos)
self.show_text = show_text
self.player = player
# Since the menu is triggered when the player touches the building,
# we don't want an endless loop, so we need a flag that prevents
# the menu until the player "leaves the building"
self.triggered = False
def change_color(self, key):
if key == pygame.K_1:
self.image.fill(pygame.Color('yellow'))
if key == pygame.K_2:
self.image.fill(pygame.Color('darkblue'))
def update(self, events, dt):
if pygame.sprite.collide_rect(self, self.player):
if not self.triggered:
self.show_text('Welcome, little blue rect. Please press (1) or (2).', (pygame.K_1, pygame.K_2), self.change_color)
self.triggered = True
else:
self.triggered = False
# This is the player.
# Does basically nothing but run around
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill(pygame.Color('dodgerblue'))
self.rect = self.image.get_rect()
self.pos = pygame.Vector2((100, 200))
def update(self, events, dt):
pressed = pygame.key.get_pressed()
move = pygame.Vector2((0, 0))
if pressed[pygame.K_w]: move += (0, -1)
if pressed[pygame.K_a]: move += (-2, 0)
if pressed[pygame.K_s]: move += (0, 2)
if pressed[pygame.K_d]: move += (2, 0)
if move.length() > 0: move.normalize_ip()
self.pos += move*(dt/5)
self.rect.center = self.pos
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
font = pygame.freetype.SysFont(None, 32)
clock = pygame.time.Clock()
dt = 0
player = Player()
# keep track of the state we're in.
# we start in the WORLD state, a.k.a. running around.
# the state just tells us which sprites are "active",
# a.k.a. if they are updated by calling thier update function
state = WORLD
# sprite group for all MENU-sprites
menu_sprites = pygame.sprite.Group()
# sprite group for all WORLD-sprites
sprites = pygame.sprite.Group(player)
# this function allows other sprites to trigger a menu
def show_text(text, listen_to, callback):
# this function is called by the menu.
# we change the state back to world and kill the TextMenu sprite
def wrapped_callback(sprite, *args):
nonlocal state
state = WORLD
callback(*args)
sprite.kill()
# so when this function is called , let's switch to the MENU state
nonlocal state
state = MENU
# add the TextMenu sprite to the menu_sprites group so it "lives"
menu_sprites.add(TextMenu(font, text, listen_to, wrapped_callback))
# create some buildings. They are all the same...
for pos in ((300, 300), (200, 400), (100, 100)):
sprites.add(House(pos, player, show_text))
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
# see which sprites are "active". The WORLD sprites or the MENU sprites
if state == WORLD:
sprites.update(events, dt)
else:
menu_sprites.update(events, dt)
screen.fill((30, 30, 30))
sprites.draw(screen)
menu_sprites.draw(screen)
pygame.display.update()
dt = clock.tick(60)
if __name__ == '__main__':
main()
Обратите внимание, как вся логика игры четко разделена, и также этот подход делаетлегко добавлять другие состояния, например, функцию паузы или меню.
Конечно, есть еще дюжина других способов сделать это, но вы поймете идею.Для другой идеи о том, как реализовать различные состояния в вашей игре, возможно, посмотрите на этот вопрос .