(https://habr.com/ru/articles/953416/)
Хочете створити свою першу гру, але не знаєте з чого почати? "Сапер" - ідеальний проект для цього! У ньому є проста, але цікава логіка, робота з введенням користувача і графіка, що робить його відмінною стартовою точкою для будь-якого початківця розробника.
У цьому покроковому посібнику ми разом напишемо повністю робочу гру «Сапер» за допомогою мови Python та популярної бібліотеки для створення ігор Pygame. Вам не потрібний досвід у розробці ігор – лише базові знання Python. Ми пройдемо весь шлях від порожнього файлу до фінального результату, і я докладно поясню кожен крок.
Ось наш план:
- Налаштуємо робоче оточення: створимо окремий простір для нашого проекту.
- Створимо «мозок» гри: напишемо код, який відповідатиме за міни та цифри на полі.
- Намалюємо ігрове поле: виведемо нашу гру на екран.
- Навчимо гру реагувати: додамо обробку кліків миші.
- Додамо умови перемоги та поразки: щоб у гру було цікаво грати!
Крок 0. Підготовка робочого місця (найважливіший крок!)
1. Створюємо папку проекту
# Створюємо папку з іменем minesweeper mkdir minesweeper # Заходим в середину цієї папки cd minesweeper
Тепер усі подальші дії ми будемо виконувати всередині папки
minesweeper.
2. Створюємо віртуальне оточення
Перебуваючи в папці minesweeper, введіть у термінал наступну
команду:
python -m venv venv
Ця команда створить усередині minesweeperнову папку з ім'ям venv. У ній зберігатиметься "чиста" копія Python і всі бібліотеки, які ми встановимо для нашої гри.
3. Активуємо оточення
Ми створили "коробку", а тепер її потрібно "відкрити" чи активувати. Команди для цього дещо відрізняються залежно від вашої операційної системи.
Якщо у вас Windows:
venv\Scripts\activate
Якщо у вас macOS або Linux:
source venv/bin/activate
Якщо все пройшло успішно, ви побачите, що на початку терміналу з'явилося (venv). Це наш сигнал – віртуальне оточення активно!
# приклад того, як має виглядати:
(venv) C:\Users\YourName\minesweeper
4. Встановлюємо Pygame
Тепер, коли наша "коробка" відкрита, ми можемо покласти до неї наш головний
інструмент - бібліотеку Pygame. pip— це стандартний
менеджер пакетів Python, який завантажить та встановить її для нас.
Виконайте у терміналі одну просту команду:
pip install pygame
pip встановить Pygame саме у наше віртуальне оточення, не торкаючись основної системи.
Крок 1. "Мозок" гри - створюємо логіку поля
Перш ніж ми почнемо щось малювати, нам потрібно створити правила та внутрішній пристрій нашої гри. Цю частину часто називають логікою чи моделлю . Уявіть, що ми створюємо «Сапера», в якого можна було б грати із заплющеними очима, просто називаючи координати.
Наш план для цього кроку:
- Створити "цеглинку" - об'єкт для одного осередку поля.
- Зібрати з цих "цеглинок" ціле ігрове поле.
- Навчити поле розставляти міни.
- Навчити поле рахувати цифри навколо мін.
Почнемо! Створіть у папці minesweeperновий файл з ім'ям main.pyта пишіть весь код із цього кроку туди.
1.1. "Цегла" для нашого поля: клас Cell
Кожен квадратик на полі «Сапера» повинен щось про себе "знати": чи є в ньому міна, чи він відкритий і так далі. Щоб зручно зберігати цю інформацію, ми створимо для осередку власний клас-шаблон.
Додати цей код у ваш файл main.py:
class Cell:
def __init__(self):
self.is_mine = False
self.is_open = False
self.is_flagged = False
self.adjacent_mines = 0
Цей простий клас – наш "будівельний блок". Щоразу, коли нам знадобиться новий осередок на полі, ми створюватимемо його за цим шаблоном.
1.2. Збираємо поле в одне ціле: клас GameBoard
Тепер, коли у нас є "цеглинка", давайте побудуємо з них стіну - наше ігрове поле. Для цього створимо ще один клас, GameBoardякий буде керувати всіма осередками.
Додати цей код в main.py під класом Cell:
import random
# Класс Cell, который мы написали выше, должен быть здесь...
class GameBoard:
def __init__(self, width, height, mines_count):
self.width = width
self.height = height
self.mines_count = mines_count
# Создаем сетку (список списков) и заполняем её нашими "кирпичиками"
self.board = [[Cell() for _ in range(width)] for _ in range(height)]
Тут ми створюємо сітку (двовимірний перелік), заповнену об'єктами Cell. Тепер у нас є структура поля, але воно поки що порожнє. Давайте це виправимо.
1.3. Розставляємо міни
Нам потрібно випадково розмістити на полі задану кількість мін. Для цього додамо новий метод (функцію) _place_minesусередину нашого класу GameBoard.
def _place_mines(self):
# Создаем список всех возможных координат(строка, столбец)
all_coords = [(r, c) for r in range(self.height) for c in range(self.width)]
# Выбираем из списка случайные уникальные координаты для мин
mine_coords = random.sample(all_coords, self.mines_count)
# В ячейках по этим координатам ставим мины
for r, c in mine_coords:
self.board[r][c].is_mine = True
Ми використали random.sample– це зручний спосіб вибрати кілька випадкових елементів зі списку, при цьому він гарантує, що всі вони будуть унікальними. Так ми уникнемо ситуації, коли дві міни потрапили в один і той самий осередок.
1.4. Вважаємо цифри навколо мін
Це найважливіша частина логіки. Нам потрібно пройти по кожному осередку поля. Якщо в ній немає міни, ми повинні порахувати, скільки мін знаходиться у 8 сусідніх осередках.
Додайте наступний метод _calculate_adjacent_minesдо класу GameBoard:
def _calculate_adjacent_mines(self):
# Проходим по каждой ячейке поля
for r in range(self.height):
for c in range(self.width):
# Если в ячейке уже есть мина, считать ничего не нужно
if self.board[r][c].is_mine:
continue
mine_count = 0
# Проверяем всех 8 соседей
for dr in [-1, 0,1]: # Смещение по строке
for dc in [-1, 0, 1]: # Смещение по столбцу
# Пропускаем саму текущую ячейку
if dr == 0 and dc == 0:
continue
# Вычисляем координаты соседа
nr, nc = r + dr, c + dc
# ВАЖНО: Проверяем, что сосед не вышел за границы поля
if 0 <= nr < self.height and 0 <= nc < self.width:
if self.board[nr][nc].is_mine:
mine_count += 1
# Записываем результат в ячейку
self.board[r][c].adjacent_mines = mine_count
І останнє – давайте зробимо так, щоб міни та цифри розставлялися автоматично одразу при створенні поля. Для цього просто викличемо наші нові методи в __init__.
Обновіть метод __init__у класі GameBoard, додавши в кінець два нові рядки:
def __init__(self, width, height, mines_count):
self.width = width
self.height = height
self.mines_count = mines_count
self.board = [[Cell() for _ in range(width)] for _ in range(height)]
# Добавляем эти две строки:
self._place_mines()
self._calculate_adjacent_mines()
Крок 2. Перше вікно – малюємо наше ігрове поле
Отже, "мозок" нашої гри готовий, але поки що він працює невидимо. Час дати йому "тіло" - графічне відображення! У цьому кроці ми створимо вікно гри та намалюємо у ньому сітку, яка представляє наше ігрове поле.
Наш план:
- Написати базовий код для запуску вікна Pygame.
- Встановити основні параметри: розміри осередків, кольори.
- Створити функцію, яка буде малювати сітку на основі даних нашого GameBoard.
- Зібрати все разом у головний ігровий цикл.
- Продовжуємо працювати у нашому файлі main.py.
2.1. Константи та базовий запуск Pygame
Хороша практика в програмуванні - виносити значення, які можуть часто змінюватися (на зразок кольорів або розмірів), в окремі змінні на початку файлу. Їх називають константами (і часто пишуть ВЕЛИКИМИ_ЛІТами).
Додайте цей код в самий низ вашого файлу main.py після всіх класів.
import pygame
# --- Константы ---# Размеры поля в ячейках
BOARD_WIDTH = 20
BOARD_HEIGHT = 15
MINES_COUNT = 30
# Размер одной ячейки в пикселях
CELL_SIZE = 30
# Рассчитываем размер окна в пикселях
SCREEN_WIDTH = BOARD_WIDTH * CELL_SIZE
SCREEN_HEIGHT = BOARD_HEIGHT * CELL_SIZE
# Цвета (в формате RGB)
BG_COLOR = (192, 192, 192) # Серый
LINE_COLOR = (128, 128, 128) # Темно-серый
# --- Инициализация Pygame и создание окна ---
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Сапёр")
# --- Создание игрового поля ---
game_board = GameBoard(BOARD_WIDTH, BOARD_HEIGHT, MINES_COUNT)
Що ми тут зробили:
- Імпортували pygame.
- Встановили всі основні налаштування нашої гри. Тепер, якщо ви захочете поле більше або менше, достатньо буде змінити цифри тут.
- Ініціалізували Pygame та створили вікно потрібного розміру.
- Створили один екземпляр нашого "мозку" - game_board. Саме його ми й малюватимемо.
2.2. Функція для відображення поля
Давайте створимо окрему функцію, яка відповідатиме лише малювання. Це робить код більш чистим та організованим.
Додайте цю функцію main.py після блоку з константами:
def draw_board(board_obj):
# Заливаем весь экран фоновым цветом
screen.fill(BG_COLOR)
# Проходим по каждой ячейке
for r in range(board_obj.height):
for c in range(board_obj.width):
# Рассчитываем координаты ячейки на экране (в пикселях) x = c * CELL_SIZE
y = r * CELL_SIZE
# Создаем прямоугольник для ячейки
rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE)
# Рисуем контур ячейки
pygame.draw.rect(screen, LINE_COLOR, rect, 1)
Ця функція:
- Заливає екран сірим кольором, щоб стерти попередній кадр.
- У подвійному циклі проходить по кожному осередку.
- Обчислює, де на екрані (у пікселях) потрібно намалювати поточну комірку.
- Малює для кожної комірки прямокутник із чорним контуром товщиною в 1 піксель.
2.3. Головний ігровий цикл
Будь - яка гра працює всередині нескінченного циклу . На кожному витку цього циклу гра:
- Перевіряє дії гравця (чи він натиснув кнопку, чи закрив вікно).
- Оновлює логіку гри.
- Перемальовує екран.
Цей цикл – серце нашої програми. Додайте його до кінця файлу main.py:
# --- Главный игровой цикл ---
running = True
while running:
# 1. Обработка событий
for event in pygame.event.get():
# Если пользователь нажал на "крестик"
if event.type == pygame.QUIT:
running = False
# 2. Отрисовка
draw_board(game_board)
# 3. Обновление экрана
pygame.display.flip()
# Корректное завершение работы
pygame.quit()
Тепер, якщо ви запустите ваш файл main.pyіз терміналу ( python main.py), ви повинні побачити вікно із сірою сіткою!
(venv) ...\minesweeper> python main.pyКрок 3. Пожвавлюємо гру – вчимо її слухати мишку
- Знайти у нашому ігровому циклі місце, де відловлюються всі дії гравця.
- Додати код для відстеження кліків миші.
- Написати просту формулу для перетворення координат кліка (у пікселях) на координати комірки (номер рядка та стовпця).
- Перевірити, що все працює, виводячи результат у термінал.


Коментарі
Дописати коментар