def start(screen: pygame.surface.Surface): is_game_open = True clock = pygame.time.Clock() # Часы # Игрок player = Player(screen.get_width() * 0.5, screen.get_height() * 0.5) # Группа со спрайтами игрока и его приуела player_sprites = pygame.sprite.Group() player_sprites.add(player) player_sprites.add(player.scope) # Фоновая музыка # FIXME: место на котором игра пролагивает (Никита пофиксит) pygame.mixer.music.load(os.path.join("assets/audio", "game_bg.ogg")) pygame.mixer.music.play(-1) pygame.mixer.music.set_volume(DEFAULT_MUSIC_VOLUME) # Игровой цикл while is_game_open: for event in pygame.event.get(): if event.type == pygame.QUIT: is_game_open = True break # Очистка экрана screen.fill((255, 255, 255)) # Обновляем и выводим все спрайты player_sprites.update() player_sprites.draw(screen) clock.tick(FPS) pygame.display.flip()
def draw(self, screen: pygame.surface.Surface): for pos in self.pos: # 绘制蛇身 rect = pygame.rect.Rect(pos[0] * config.horzInr, pos[1] * config.vertInr, config.horzInr + 2, config.vertInr + 2) color = (0, 0, 255) screen.fill(color, rect) # 绘制蛇的两个眼睛 if pos == self.pos[0]: rectL = rect rectL.width /= 4 rectL.height /= 4 rectR = rectL.copy() curDir = self.getCurDir() if curDir == Direction.UP: rectL.center = (pos[0] * config.horzInr + config.horzInr / 4, pos[1] * config.vertInr + config.vertInr / 4) rectR.center = (pos[0] * config.horzInr + config.horzInr / 4 * 3, pos[1] * config.vertInr + config.vertInr / 4) elif curDir == Direction.DOWN: rectL.center = (pos[0] * config.horzInr + config.horzInr / 4 * 3, pos[1] * config.vertInr + config.vertInr / 4 * 3) rectR.center = (pos[0] * config.horzInr + config.horzInr / 4, pos[1] * config.vertInr + config.vertInr / 4 * 3) elif curDir == Direction.LEFT: rectL.center = (pos[0] * config.horzInr + config.horzInr / 4, pos[1] * config.vertInr + config.vertInr / 4 * 3) rectR.center = (pos[0] * config.horzInr + config.horzInr / 4, pos[1] * config.vertInr + config.vertInr / 4) elif curDir == Direction.RIGHT: rectL.center = (pos[0] * config.horzInr + config.horzInr / 4 * 3, pos[1] * config.vertInr + config.vertInr / 4) rectR.center = (pos[0] * config.horzInr + config.horzInr / 4 * 3, pos[1] * config.vertInr + config.vertInr / 4 * 3) color = (250, 235, 215) pygame.draw.ellipse(screen, color, rectL) pygame.draw.ellipse(screen, color, rectR)
def main_menu(surface: pygame.surface.Surface): """ Initiates a few required variables (GUI management, fonts, text) """ gui_manager = pygame_gui.UIManager( (constants.window_width, constants.window_height), 'main_menu_theme.json') clock = pygame.time.Clock() font = pygame.font.Font("ARCADECLASSIC.TTF", 98, bold=True) title_img = font.render("Smart Asteroids", True, (200, 200, 200, 200), background_color) title_rect = title_img.get_rect() title_rect.center = (constants.window_width * 0.5, constants.window_height * 235 / 720) title_rect.size = (constants.window_width * 700 // 1280, constants.window_height * 250 // 720) play_button_rect = pygame.Rect((constants.window_width * 470 // 1280, constants.window_height * 405 // 720), (constants.window_width * 340 // 1280, constants.window_height * 80 // 720)) play_button = pygame_gui.elements.UIButton(relative_rect=play_button_rect, text="Play", manager=gui_manager) quit_button_rect = pygame.Rect((constants.window_width * 470 // 1280, constants.window_height * 529 // 720), (constants.window_width * 340 // 1280, constants.window_height * 80 / 720)) quit_button = pygame_gui.elements.UIButton(relative_rect=quit_button_rect, text="Quit", manager=gui_manager) is_running = True while is_running: dt = clock.tick(60) / 1000.0 for event in pygame.event.get(): if event.type == pygame.QUIT: is_running = False if event.type == pygame.USEREVENT: if event.user_type == pygame_gui.UI_BUTTON_PRESSED: if event.ui_element == play_button: return game_states.GAME_STATES.IN_GAME elif event.ui_element == quit_button: return game_states.GAME_STATES.QUIT gui_manager.process_events(event) gui_manager.update(dt) surface.fill(background_color) surface.blit(title_img, title_rect) gui_manager.draw_ui(surface) pygame.display.update()
def fall(self, gameObject: Game, fen: pygame.surface.Surface, frames: int = 30): #Animation to do start_pos = (self.p.x + self.p.w, self.p.y) alphas = [pi / (2 * frames) * i for i in range(frames + 1)][::-1] for alpha in alphas: fen.fill((0, 0, 0)) end_pos = (start_pos[0] + self.l * cos(-alpha), start_pos[1] + self.l * sin(-alpha)) pygame.draw.line(fen, (0, 255, 0), start_pos, end_pos, 1) gameObject.rendering([False, True, False, True])
def result(displaysurf: pg.surface.Surface, clock: pg.time.Clock, diff: str, diff_color: Tuple[int, int, int], score: int) -> None: """스코어보드를 업데이트하고 출력한다. Args: displaysurf: init 함수에 의해 반환된 최상위 Surface clock: init 함수에 의해 반환된 Clock diff: prompt_difficulty 함수에 의해 반환된 난이도 diff_color: prompt_difficulty 함수에 의해 반환된 난이도에 해당하는 색상 score: game 함수에 의해 반환된 점수 """ scorefile: Path = Path.cwd() / ct.SCOREDIR / f"{diff}.pkl" scores: List[int] = [] try: scores = pickle.load(scorefile.open("rb")) except FileNotFoundError: pass scores.append(score) scores.sort() scores.reverse() scores = scores[:5] pickle.dump(scores, scorefile.open("wb")) displaysurf.fill(ct.BLACK) write_text_ct(displaysurf, 60, (ct.WIDTH / 2, ct.HEIGHT * 0.15), f'Score ({diff})', diff_color) for i, sco in enumerate(scores): write_text_ct(displaysurf, 40, (ct.WIDTH / 2, ct.HEIGHT * (0.3 + 0.1 * i)), f'{i + 1}. {sco}', ct.WHITE) write_text_ct(displaysurf, 40, (ct.WIDTH / 2, ct.HEIGHT * 0.85), f'Your score: {score}', ct.WHITE) while True: for event in pg.event.get(): if event.type == pg.QUIT\ or (event.type == pg.KEYDOWN and event.key == ord('q')): # 종료 pg.quit() sys.exit() pg.display.update() clock.tick(ct.FPS)
def draw_colourless_rounded_rectangle( large_corner_radius: int, large_shape_surface: pygame.surface.Surface, clear_colour_string: str = '#00000000', corner_offset: int = 0): """ Draw a rounded rectangle shape in pure white so it is ready to be multiplied by a colour or gradient. :param large_corner_radius: The radius of the corners. :param large_shape_surface: The surface to draw onto, the shape fills the surface. :param clear_colour_string: The colour to clear the background to. :param corner_offset: Offsets the corners, used to help avoid overlaps that look bad. """ if pygame.version.vernum[0] >= 2 and PYGAME_DEV_NUM >= 8: pygame.draw.rect( large_shape_surface, pygame.Color('#FFFFFFFF'), pygame.Rect( (corner_offset, corner_offset), (large_shape_surface.get_width() - corner_offset, large_shape_surface.get_height() - corner_offset)), border_radius=large_corner_radius) else: pygame.draw.circle(large_shape_surface, pygame.Color('#FFFFFFFF'), (large_corner_radius + corner_offset, large_corner_radius + corner_offset), large_corner_radius) if corner_offset > 0: large_shape_surface.fill( pygame.Color(clear_colour_string), pygame.Rect(0, int(large_shape_surface.get_height() / 2), large_shape_surface.get_width(), int(large_shape_surface.get_height() / 2))) large_shape_surface.fill( pygame.Color(clear_colour_string), pygame.Rect(int(large_shape_surface.get_width() / 2), 0, int(large_shape_surface.get_width() / 2), large_shape_surface.get_height())) x_flip = pygame.transform.flip(large_shape_surface, True, False) large_shape_surface.blit(x_flip, (0, 0)) y_flip = pygame.transform.flip(large_shape_surface, False, True) large_shape_surface.blit(y_flip, (0, 0)) large_shape_surface.fill( pygame.Color("#FFFFFFFF"), pygame.Rect((large_corner_radius, 0), (large_shape_surface.get_width() - (2 * large_corner_radius), large_shape_surface.get_height()))) large_shape_surface.fill( pygame.Color("#FFFFFFFF"), pygame.Rect((0, large_corner_radius), (large_shape_surface.get_width(), large_shape_surface.get_height() - (2 * large_corner_radius))))
def loading_screen(screen: pygame.surface.Surface) -> None: """ Функция, устанавливающая псевдо загрузочный экран :param screen: Поверхность с экраном, где отрисовывается экран загрузки """ # Центральная точка на экране для вывода текста central_point = (screen.get_width() * 0.5, screen.get_height() * 0.5) # Шрифт для текста font = load_game_font(font_size=48) for i in range(4): text = font.render('Загрузка' + '.' * i, True, (240, 240, 240)) # Вывод фона и текста screen.fill(BACKGROUND_COLOR) screen.blit(text, (central_point[0] - text.get_width() * 0.5, central_point[1] - text.get_height() * 0.5)) pygame.display.flip() # Задержка в милисекундах pygame.time.wait(100)
def execute(screen: pygame.surface.Surface): """ Функция запускает меню игры на паузе на переданном экране. В зависимости от действий закрывает всю игру, либо продолжает дальше :param screen: Экран на котором надо отрисовывать менюв :return: Возвращает -1, если игру надо закрыть, None если нет """ is_open = True clock = pygame.time.Clock() joystick = get_joystick() if check_any_joystick() else None # Смещение между UI элементами UI_MARGIN = 20 # Фоновое изображение для всего экрана background_image = load_image("pause_menu_BG.png", "assets/UI") background_image = pygame.transform.scale(background_image, screen.get_size()) # Фоновое игображение ui_background_image = load_image("pause_menu_UI_BG.png", "assets/UI") # Центральная координата всего меню на экране menu_top_left = (screen.get_width() * 0.5 - ui_background_image.get_width() * 0.5, screen.get_height() * 0.5 - ui_background_image.get_height() * 0.5) # Создание UI элементов next_y = menu_top_left[1] + ui_background_image.get_width( ) * 0.5 - UI_MARGIN * 2.5 button_continue = Button((screen.get_width() // 2, next_y), "Продолжить", 32, base_button_filename="button_1.png", hover_button_filename="button_1_hover.png") next_y += button_continue.rect.width * 0.5 + UI_MARGIN button_exit = Button((screen.get_width() // 2, next_y), "Выйти в меню", 32, base_button_filename="button_1.png", hover_button_filename="button_1_hover.png") # Добавление в группу UI_sprites = pygame.sprite.Group() UI_sprites.add(button_continue) UI_sprites.add(button_exit) # Изображение для курсора cursor_image = load_image("cursor.png", "assets/UI/icons") # координаты курсора cursor_x, cursor_y = screen.get_rect().center cursor_speed = 25 # скорость курсора (нужно если используется джойстик) # Цикл меню while is_open: # Переменная, становящайся True если было нажатие курсора # (предусмотрен как джойстик, так и обычная мышка) was_click = False # Обработка событий for event in pygame.event.get(): if event.type == pygame.QUIT: is_open = False break if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: was_click = True if event.type == Button.PRESS_TYPE: # Текст нажатой кнопки # (гарантированно есть, т.к. устанавливается при инициализации) sender_text = event.dict["sender_text"] # Продолжить if sender_text == button_continue.text: is_open = False UI_sprites.empty() # удаление всех спрайтов в группе break # Выход if sender_text == button_exit.text: return -1 # Определение местоположения для курсора if joystick: axis_x, axis_y = joystick.get_axis(0), joystick.get_axis(1) cursor_x += cursor_speed * axis_x if abs( axis_x) >= JOYSTICK_SENSITIVITY else 0 cursor_y += cursor_speed * axis_y if abs( axis_y) >= JOYSTICK_SENSITIVITY else 0 # Проверка на нажатие was_click = joystick.get_button(CONTROLS["JOYSTICK_UI_CLICK"]) else: cursor_x, cursor_y = pygame.mouse.get_pos() cursor_position = (cursor_x, cursor_y) # Обновляем все UI элементы UI_sprites.update(cursor_position, was_click) # Очистка экрана screen.fill((0, 0, 0)) # Фоновое изображение окна screen.blit(background_image, (0, 0)) # Фоновое изобраджение UI screen.blit(ui_background_image, menu_top_left) # Рисуем весь UI UI_sprites.draw(screen) # Рисуем курсор поверх всего screen.blit(cursor_image, cursor_position) pygame.display.flip() # Обновляем состояние джойстика joystick = get_joystick() if check_any_joystick() else None clock.tick(FPS)
def execute(screen: pygame.surface.Surface) -> int: """ Функция запускает меню игры на паузе на переданном экране. В зависимости от действий закрывает всю игру, либо продолжает дальше :param screen: Экран на котором надо отрисовывать меню :return: Возвращает код. (1 - начать заного, -1 - закрыть игру, None - ничего) """ is_open = True clock = pygame.time.Clock() joystick = get_joystick() if check_any_joystick() else None # Смещение между UI элементами margin = 20 # Фоновое изображение для всего экрана background_image = AnimatedBackground( "pause_menu_BG_{0}.png", "assets/sprites/UI/backgrounds/pause_BG", 1, 45, 25, screen.get_size()) menu_width, menu_height = 280, 360 # Фоновое игображение background_menu_image = scale_frame( load_image("assets/sprites/UI/backgrounds/pause_menu_UI_BG.png"), (menu_width, menu_height)) # Центральная координата всего меню на экране menu_top_left = (screen.get_width() * 0.5 - menu_width * 0.5, screen.get_height() * 0.25) # Группа со спрайтами интерфейса UI_sprites = pygame.sprite.Group() # Создание кнопок next_y = menu_top_left[1] + margin * 3.5 # позиция y следущего элемента titles = ("Продолжить", "Начать заново", "Выйти в меню" ) # заголовки кнопок for number in range(len(titles)): # Текущая кнопка button = Button((screen.get_width() // 2, next_y), titles[number], 32, base_button_filename="button_1.png", hover_button_filename="button_1_hover.png") # Высчитывание следущей позиции по y со смещением next_y += button.rect.height + margin # Добавление в группу UI_sprites.add(button) # Изображение для курсора cursor_image = load_image("assets/sprites/UI/icons/cursor.png") # координаты курсора cursor_x, cursor_y = screen.get_rect().center cursor_speed = 40 # скорость курсора (нужно если используется джойстик) # Цикл меню while is_open: # Переменная, становящайся True если было нажатие курсора # (предусмотрен как джойстик, так и обычная мышка) was_click = False # Обработка событий for event in pygame.event.get(): if event.type == pygame.QUIT: is_open = False break if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: was_click = True if event.type == pygame.KEYDOWN: if event.key == CONTROLS["KEYBOARD_PAUSE"]: is_open = False UI_sprites.empty() # удаление всех спрайтов в группе break if event.type == Button.PRESS_TYPE: # Текст нажатой кнопки # (гарантированно есть, т.к. устанавливается при инициализации) sender_text = event.dict["sender_text"] # Продолжить if sender_text == titles[0]: is_open = False UI_sprites.empty() # удаление всех спрайтов в группе break # Начать заного if sender_text == titles[1]: return 1 # Выход if sender_text == titles[-1]: return -1 # Определение местоположения для курсора if joystick: axis_x, axis_y = joystick.get_axis(0), joystick.get_axis(1) cursor_x += cursor_speed * axis_x if abs( axis_x) >= JOYSTICK_SENSITIVITY else 0 cursor_y += cursor_speed * axis_y if abs( axis_y) >= JOYSTICK_SENSITIVITY else 0 # Проверка на нажатие was_click = joystick.get_button(CONTROLS["JOYSTICK_UI_CLICK"]) else: cursor_x, cursor_y = pygame.mouse.get_pos() cursor_position = cursor_x, cursor_y # Обновляем все UI элементы UI_sprites.update(cursor_position, was_click) # Очистка экрана screen.fill((0, 0, 0)) # Фоновое изображение окна background_image.update() screen.blit(background_image.image, (0, 0)) # Фоновое изобраджение UI screen.blit(background_menu_image, menu_top_left) # Рисуем весь UI UI_sprites.draw(screen) # Рисуем курсор поверх всего screen.blit(cursor_image, cursor_position) pygame.display.flip() # Обновляем состояние джойстика joystick = get_joystick() if check_any_joystick() else None clock.tick(FPS)
def execute(screen: pygame.surface.Surface, money: int, count_of_alive_assistants: int, is_win=False): """ Функция запускает конечной экран (либо смерти, либо победы) :param screen: Экран на котором надо отрисовывать менюв :param is_win: Флаг, выиграл ли игрок :param money: Количество собранных игроком и асистентом денег :param count_of_alive_assistants: Количетсво всех живых осистентов к концу игры игры """ is_open = True # Фоновое изображение для всего экрана if is_win: # Фоновая музыка при победе pygame.mixer.music.load("assets/audio/music/win_screen_BG.ogg") pygame.mixer.music.play(-1) animated_background = AnimatedBackground( "win_{0}.png", "assets/sprites/UI/backgrounds/triumph_screen", 1, 8, 80, screen.get_size()) # Картигка с заголовком победы title_you_win = load_image('assets/sprites/UI/you_win.png') you_win_rect = title_you_win.get_rect() you_win_rect.center = screen.get_rect().centerx, int( screen.get_rect().centery * 0.7) else: # Фоновая музыка при проигрыше pygame.mixer.music.load("assets/audio/music/fail_screen_BG.mp3") pygame.mixer.music.play(-1) # Высчитывание размера для фона и сам фон size = screen.get_width() // 3, screen.get_height() // 3 animated_background = AnimatedBackground( "death_{0}.png", "assets/sprites/UI/backgrounds/fail_screen", 1, 23, 140, size, scale_2n=True) # Лого игры logo = LogoImage((screen.get_width() * 0.5, screen.get_height() * 0.1)) # Изображение курсора cursor_image = load_image("assets/sprites/UI/icons/cursor.png") # Получение джойстика (если есть) и определение начальной позиции курсора if check_any_joystick(): joystick = get_joystick() cursor_x, cursor_y = screen.get_rect().center else: joystick = None # Т.к. джойстика нет позиция будет сразу переопределна далее, # поэтому тут начальная позиция не задаётся cursor_x, cursor_y = 0, 0 # Т.к. игрок завершил игру, то файл с сохранением будет перезаписан if os.path.isfile("data/save.txt"): with open('data/save.txt', 'r+', encoding="utf-8") as file: file.truncate(0) # Кортеж с текстом который надо вывести (каждый элемент на новой строке) texts = (f"Деньги собранные игроком вместе с асистентом: {money}", f"Количество живых асистентов: {count_of_alive_assistants}") # Шрифт для поверхностей ниже title_font = load_game_font(64) # Поверхности с одним и тем же текстом, но разный цвет делает крассивый эффект text_surfaces_yellow = [ title_font.render(part.strip(), True, (255, 184, 50)) for part in texts ] text_surfaces_red = [ title_font.render(part.strip(), True, (179, 64, 16)) for part in texts ] # Смещение между наложенными поверхностями для красивого эффекта surfaces_offset = 3 margin = title_font.get_height() * 0.9 # отступ между двумя поверхностями # События, которые активируют закрытие экрана с концном QUITING_EVENTS = ( pygame.QUIT, pygame.MOUSEBUTTONUP, pygame.KEYDOWN, ) # Цикл меню while is_open: # Обработка событий for event in pygame.event.get(): if event.type in QUITING_EVENTS: is_open = False break # Обновление позиции курсора if joystick is not None: # Проверка на выход if joystick.get_button(CONTROLS["JOYSTICK_UI_CLICK"]): break # Значение осей на левом стике axis_x, axis_y = joystick.get_axis(0), joystick.get_axis(1) # Перемещение курсора при движении оси if abs(axis_x) >= JOYSTICK_SENSITIVITY: cursor_x += JOYSTICK_CURSOR_SPEED * axis_x if abs(axis_y) >= JOYSTICK_SENSITIVITY: cursor_y += JOYSTICK_CURSOR_SPEED * axis_y else: cursor_x, cursor_y = pygame.mouse.get_pos() # На экране проигрыша есть фон, которого нет на экране победы if not is_win: screen.fill((31, 30, 36)) # Вывод текущего кадра фонового изображения animated_background.update() screen.blit( animated_background.image, animated_background.image.get_rect( center=screen.get_rect().center)) # Вывод картинки победного заголовка, если игрок выиграл if is_win: # Анализатор может ругаться, но если is_win истина, то # переменные 100% объявлены выше screen.blit(title_you_win, you_win_rect) # следущая позиция по y (будет нужно при вычислении смещения) next_y = 20 # Вывод красного текста for text_surface in text_surfaces_red: y_pos = screen.get_height() * 0.6 + next_y screen.blit( text_surface, text_surface.get_rect(midtop=(screen.get_rect().centerx + surfaces_offset, y_pos + surfaces_offset))) next_y += margin next_y = 20 # Вывод жёлтого текста for text_surface in text_surfaces_yellow: y_pos = screen.get_height() * 0.6 + next_y screen.blit( text_surface, text_surface.get_rect(midtop=(screen.get_rect().centerx, y_pos))) next_y += margin # Вывод логотипа игры screen.blit(logo.image, logo.rect.topleft) # Вывод изображения курсора screen.blit(cursor_image, (cursor_x, cursor_y)) # Обновление состояния джойстика joystick = get_joystick() if check_any_joystick() else None pygame.display.flip()
def execute(screen: pygame.surface.Surface, is_win=False): """ Функция запускает конечной экран (либо смерти, либо победы) :param screen: Экран на котором надо отрисовывать менюв :param is_win: Флаг, выиграл ли игрок """ is_open = True clock = pygame.time.Clock() joystick = get_joystick() if check_any_joystick() else None # Фоновое изображение для всего экрана if not is_win: animated_background = AnimatedBackground("death_{0}.png", 1, 23, 60, screen.get_size()) else: # Фоновая музыка для победителя pygame.mixer.music.load( concat_two_file_paths("assets/audio", "win_screen_BG.ogg")) pygame.mixer.music.play(-1) animated_background = AnimatedBackground("win_{0}.png", 1, 8, 60, screen.get_size()) # Лого игры logo = LogoImage((screen.get_width() * 0.5, screen.get_height() * 0.1)) # Кнопка возвращения в меню button_exit = Button((screen.get_width() // 2, screen.get_height() * 0.9), "Вернутся в меню", 32, base_button_filename="button_1.png", hover_button_filename="button_1_hover.png") # Добавление в группу UI_sprites = pygame.sprite.Group() UI_sprites.add(logo) UI_sprites.add(button_exit) # Изображение для курсора cursor_image = load_image("cursor.png", "assets/UI/icons") # координаты курсора cursor_x, cursor_y = screen.get_rect().center cursor_speed = 15 # скорость курсора (нужно если используется джойстик) # Т.к. игрок завершил игру, то файл с сохранением будет перезаписан if os.path.isfile("data/save.txt"): with open('data/save.txt', 'r+', encoding="utf-8") as file: file.truncate(0) # Цикл меню while is_open: # Переменная, становящайся True если было нажатие курсора # (предусмотрен как джойстик, так и обычная мышка) was_click = False # Обработка событий for event in pygame.event.get(): if event.type == pygame.QUIT: is_open = False break if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: was_click = True if event.type == Button.PRESS_TYPE: # Текст нажатой кнопки # (гарантированно есть, т.к. устанавливается при инициализации) sender_text = event.dict["sender_text"] # Выход if sender_text == button_exit.text: return # Определение местоположения для курсора if joystick: axis_x, axis_y = joystick.get_axis(0), joystick.get_axis(1) cursor_x += cursor_speed * axis_x if abs( axis_x) >= JOYSTICK_SENSITIVITY else 0 cursor_y += cursor_speed * axis_y if abs( axis_y) >= JOYSTICK_SENSITIVITY else 0 # Проверка на нажатие was_click = joystick.get_button(CONTROLS["JOYSTICK_UI_CLICK"]) else: cursor_x, cursor_y = pygame.mouse.get_pos() cursor_position = (cursor_x, cursor_y) # Обновляем все UI элементы UI_sprites.update(cursor_position, was_click) # Очистка экрана screen.fill((0, 0, 0)) animated_background.update() # Вывод текущего кадра фонового изображения screen.blit(animated_background.image, (0, 0)) # Рисуем весь UI UI_sprites.draw(screen) # Рисуем курсор поверх всего screen.blit(cursor_image, cursor_position) pygame.display.flip() # Обновляем состояние джойстика joystick = get_joystick() if check_any_joystick() else None clock.tick(FPS)
def game(displaysurf: pg.surface.Surface, clock: pg.time.Clock, diff: str, diff_color: Tuple[int, int, int]) -> int: """게임의 메인 로직을 실행한다. Args: displaysurf: init 함수에 의해 반환된 최상위 Surface clock: init 함수에 의해 반환된 Clock diff: prompt_difficulty 함수에 의해 반환된 난이도 diff_color: prompt_difficulty 함수에 의해 반환된 난이도에 해당하는 색상 Returns: 게임 결과(점수) """ screenrect = displaysurf.get_rect() # 게임 영역 설정 groupdict: Dict[str, pg.sprite.Group] = dict() # 그룹 불러오기 groupdict = { 'bullet': pg.sprite.Group(), 'player': pg.sprite.Group(), 'enemy': pg.sprite.Group(), 'danmaku': pg.sprite.Group() } spritedict: Dict[str, Element] = dict() parser = Parser(screenrect, groupdict, spritedict) # 패턴 구문분석 spritedict['player'] = parser.load('assets/player.json') groupdict['player'].add(spritedict['player']) # 플레이어 추가 loadeddict = loadfiles(diff) # 패턴 파일 로드 sounddict = loadsounds() _frame: int = 0 frame: int = 0 score: int = ct.INITIALSCORE limittime: float = ct.LIMITTIME onon: int = 0 # 변수 결정 pg.mixer.Sound.play(sounddict['bgm']) while True: # 게임 구동기 _frame += 1 # 시간 증가 frame += 1 if frame == limittime // 1 and onon == 0: # 게임 중 적 생성 시간일 때 enemychoose(groupdict['enemy'], parser, loadeddict) # 적 생성 frame = 0 # 적 생성 시간 초기화 if limittime > ct.OVERLIMIT: limittime -= ct.LIMITREDUCE # 적 생성 주기 단축 else: onon = 1 # 게임 종료 시간 if onon == 1: # 게임 끝 if frame == ct.OVERTIME: return score for event in pg.event.get(): groupdict['player'].update(event=event) # 객체 위치 이동 if event.type == pg.QUIT: # 종료 버튼 pg.quit() sys.exit() displaysurf.fill(ct.BLACK) # 배경 색 if pg.sprite.groupcollide(groupdict['player'], groupdict['danmaku'], False, False): score -= 1 # 부딫혔을 때 충돌 카운트 +1 pg.mixer.Sound.play(sounddict['gothit']) # 쏜 총이 적 맞았을 때 적 kill pg.sprite.groupcollide(groupdict['bullet'], groupdict['enemy'], False, True) enemyn = len(groupdict['enemy']) for key in groupdict: groupdict[key].update() # 모든 객체 위치 업데이트 groupdict[key].draw(displaysurf) # 적이 자연적으로 죽을 경우 페널티 score -= ct.PENALTY * (enemyn - len(groupdict['enemy'])) write_text(displaysurf, 60, (20, 20), f"{score}", ct.WHITE) write_text_rt(displaysurf, 60, (ct.WIDTH - 20, 20), diff, diff_color) pg.display.update() clock.tick(ct.FPS) # 시간 업데이트
def game_over_menu(surface: pygame.surface.Surface, score: int): """ Displays the game over menu """ menu_rect = pygame.rect.Rect( (constants.window_width // 4, constants.window_width // 4), (constants.window_width // 2, constants.window_height // 2)) menu_rect.center = (constants.window_width // 2, constants.window_height // 2) over_background = (50, 50, 50) over_foreground = (200, 200, 200) gui_manager = pygame_gui.UIManager( (constants.window_width, constants.window_height), 'game_over_theme.json') clock = pygame.time.Clock() font = pygame.font.Font("ARCADECLASSIC.TTF", constants.generalise_height(75)) over_img = font.render("game over", True, over_foreground, over_background) over_rect = over_img.get_rect() over_rect.center = (constants.generalise_width(640), constants.generalise_height(292)) score_font = pygame.font.Font("ARCADECLASSIC.TTF", constants.generalise_height(48)) string = "Score is " + str(score) score_img = score_font.render(string, True, over_foreground, over_background) score_rect = score_img.get_rect() score_rect.center = (constants.generalise_width(549 + 82), constants.generalise_height(370)) restart_button_rect = pygame.Rect( (constants.generalise_width(549), constants.generalise_height(409)), (constants.generalise_width(164), constants.generalise_height(41))) quit_button_rect = pygame.Rect( (constants.generalise_width(549), constants.generalise_height(468)), (constants.generalise_width(164), constants.generalise_height(41))) restart_button = pygame_gui.elements.UIButton( relative_rect=restart_button_rect, text="restart", manager=gui_manager) quit_button = pygame_gui.elements.UIButton(relative_rect=quit_button_rect, text="Exit to menu", manager=gui_manager) is_paused = True while is_paused: dt = clock.tick(60) / 1000.0 for event in pygame.event.get(): if event.type == pygame.QUIT: return GAME_STATES.QUIT if event.type == pygame.USEREVENT: if event.user_type == pygame_gui.UI_BUTTON_PRESSED: if event.ui_element == quit_button: return GAME_STATES.MAIN_MENU if event.ui_element == restart_button: return GAME_STATES.IN_GAME gui_manager.process_events(event) gui_manager.update(dt) surface.fill(over_background, menu_rect) surface.blit(over_img, over_rect) surface.blit(score_img, score_rect) gui_manager.draw_ui(surface) pygame.display.update(menu_rect)
def ingame(surface: pygame.surface.Surface): """ Create the asteroids Initialise necessary variables """ asteroid_count = 8 clock = pygame.time.Clock() font = pygame.font.Font("ARCADECLASSIC.TTF", 48) score = 0 score_img = font.render(str(score), True, (200, 200, 200), background_color) score_rect = score_img.get_rect() score_rect.topright = (constants.window_width - 30, 30) asteroids_group = pygame.sprite.Group() for i in range(0, asteroid_count): tmp_start_vel = [0, 0] tmp_start_pos = [0, 0] if numpy.random.random() < 0.5: tmp_start_pos = [ -constants.asteroid_radius, constants.window_height * (0.1 + numpy.random.random() * 0.8) ] tmp_start_vel = [constants.asteroid_start_vel, 0] else: tmp_start_pos = [ constants.window_width + constants.asteroid_radius, constants.window_height * numpy.random.random() ] tmp_start_vel = [-constants.asteroid_start_vel, 0] tmp = asteroid.asteroid( asteroid_count, numpy.array(tmp_start_pos), constants.generalise_height(constants.asteroid_radius), numpy.array(tmp_start_vel)) asteroids_group.add(tmp) player = rocket.rocket() bullets = [] parent = None parent_qual = 0 last_best_check_tick = -1000 is_running = True while is_running: """ Calculate the time taken for the previous frame and cap FPS to 60 """ dt = clock.tick(60) / 1000.0 """ Handle input and quit events """ for event in pygame.event.get(): if event.type == pygame.QUIT: return GAME_STATES.QUIT elif event.type == pygame.KEYUP: if event.key == pygame.K_ESCAPE: ret = pause_menu(surface) if ret != GAME_STATES.IN_GAME: return ret """ If the player is dead, display the game over menu """ if player.status == rocket.ROCKET_STATUS.DEAD: return game_over_menu(surface, score) """ Update positions and velocities of bullets, asteroids and player """ asteroids_group.update(asteroids_group, player, dt, bullets) player.update(asteroids_group, dt, bullets) for i in range(0, len(bullets)): bullets[i].update(dt) """ Remove destroyed asteroids, and create new ones if necessary """ to_be_removed = [] if parent is None: parent = asteroids_group.sprites()[0] parent_qual = calc_quality(parent) for a in asteroids_group: qual = calc_quality(a) if qual > parent_qual: parent = a parent_qual = qual for ast in asteroids_group: if ast.status == asteroid.ASTEROID_STATUS.DESTROYED: to_be_removed.append(ast) if ast.destroyed_by_player: """ Increment the score and update the score display if an asteroid has been destroyed by the player """ score += 1 score_img = font.render(str(score), True, (200, 200, 200), background_color) score_rect = score_img.get_rect() score_rect.topright = (constants.window_width - 30, 30) for r in to_be_removed: asteroids_group.remove(r) tmp_start_vel = [0, 0] tmp_start_pos = [0, 0] if numpy.random.random() < 0.5: tmp_start_pos = [ 0 - constants.asteroid_radius, constants.window_height * (0.1 + numpy.random.random() * 0.8) ] tmp_start_vel = [constants.asteroid_start_vel, 0] else: tmp_start_pos = [ constants.window_width + constants.asteroid_radius, constants.window_height * numpy.random.random() ] tmp_start_vel = [-constants.asteroid_start_vel, 0] tmp = asteroid.asteroid( asteroid_count, numpy.array(tmp_start_pos), constants.generalise_height(constants.asteroid_radius), numpy.array(tmp_start_vel)) tmp.evolve_from(parent) asteroids_group.add(tmp) to_be_removed = [] """ Clear the background and draw the player, asteroids and the bullets """ surface.fill(background_color) for i in range(0, len(bullets)): pos = bullets[i].position if pos[0] < 0 or pos[0] > constants.window_width or pos[1] < 0 or\ pos[1] > constants.window_height: to_be_removed.append(bullets[i]) bullets[i].draw(surface) for i in to_be_removed: bullets.remove(i) asteroids_group.draw(surface) surface.blit(player.image, player.rect) surface.blit(score_img, score_rect) pygame.display.update()
def pause_menu(surface: pygame.surface.Surface): """ Displays the pause menu """ menu_rect = pygame.rect.Rect( (constants.window_width // 4, constants.window_width // 4), (constants.window_width // 2, constants.window_height // 2)) menu_rect.center = (constants.window_width // 2, constants.window_height // 2) paused_background = (50, 50, 50) paused_foreground = (200, 200, 200) gui_manager = pygame_gui.UIManager( (constants.window_width, constants.window_height), 'pause_menu_theme.json') clock = pygame.time.Clock() font = pygame.font.Font("ARCADECLASSIC.TTF", constants.generalise_height(75)) pause_img = font.render("game paused", True, paused_foreground, paused_background) pause_rect = pause_img.get_rect() pause_rect.center = (constants.generalise_width(640), constants.generalise_height(292)) resume_button_rect = pygame.Rect( (constants.generalise_width(549), constants.generalise_height(350)), (constants.generalise_width(164), constants.generalise_height(41))) quit_button_rect = pygame.Rect( (constants.generalise_width(549), constants.generalise_height(409)), (constants.generalise_width(164), constants.generalise_height(41))) menu_button_rect = pygame.Rect( (constants.generalise_width(549), constants.generalise_height(468)), (constants.generalise_width(164), constants.generalise_height(41))) resume_button = pygame_gui.elements.UIButton( relative_rect=resume_button_rect, text="Resume", manager=gui_manager) quit_button = pygame_gui.elements.UIButton(relative_rect=quit_button_rect, text="Quit", manager=gui_manager) menu_button = pygame_gui.elements.UIButton(relative_rect=menu_button_rect, text="Exit to menu", manager=gui_manager) is_paused = True while is_paused: dt = clock.tick(60) / 1000.0 for event in pygame.event.get(): if event.type == pygame.QUIT: return GAME_STATES.QUIT if event.type == pygame.USEREVENT: if event.user_type == pygame_gui.UI_BUTTON_PRESSED: if event.ui_element == quit_button: return GAME_STATES.QUIT if event.ui_element == resume_button: return GAME_STATES.IN_GAME if event.ui_element == menu_button: return GAME_STATES.MAIN_MENU gui_manager.process_events(event) gui_manager.update(dt) surface.fill(paused_background, menu_rect) surface.blit(pause_img, pause_rect) gui_manager.draw_ui(surface) pygame.display.update(menu_rect)
def execute(screen: pygame.surface.Surface) -> int: """ Функция запускает главное меню игры на переданном экране. В зависимости от действий возвращает свой код 0 - была нажата кнопка выйти 1 - была нажата кнопка играть :param screen: Экран на котором надо отрисовывать менюв :return: Код """ is_open = True clock = pygame.time.Clock() joystick = get_joystick() if check_any_joystick() else None # Смещение между UI элементами UI_MARGIN = 55 # Создание UI элементов game_logo = LogoImage( (screen.get_width() // 2, screen.get_height() // 6 - UI_MARGIN)) next_y = game_logo.rect.y + game_logo.rect.height + UI_MARGIN * 2 button_play = Button((screen.get_width() // 2, next_y), "Играть", 32) next_y = button_play.rect.y + button_play.rect.height + UI_MARGIN button_controls = Button((screen.get_width() // 2, next_y), "Управление", 32) next_y = button_controls.rect.y + button_controls.rect.height + UI_MARGIN button_about = Button((screen.get_width() // 2, next_y), "Об игре", 32) next_y = button_about.rect.y + button_about.rect.height + UI_MARGIN button_authors = Button((screen.get_width() // 2, next_y), "Авторы", 32) next_y = button_authors.rect.y + button_authors.rect.height + UI_MARGIN button_exit = Button((screen.get_width() // 2, next_y), "Выйти", 32) # Добавление в группу UI_sprites = pygame.sprite.Group() UI_sprites.add(game_logo) UI_sprites.add(button_play) UI_sprites.add(button_controls) UI_sprites.add(button_about) UI_sprites.add(button_authors) UI_sprites.add(button_exit) # Текущие диалог (может появлятся при нажатии кнопок) current_message_box = None # Фоновое изоюражение background_image = load_image("main_menu_BG.png", "assets/UI") # Меняем размер картинки в зависимости от размера экрана background_image = pygame.transform.scale( background_image, (screen.get_width(), screen.get_height())) # Делаем курсор мыши невидимым и загружаем вместо него своё изображение pygame.mouse.set_visible(False) cursor_image = load_image("cursor.png", "assets/UI/icons") # координаты курсора cursor_x, cursor_y = screen.get_width() * 0.5, screen.get_height() * 0.1 cursor_speed = 30 # скорость курсора (нужно если используется джойстик) # Фоновая музыка pygame.mixer.music.load( concat_two_file_paths("assets/audio", "main_menu.ogg")) # Воспроизведение музыки вечно pygame.mixer.music.play(-1) # Установка громкости pygame.mixer.music.set_volume(DEFAULT_MUSIC_VOLUME) # Переменная, становящайся True если было нажатие курсора # (предусмотрен как джойстик, так и обычная мышка) was_click = False # Цикл окна while is_open: # Обработка событий for event in pygame.event.get(): if event.type == pygame.QUIT: is_open = False break if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: was_click = True if event.type == Button.PRESS_TYPE: # Текст нажатой кнопки # (гарантированно есть, т.к. устанавливается при инициализации) sender_text = event.dict["sender_text"] # Управление if sender_text == button_controls.text: text = str("Базовое управление:\n" + "На клавиатуре: WASD - двигаться; Q - рывок\n" + "На джойстике: PADS - двигаться; R1 - рывок\n") current_message_box = MessageBox( text, 30, (screen.get_width() * 0.5, screen.get_height() * 0.5)) continue # Об игре if sender_text == button_about.text: text = str("Игра жанра Rogulite, в \n" "которой надо пройти \n" + "сквозь подземелье, заполненное врагами.\n" "Желаем удачи\n") current_message_box = MessageBox( text, 30, (screen.get_width() * 0.5, screen.get_height() * 0.5)) continue # Авторы if sender_text == button_authors.text: text = str("Никита Сошнев (Nik4ant)\n" "Максим Рудаков (Massering)") current_message_box = MessageBox( text, 30, (screen.get_width() * 0.5, screen.get_height() * 0.5)) continue # Музыка затухает (1 секунду), т.к. главный экран закроется pygame.mixer.music.fadeout(1000) # Проверяем какая кнопка была нажата if sender_text == button_play.text: return 1 elif sender_text == button_exit.text: return 0 # Определение местоположения для курсора if joystick: axis_x, axis_y = joystick.get_axis(0), joystick.get_axis(1) cursor_x += cursor_speed * axis_x if abs( axis_x) >= JOYSTICK_SENSITIVITY else 0 cursor_y += cursor_speed * axis_y if abs( axis_y) >= JOYSTICK_SENSITIVITY else 0 # Проверка на нажатие was_click = joystick.get_button(CONTROLS["JOYSTICK_UI_CLICK"]) else: cursor_x, cursor_y = pygame.mouse.get_pos() cursor_position = (cursor_x, cursor_y) # Обновляем все UI элементы UI_sprites.update(cursor_position, was_click) # Очистка экрана screen.fill((0, 0, 0)) # Фоновое изобраджение screen.blit(background_image, (0, 0)) # Рисуем весь UI UI_sprites.draw(screen) # Если есть диалог, то его тоже обновляем и рисуем if current_message_box: current_message_box.update(was_click) if current_message_box.need_to_draw: current_message_box.draw(screen) # Рисуем курсор поверх всего screen.blit(cursor_image, cursor_position) pygame.display.flip() # Обновляем состояние джойстика joystick = get_joystick() if check_any_joystick() else None was_click = False clock.tick(FPS)