代码之家  ›  专栏  ›  技术社区  ›  LuminousNutria

在PyGame中切换显示的曲面允许用户滚动出曲面的边界

  •  0
  • LuminousNutria  · 技术社区  · 6 年前

    我正在用PyGame创建一个随机生成的地图。但是,我遇到了一个问题,如果用户从地图的左上角滚动,并更改显示的PyGame图面,就会发生问题。

    问题是,PyGame仍然会在曲面的左上角启动它们,然后允许它们从曲面的边上滚动,因为跟踪这些边的列表, camera_pos ,现在的值不正确。

    所有的表面都是相同的尺寸,我想让它使用户在相同的位置,当他们改变显示的表面。但是,我不知道如何设置pygame切换曲面时用户视图的位置。

    如何将用户视图的位置切换回曲面切换时的位置?

    我在下面举了一个MCV的例子,希望能有所帮助。它不是显示地图,而是在纯色周围绘制边框。我很抱歉要等多久。我不知道怎么把它缩短。

    在本例中,滚动是用箭头键完成的。您可以按键盘上的r、g或b来显示不同颜色的表面。

    import pygame
    import numpy as np
    import sys
    
    def scroll_y(display_surface, offset):
        """
        Handles vertical scrolling.
    
        :param display_surface: A pyGame surface object.
        :param offset: The speed of the scroll
        """
    
        width, height = display_surface.get_size()
    
        map_copy = display_surface.copy()
    
        display_surface.blit(map_copy, (0, offset))
    
        # handle scrolling down
        if offset < 0:
            display_surface.blit(map_copy,
                                 (0, height + offset),
                                 (0, 0, width, -offset))
    
        # handle scrolling up
        else:
            display_surface.blit(map_copy,
                                 (0, 0),
                                 (0, height - offset, width, offset))
    
    
    def scroll_x(display_surface, offset):
        """
        Handles horizontal scrolling.
    
        :param display_surface: A pyGame surface object.
        :param offset: The speed of the scroll
        """
    
        width, height = display_surface.get_size()
    
        map_copy = display_surface.copy()
    
        display_surface.blit(map_copy, (offset, 0))
    
        # handle scrolling right
        if offset < 0:
            display_surface.blit(map_copy,
                                 (width + offset, 0),
                                 (0, 0, -offset, height))
    
        # handle scrolling left
        else:
            display_surface.blit(map_copy,
                                 (0, 0),
                                 (width - offset, 0, offset, height))
    
    
    def main():
        """
        This function displays the three surfaces.
    
        Press r to show the red surface (which is displayed by default).
        Press g to show the green surface.
        Press b to show the blue surface.
        """
        pygame.init()
    
        window = pygame.display.set_mode((1600, 900))
    
        red_surface = pygame.Surface([3200, 1800]).convert(window)
        green_surface = pygame.Surface([3200, 1800]).convert(window)
        blue_surface = pygame.Surface([3200, 1800]).convert(window)
    
        red_surface.fill((255, 145, 145))
        green_surface.fill((145, 255, 145))
        blue_surface.fill((145, 145, 255))
    
        # draw thick black lines on surface borders
        pygame.draw.rect(red_surface, (0, 0, 0), (0, 0, 3200, 1800), 40)
        pygame.draw.rect(green_surface, (0, 0, 0), (0, 0, 3200, 1800), 40)
        pygame.draw.rect(blue_surface, (0, 0, 0), (0, 0, 3200, 1800), 40)
    
        display_surface = red_surface.copy()
    
        camera_pos = np.array([0, 0])
    
        while True:  # <-- the pyGame loop
    
            event = pygame.event.poll()
    
            pressed = pygame.key.get_pressed()
    
            # handle closing the window
            if event.type == pygame.QUIT:
                break
    
            window.blit(display_surface, (0, 0))
    
            # handle switching display modes
            if pressed[pygame.K_g]:
                display_surface = green_surface
            elif pressed[pygame.K_b]:
                display_surface = blue_surface
            elif pressed[pygame.K_r]:
                display_surface = red_surface
    
            # handle scrolling, make sure you can't scroll past the borders
            if pressed[pygame.K_UP] and camera_pos[1] > 0:
                scroll_y(display_surface, 5)
                camera_pos[1] -= 5
    
            elif pressed[pygame.K_DOWN] and camera_pos[1] < (1800 / 2):
                scroll_y(display_surface, -5)
                camera_pos[1] += 5
    
            elif pressed[pygame.K_LEFT] and camera_pos[0] > 0:
                scroll_x(display_surface, 5)
                camera_pos[0] -= 5
    
            elif pressed[pygame.K_RIGHT] and camera_pos[0] < (3200 / 2):
                scroll_x(display_surface, -5)
                camera_pos[0] += 5
    
            # updates what the window displays
            pygame.display.update()
    
        pygame.quit()
        sys.exit(0)
    
    
    if __name__ == "__main__":
        # runs the pyGame loop
        main()
    
    0 回复  |  直到 6 年前
        1
  •  2
  •   martineau    6 年前

    我认为这是一个相当优雅的解决方案,它不需要两个滚动函数, scroll_x() scroll_y() 你有。因为不使用它们太快了,所以主循环检测到同一个滚动键被多次按下,需要添加一个 pygame.time.Clock

    不像代码那样,通过这些滚动函数来滚动显示表面本身,这个版本只是更新当前的“相机”位置,然后闪烁当前屏幕的相应区域 display_surface 无论什么时候修改它。通过确保其x和y分量保持在某些边界限制常数内,可以限制相机的位置 MINX MINY MAXX , MAXY 它是根据其他一些先前定义的常量的值来计算的。

    import pygame
    import sys
    
    
    def main():
        """
        This function displays the three surfaces.
    
        Press r to show the red surface (which is displayed by default).
        Press g to show the green surface.
        Press b to show the blue surface.
        """
        FPS = 60  # Frames per second
        SURF_WIDTH, SURF_HEIGHT = 3200, 1800
        WIN_WIDTH, WIN_HEIGHT = 1600, 900
        DX, DY = 5, 5  # Scroll amounts.
        MINX, MAXX = DX, SURF_WIDTH - WIN_WIDTH + DX - 1
        MINY, MAXY = DY, SURF_HEIGHT - WIN_HEIGHT + DY - 1
    
        pygame.init()
        pygame.font.init()
        fonts = pygame.font.get_fonts()
        clock = pygame.time.Clock()
    
        window = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
    
        red_surface = pygame.Surface([SURF_WIDTH, SURF_HEIGHT]).convert(window)
        green_surface = pygame.Surface([SURF_WIDTH, SURF_HEIGHT]).convert(window)
        blue_surface = pygame.Surface([SURF_WIDTH, SURF_HEIGHT]).convert(window)
    
        red_surface.fill((255, 145, 145))
        green_surface.fill((145, 255, 145))
        blue_surface.fill((145, 145, 255))
    
        # Draw thick black lines on surface borders
        pygame.draw.rect(red_surface, (0, 0, 0), (0, 0, SURF_WIDTH, SURF_HEIGHT), 40)
        pygame.draw.rect(green_surface, (0, 0, 0), (0, 0, SURF_WIDTH, SURF_HEIGHT), 40)
        pygame.draw.rect(blue_surface, (0, 0, 0), (0, 0, SURF_WIDTH, SURF_HEIGHT), 40)
    
        # Draw label on each of the surfaces for testing. (ADDED)
        font = pygame.font.SysFont(None, 35)
    
        rtext = font.render('red surface', True, (255, 0, 0))
        textpos = rtext.get_rect(centerx=300, centery=200)  # Reused.
        red_surface.blit(rtext, textpos)
    
        rtext = font.render('green surface', True, (0, 192, 0))
        green_surface.blit(rtext, textpos)
    
        rtext = font.render('blue surface', True, (0, 0, 255))
        blue_surface.blit(rtext, textpos)
    
        display_surface = red_surface
        camera_pos = pygame.math.Vector2(0, 0)
        update_surface = True
    
        while True:  # Game loop
    
            if update_surface:
                window.blit(display_surface, (0, 0), (camera_pos[0], camera_pos[1],
                                                      WIN_WIDTH, WIN_HEIGHT))
                update_surface = False
    
            event = pygame.event.poll()
            pressed = pygame.key.get_pressed()
    
            # Close window?
            if event.type == pygame.QUIT or pressed[pygame.K_ESCAPE]:
                break
    
            # Switch display surface?
            if pressed[pygame.K_g]:
                display_surface = green_surface
                update_surface = True
            elif pressed[pygame.K_b]:
                display_surface = blue_surface
                update_surface = True
            elif pressed[pygame.K_r]:
                display_surface = red_surface
                update_surface = True
    
            # Constrain scrolling to within borders
            if pressed[pygame.K_LEFT] and camera_pos[0] >= MINX:
                camera_pos[0] -= DX
                update_surface = True
            elif pressed[pygame.K_RIGHT] and camera_pos[0] <= MAXX:
                camera_pos[0] += DX
                update_surface = True
            elif pressed[pygame.K_UP] and camera_pos[1] >= MINY:
                camera_pos[1] -= DY
                update_surface = True
            elif pressed[pygame.K_DOWN] and camera_pos[1] <= MAXY:
                camera_pos[1] += DY
                update_surface = True
    
            # updates what the window displays
            pygame.display.update()
            clock.tick(FPS)
    
    
        pygame.quit()
        sys.exit(0)
    
    
    if __name__ == "__main__":
    
        main()  # runs the pyGame loop