Переключение отображаемой поверхности в PyGame позволяет пользователю прокручивать границы поверхности - PullRequest
1 голос
/ 30 марта 2019

Я создаю случайно сгенерированную карту в 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 Ответ

2 голосов
/ 31 марта 2019

Вот что я считаю довольно элегантным решением, для которого не нужны две функции прокрутки, 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
...