简体   繁体   English

如何在pygame中创建新窗口?

[英]How to make new window in pygame?

I've been making game using pygame and found out necessity of new window in this game. 我一直在使用pygame制作游戏,并发现在此游戏中需要新窗口。 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. 当男孩与其中一家公司(Web Canape,Smolenskiye Brillianty ...)发生碰撞时,必须打开新窗口在该处进行测验。 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. 可能可以使用新模块(例如PyQt5或Tkinter)以免终止整个游戏。

https://github.com/TotumRevolutum/shadows-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. 只需创建另一个Surface / Sprite呈现文本并处理事件。

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 . 有关如何在游戏中实现不同状态的另一种想法,也许看看这个问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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