在PyGame中切换显示的表面可以使用户滚动出该表面的边界

时间:2019-03-30 18:35:00

标签: python python-2.7 user-interface scroll pygame

我正在使用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()

1 个答案:

答案 0 :(得分:2)

我认为这是一个相当优雅的解决方案,不需要您拥有两个滚动功能scroll_x()scroll_y()。因为不使用它们是如此之快,所以主循环正在检测到与多次按下相同的滚动键,因此必须添加pygame.time.Clock才能将帧速率降低到合理的水平。

此版本并没有像您的代码那样通过那些滚动功能来滚动显示表面本身,而是仅更新当前的“相机”位置,然后将当前display_surface的相应区域涂抹到窗口中。改性。通过确保其x和y分量保持在某些边界极限常数(MINXMINYMAXXMAXY)中来约束摄像机的位置,这些常数是根据值计算得出的其他一些先前定义的常量。

使用符号常量而不是将代码中的文字值硬编码在代码中的多个位置被认为是一种很好的编程习惯,因为这样做使更改它们变得更加容易,因为这样做只需要将源代码更改一次即可。

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