简体   繁体   中英

How to make new window in pygame?

I've been making game using pygame and found out necessity of new window in this game. screenshot of the game

When boy collide with one of the companies(Web Canape, Smolenskiye Brillianty...) new window must be opened to do quize there. The main game also need to continue working, because boy's task is to go through all companies.

Can someone help me with solving this problem, please?
Probably, it is possible to use new module such as PyQt5 or Tkinter in order not to terminate whole game.

https://github.com/TotumRevolutum/shadows-game

import sys
from map import *
import pygame.display


pygame.init()    

WIDTH = 11 * 100
HEIGHT = 7 * 100
clock = pygame.time.Clock()


def text_show(number):
    intro_text_1 = ["Привет! Меня зовут Емеля.", "Я приглашаю тебя на         День",
                "Теней на предприятия", "Смоленской области.", " ", " ДАЛЕЕ"]
intro_text_2 = ['"День Теней" - это день,', "в течение которого школьники",
                "могут лично следить за работой ", "специалистов с целью проверки",
                "правильности выбора профессии.", " ДАЛЕЕ"]
intro_text_3 = ['Мы с тобой будем определяться', "с профессией по принципу ",
                'индукции от "частного" к "общему",', 'от "предприятия" к "профессии."',
                "", " ДАЛЕЕ"]
intro_text_4 = ['В конце Дня Теней', "ты сможешь выбрать предприятие,",
                'на котором хотел бы работать!', '',
                "", " ДАЛЕЕ"]
if number == 1:
    text = intro_text_1
elif number == 2:
    text = intro_text_2
elif number == 3:
    text = intro_text_3
else:
    text = intro_text_4

back = Background('bg/boy_start.png', [0, 0])
screen.blit(back.image, back.rect)
font = pygame.font.SysFont("Typewriter", 33)
tmp = 0
for line in text:
    if line == " ДАЛЕЕ":
        lines = font.render(line, 1, pygame.Color('red'))
    else:
        lines = font.render(line, 1, pygame.Color('black'))
    display_rect = lines.get_rect()
    tmp += 10
    display_rect.y = 140 + tmp
    display_rect.x = 640
    tmp += display_rect.height
    screen.blit(lines, display_rect)

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.MOUSEBUTTONDOWN:
            return
    pygame.display.flip()
    clock.tick(30)


text_show(1)
text_show(2)
text_show(3)
text_show(4)
running = True
generate_level(load_level())

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

keys = pygame.key.get_pressed()
move_boy(keys, player_group)

all_sprites.draw(screen)
tiles_group.draw(screen)
player_group.draw(screen)
pygame.display.flip()
clock.tick(60)
# other parts are located in the git 
# https://github.com/TotumRevolutum/shadows-game

You don't need and don't want a new window. Just create another Surface / Sprite that renders the text and handles the events.

Here's a simple example I hacked together. Note the comments, as they explain what's going on:

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()

在此处输入图片说明

Note how all the game logic is cleanly seperated, and also this approach makes it easy to add other states, like a pause function or a menu.

Of course there are dozen other ways to do this, but you'll get the idea. For another idea of how to implement different states in your game, maybe look at this question .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM