Создание игры «Сапер» на Python с использованием Pygame.
Игры занимают важное место в мире программирования, позволяя сочетать творческий подход и технические навыки. Одна из классических и популярных игр — «Сапер» — отлично подходит для освоения основ разработки игр. В этой статье мы подробно рассмотрим, как создать игру «Сапер» на языке Python с использованием библиотеки Pygame.
Pygame — удобная библиотека для создания 2D-игр, обеспечивающая инструменты для работы с графикой, звуком и событиями. «Сапер» же — логическая игра, в которой игроку нужно открыть все клетки на поле, избегая мин. В процессе реализации мы разберём основные части кода, логику игры, создание интерфейса и взаимодействие с пользователем.
Подготовка к работе: установка и настройка Pygame
Прежде чем приступать к написанию игры, необходимо убедиться, что у вас установлен Python и пакет Pygame. Если Pygame отсутствует, его легко установить через менеджер пакетов pip с помощью команды в терминале:
pip install pygame
После установки библиотеки важно проверить, что Pygame корректно подключается и работает в вашей среде разработки. Для этого можно написать небольшую программу, создающую пустое окно. Это позволит убедиться в готовности к следующему этапу — разработке игрового процесса.
Структура проекта
Для удобства работы рекомендуется организовать проект в отдельную папку, в которой будет основной файл с кодом (например, minesweeper.py) и при необходимости папка с ресурсами — изображениями, звуками и т. д. В нашем случае мы попробуем использовать базовые цветовые решения, не прибегая к внешним картинкам, чтобы сделать проект максимально простым для понимания и кастомизации.
Основные элементы игры «Сапер»
Чтобы правильно подготовить программу, нужно чётко понимать, из каких компонентов состоит игра. В «Сапере» присутствуют следующие элементы:
- Игровое поле — двумерная сетка клеток, часть из которых содержит мины.
- Мины — опасные клетки, при открытии которых игра заканчивается поражением.
- Отметки флагами — пользователь может помечать клетки как подозрительные.
- Числа на клетках — показывают количество мин на соседних клетках.
- Счётчик оставшихся мин и таймер игры.
На уровне программирования нам нужно реализовать данные элементы с помощью соответствующих структур данных и функций обработки событий, таких как нажатие мыши и обновление графики.
Моделирование игрового поля
Игровое поле можно представить в виде двумерного списка (матрицы), где каждый элемент — это объект или словарь с информацией о клетке. Основные атрибуты клетки могут быть следующими:
| Атрибут | Описание |
|---|---|
| is_mine | Булево значение, указывающее наличие мины. |
| is_open | Статус, открыта ли клетка. |
| is_flagged | Помечена ли клетка флагом. |
| neighbor_mines | Количество мин вокруг текущей клетки. |
Такое представление упрощает процесс обновления состояния игры и отрисовку интерфейса.
Создание игрового окна и отрисовка элементов
Для начала необходимо инициализировать Pygame и создать окно с определённым размером, зависящим от размера поля и размера клетки. Например, если поле 10×10, а клетка 40×40 пикселей, то окно будет примерно 400×400 пикселей.
Далее нужно продумать, как рисовать клетки. Обычно клетки показываются в трёх состояниях:
- Закрытая — оттенок серого цвета, либо текстура.
- Открытая — светлая клетка с либо пустым пространством, либо цифрой количества соседних мин.
- Помеченная — с изображением флага (можно нарисовать простым треугольником или другим символом).
Хорошей практикой является создание отдельной функции, которая получает параметры клетки и рисует её на заданных координатах. Это упрощает основной цикл отрисовки и улучшает читаемость кода.
Код инициализации и базовой отрисовки
Пример инициализации игрового окна и отрисовки базовой сетки:
import pygame
pygame.init()
CELL_SIZE = 40
GRID_SIZE = 10
screen = pygame.display.set_mode((CELL_SIZE * GRID_SIZE, CELL_SIZE * GRID_SIZE))
pygame.display.set_caption('Сапер')
def draw_cell(x, y, color):
rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
pygame.draw.rect(screen, color, rect)
pygame.draw.rect(screen, (0, 0, 0), rect, 1) # рамка
running = True
while running:
screen.fill((192, 192, 192)) # фон
for y in range(GRID_SIZE):
for x in range(GRID_SIZE):
draw_cell(x, y, (160, 160, 160)) # закрытые клетки
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.flip()
pygame.quit()
Этот код создаёт окно и рисует серые квадраты как закрытые клетки. Дальше будем добавлять логику и отображение обновлённого состояния.
Реализация логики игры
Следующий важный этап — создание логики игры, включающей:
- Расстановку мин случайным образом.
- Вычисление количества мин вокруг каждой клетки.
- Обработку кликов игроком.
- Открытие клеток и автоматическое открытие соседних пустых клеток.
- Управление флагами.
Распределение мин
Распределять мины можно с помощью модуля random. Для этого создаём список всех координат клеток, перемешиваем и выбираем первые n, где n — количество мин. Затем отмечаем эти клетки как содержащие мины.
Подсчёт соседних мин
Для каждой клетки считаем количество мин в восьми соседних позициях (если они в пределах поля). Эта информация нужна для отображения цифр после открытия клетки.
Обработка кликов
В Pygame мышиные события можно обрабатывать через pygame.MOUSEBUTTONDOWN. Левой кнопкой обычно открываем клетку, правой — ставим или снимаем флаг. Важно корректно преобразовывать координаты клика в индексы клеток.
Открытие клеток и рекурсивное раскрытие пустых зон
Если игрок открывает клетку без мин и с нулём соседних мин, нужно автоматически открыть все соседние клетки, чтобы упростить игровой процесс. Для этого используется рекурсивный (или с использованием стека) алгоритм обхода соседей.
Работа с пользовательским интерфейсом
В дополнение к игровому полю стоит реализовать простой интерфейс, включающий:
- Отображение количества оставшихся мин.
- Таймер, показывающий время с момента запуска игры.
- Кнопку перезапуска.
Для вывода текста используется модуль шрифтов Pygame — pygame.font. Таймер можно реализовать с помощью вычисления разницы времени между началом игры и текущим моментом.
Пример вывода текста
font = pygame.font.SysFont('Arial', 24)
text_surface = font.render('Мины: 10', True, (0, 0, 0))
screen.blit(text_surface, (10, 10))
Данная технология позволяет встраивать в основное окно различные вспомогательные элементы без дополнительной сложной графики.
Полный пример кода: упрощённый «Сапер»
Ниже приведён упрощённый отрывок кода, демонстрирующий основные части игры. Это не полный код, но основа для понимания работы.
import pygame
import random
import sys
from pygame.locals import *
pygame.init()
CELL_SIZE = 30
GRID_WIDTH = 10
GRID_HEIGHT = 10
MINES_COUNT = 15
WIDTH = CELL_SIZE * GRID_WIDTH
HEIGHT = CELL_SIZE * GRID_HEIGHT + 40 # для панели статистики
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Сапер')
font = pygame.font.SysFont('Arial', 20)
class Cell:
def __init__(self, x, y):
self.x = x
self.y = y
self.is_mine = False
self.is_open = False
self.is_flagged = False
self.neighbor_mines = 0
def init_field():
field = [[Cell(x, y) for x in range(GRID_WIDTH)] for y in range(GRID_HEIGHT)]
# Расставляем мины
coords = [(x, y) for x in range(GRID_WIDTH) for y in range(GRID_HEIGHT)]
random.shuffle(coords)
for i in range(MINES_COUNT):
x, y = coords[i]
field[y][x].is_mine = True
# Считаем соседей
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
if not field[y][x].is_mine:
count = 0
for ny in range(max(0, y-1), min(GRID_HEIGHT, y+2)):
for nx in range(max(0, x-1), min(GRID_WIDTH, x+2)):
if field[ny][nx].is_mine:
count +=1
field[y][x].neighbor_mines = count
return field
def draw_cell(cell):
rect = pygame.Rect(cell.x * CELL_SIZE, cell.y * CELL_SIZE + 40, CELL_SIZE, CELL_SIZE)
if cell.is_open:
pygame.draw.rect(screen, (200, 200, 200), rect)
if cell.is_mine:
pygame.draw.circle(screen, (255, 0, 0), rect.center, CELL_SIZE//4)
elif cell.neighbor_mines > 0:
text = font.render(str(cell.neighbor_mines), True, (0, 0, 255))
text_rect = text.get_rect(center=rect.center)
screen.blit(text, text_rect)
else:
pygame.draw.rect(screen, (100, 100, 100), rect)
if cell.is_flagged:
pygame.draw.polygon(screen, (255, 0, 0), [
(rect.left + CELL_SIZE * 0.3, rect.top + CELL_SIZE * 0.2),
(rect.left + CELL_SIZE * 0.7, rect.top + CELL_SIZE * 0.5),
(rect.left + CELL_SIZE * 0.3, rect.top + CELL_SIZE * 0.8)
])
pygame.draw.rect(screen, (0, 0, 0), rect, 1)
def open_cell(field, x, y):
cell = field[y][x]
if cell.is_open or cell.is_flagged:
return
cell.is_open = True
if cell.neighbor_mines == 0 and not cell.is_mine:
for ny in range(max(0, y-1), min(GRID_HEIGHT, y+2)):
for nx in range(max(0, x-1), min(GRID_WIDTH, x+2)):
if not field[ny][nx].is_open:
open_cell(field, nx, ny)
def check_victory(field):
for row in field:
for cell in row:
if not cell.is_mine and not cell.is_open:
return False
return True
def main():
field = init_field()
clock = pygame.time.Clock()
start_ticks = pygame.time.get_ticks()
game_over = False
victory = False
while True:
screen.fill((220, 220, 220))
# Отрисовка панели статистики
elapsed_seconds = (pygame.time.get_ticks() - start_ticks) // 1000
mines_left = MINES_COUNT - sum(cell.is_flagged for row in field for cell in row)
info_surf = font.render(f'Мины: {mines_left} Время: {elapsed_seconds}s', True, (0,0,0))
screen.blit(info_surf, (10, 10))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if not game_over and not victory:
if event.type == MOUSEBUTTONDOWN:
mx, my = event.pos
if my >= 40:
gx, gy = mx // CELL_SIZE, (my - 40) // CELL_SIZE
if event.button == 1: # левая кнопка
if not field[gy][gx].is_flagged:
open_cell(field, gx, gy)
if field[gy][gx].is_mine:
game_over = True
elif event.button == 3: # правая кнопка
cell = field[gy][gx]
if not cell.is_open:
cell.is_flagged = not cell.is_flagged
for row in field:
for cell in row:
draw_cell(cell)
if not game_over and check_victory(field):
victory = True
if game_over:
lose_text = font.render('Вы проиграли! Нажмите R для рестарта.', True, (255, 0, 0))
screen.blit(lose_text, (10, HEIGHT // 2))
if victory:
win_text = font.render('Поздравляем! Вы выиграли! Нажмите R для рестарта.', True, (0, 128, 0))
screen.blit(win_text, (10, HEIGHT // 2))
keys = pygame.key.get_pressed()
if keys[K_r]:
field = init_field()
game_over = False
victory = False
start_ticks = pygame.time.get_ticks()
pygame.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
Данный код показывает базовую работу с игровым полем, отрисовку клеток, открытие, постановку флагов и проверку победы/поражения.
Заключение
Создание игры «Сапер» на Python с использованием Pygame является отличным упражнением для начинающих и продвинутых разработчиков, позволяющим практиковаться в работе с графикой, событиями и алгоритмами. Мы рассмотрели основные этапы разработки: подготовку среды, построение структуры данных, логику игры, отрисовку и взаимодействие с пользователем.
Реализованный проект можно расширять, добавляя более сложные функции — разные уровни сложности, сохранение результатов, улучшенную графику и анимации. Также можно оптимизировать производительность и интерфейс для более комфортной игры.
Освоив эти базовые навыки, вы сможете создавать собственные интересные проекты и углублять знания в программировании игр.
«`html
«`