Как сделать игру тетрис на Python
Показываем и рассказываем, как написать тетрис на Python с помощью модуля Pygame.
Классический тетрис появился в 1984 году — но до сих пор будоражит умы людей со всего мира. В тетрис и по сей день играют миллионы, используя различные гаджеты и вычислительные машины.
Игру любят за четкость, достижимость цели и наглядную «обратную связь». В то же время тетрис полезен: развивает скорость реакции и пространственное мышление, учит концентрироваться на задаче и абстрагироваться от внешних раздражителей, успокаивает нервы и повышает производительность работы мозга.
Знать, как устроена любимая игра — захватывающе. Конструирование тетриса на языке Пайтон — способ испытать себя и усовершенствовать навыки программирования для начинающего кодера.
В конце инструкции — ссылка на тематический видеоурок: просмотр закрепит знания.
Начинаем воплощать тетрис на Пайтоне
Сразу напомним: модуль Pygame изначально не встроен в Python. Если он не установлен, проще это сделать в консоли командой pip install pygame.
Первым делом импортируем нужные библиотеки: pygame, random, copy. Copy понадобится, чтобы разобраться с одной особенностью хранения в Python.
После чего активируем модули Pygame и зададим 2 параметра сетки — I_max и J_max. Сетка строится по такому принципу: число ячеек равно числу точек -1. Можете убедиться в этом, посчитав на рисунке или нарисовав сами:
Задав число точек I_max и J_max 11 и 21, получим 11 и 20 ячеек соответственно.
Добавляем переменные
Зададим 2 переменные с параметрами экрана 300 на 600. После сделаем экран, туда запишем переменные, а также создадим класс Clock(). Он понадобится для работы со временем — с частотой обновления экрана.
Сосчитаем шаг по x и по y для сетки — ширину и высоту ячейки. Делим на I_max -1, J_max -1: область необходимо разделить на число ячеек, а оно как раз на 1 меньше числа точек. Создаем переменную для частоты обновления кадров.
Как сделать тетрис на Python: применяем списки и циклы
Делаем список, который будет хранить внутри себя параметры сетки, отображенные в окне. Добавим по списку на каждом элементе по ширине и на каждом элементе по высоте. Сразу занесем туда 1 — позднее станет понятно, зачем это сделано.
Кроме 1, заполним списки сущностями прямоугольников — rect, а также цветом для каждого из них. В координатах прямоугольников пишем координаты, где они должны находиться. Это можно сосчитать, умножив номер точки на шаг — ширину или высоту ячейки.
Пора запустить бесконечный цикл, заполнить экран для его обновления и нарисовать сетку. Для этого снова пройдемся по элементам сетки и нарисуем каждый квадратик. Третий элемент списка — 2: это цвет. Второй — 1: сущность прямоугольника, созданного ранее. Тип заполнения — 0: когда стоит 1, у прямоугольника нарисована граница шириной 1. Если бы поставили 0, он бы заполнился полностью.
Не забудьте добавить событие нажатия на крестик в игре.
Переходим к деталям
Тетрис — игра про детали. Потому рекомендуем посмотреть на 7 оригинальных фигур тетриса:
Распишем их координаты в списке. Будем придерживаться того, что каждый третий — 0,0 элемент, который в будущем примем за ось вращения.
Создадим список уже не с координатами, а с самими деталями. Для этого делаем список det: каждый элемент будем заполнять 4 прямоугольниками. Координаты берем из списка с координатами и создаем детали в центре экрана сверху.
Посмотрим, чем заполняется список, выведем это в консоль. Видим, что в нем 7 списков, по 4 прямоугольника в каждом — что и хотели получить.
Настроим случайный выбор детали из списка и ее отрисовку в программе. Каждый кадр проводится циклом по 4 прямоугольникам внутри детали. Прямоугольники будут отрисовываться.
Как создать тетрис на Python: проектируем движения
Для создания движения будем брать рандомно выбранную деталь det_choice и менять координаты у 4 прямоугольников на шаг dx и dy.
Для примера напишем 1*dx и посмотрим, что будет. Деталь быстро улетела вправо. Если поменяем на -1 — быстро улетит влево. Будем менять данные параметры с 0 на 1 и -1 при взаимодействии с клавишами стрелки вправо или стрелки влево. Создадим event нажатия на клавишу: если нажата стрелка влево, меняем переменную delta_x на -1. Для правой стрелки аналогично, только +1.
Теперь нужно настроить границы, чтобы за них нельзя было выходить. Для этого зададим условие: если хотя бы одна из деталей на следующей клетке (на следующем шаге) за пределами границ экрана, то delta_x зануляем, движения не произойдет.
Таким же образом сделаем перемещение по y. Для замедления включаем передвижение только каждые 30 обновлений. Для этого будем сохранять число обновлений в переменной.
Добавим вариант ускорения передвижения по y, если хотим резко уронить деталь и не ждать ее медленного падения. Чтобы зажимать клавишу, а не нажимать по кнопке много раз, используем функцию get_pressed(). Если ключ, который нажмет человек, совпадает с pygame.K_DOWN, т.е. стрелкой вниз, число обновлений сразу зададим равным 31.
Настроим границы для y: если на следующем шаге по y хотя бы одна деталь выйдет за нижнюю границу, остановим движение.
Остановили движение — зареспавним еще одну деталь. Для этого закрасим клетки сетки, где осталась фигура, белым цветом — зададим параметр заливки равным 0, а цвет изменим с серого на белый и сделаем новый выбор детали.
Исправляем ошибки
После проверки в игре обнаружен баг — деталь появляется только 7 раз, после этого ничего не происходит. Это связано с хранением информации в Python: меняя координаты деталей, меняем и список — фигуры сразу возникают в самом низу. Чтобы такого не происходило, будем делать копию детали из списка и менять уже координаты копии, а не фигуры.
Теперь их появляется уже больше 7 штук, но они не ставятся друг на друга. Это следует исправить: пропишем условие в границы: если следующая снизу ячейка сетки закрашена, останавливаем движение.
Доделываем игру тетрис на Python
Основные механики готовы, осталось только внедрить пару вещей из оригинальной игры: поворот деталей и снос заполненного ряда.
Начнем с поворота. Его сделаем по стандартной математической формуле поворота на 90 градусов в декартовой системе координат — относительно центра в (0,0). Центр записан в третьей ячейке, поэтому обозначаем как центр второй индекс det_choice[2].
Создадим проверку на контакт с клавишей стрелки вверх — при ее нажатии переменная rotate становится равной True, выполняется условие. Проходимся по элементам квадратика и смещаем их относительно центра вращения, после чего переписываем rotate на False.
Последний штрих — снос заполненного ряда. Для этого запустим цикл, проверяющий, насколько заполнен каждый ряд. Идти будем снизу вверх и слева направо, считая число заполненных ячеек: в этом случае параметр заполнения равен 0. Если же хотя бы одна ячейка не равна 0, можем сказать, что полоска не заполнена — а затем завершить цикл на этом ряду и перейти к проверке следующего.
Если число заполненных ячеек равно числу ячеек в целом, запускаем еще 2 цикла. Сначала ячейки верхнего ряда делаем незакрашенными, а после все ячейки рядов смещаем на ряд вниз, начиная с заполненного.
Надеемся, вы попробовали написать данный код и улучшить его — поверьте, здесь есть потенциал для совершенствования.
Что дальше
Как и обещали, делимся YouTube-видеороликом о том, как сделать игру тетрис на Пайтоне: он поможет пройтись по изученному материалу. Также на канале находится плейлист с бесплатными роликами, посвященными реализации игр на Python. Стоит посмотреть, чтобы вдохновиться на конструирование игр.
Отточить мастерство геймдева и программирования на Питоне также можно на специальных курсах под присмотром опытного преподавателя.
- Ребятам 10–14 лет подойдет курс изучения языка Пайтон. Проштудировав азы разработки, подросток напишет квест или викторину, запустит чат-бота для мессенджера или игру с функцией стрельбы, бонусами и подсчетом очков.
- Любителям Майнкрафта 9–13 лет понравится курс по кодингу на Python в Minecraft. Ребенок применит знания в любимой вселенной, написав внутриигровые программы или придумав персонажа с искусственным интеллектом.
Изучая разработку на Питоне любым из методов, ребенок приобретет полезные качества и умения, которые пригодятся в жизни и при построении карьеры в удивительном мире IT.
Полный код программы:
import pygame
import random
import copy
pygame.init()
I_max = 11
J_max = 21
screen_x = 300
screen_y = 600
screen = pygame.display.set_mode((screen_x, screen_y))
clock = pygame.time.Clock()
pygame.display.set_caption("Tetris Pixel Game")
dx = screen_x/(I_max - 1)
dy = screen_y/(J_max - 1)
fps = 60
grid = []
for i in range(0, I_max):
grid.append([])
for j in range(0, J_max):
grid[i].append([1])
for i in range(0, I_max):
for j in range(0, J_max):
grid[i][j].append(pygame.Rect(i*dx, j*dy, dx, dy))
grid[i][j].append(pygame.Color("Gray"))
details = [
[[-2, 0], [-1, 0], [0, 0], [1, 0]],
[[-1, 1], [-1, 0], [0, 0], [1, 0]],
[[1, 1], [-1, 0], [0, 0], [1, 0]],
[[-1, 1], [0, 1], [0, 0], [-1, 0]],
[[1, 0], [1, 1], [0, 0], [-1, 0]],
[[0, 1], [-1, 0], [0, 0], [1, 0]],
[[-1, 1], [0, 1], [0, 0], [1, 0]],
]
det = [[],[],[],[],[],[],[]]
for i in range(0, len(details)):
for j in range(0, 4):
det[i].append(pygame.Rect(details[i][j][0]*dx + dx*(I_max//2), details[i][j][1]*dy, dx, dy))
detail = pygame.Rect(0, 0, dx, dy)
det_choice = copy.deepcopy(random.choice(det))
count = 0
game = True
rotate = False
while game:
delta_x = 0
delta_y = 1
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
delta_x = -1
elif event.key == pygame.K_RIGHT:
delta_x = 1
elif event.key == pygame.K_UP:
rotate = True
key = pygame.key.get_pressed()
if key[pygame.K_DOWN]:
count = 31 * fps
screen.fill(pygame.Color("Black"))
for i in range(0, I_max):
for j in range(0, J_max):
pygame.draw.rect(screen, grid[i][j][2], grid[i][j][1], grid[i][j][0])
#границы
for i in range(4):
if ((det_choice[i].x + delta_x * dx < 0) or (det_choice[i].x + delta_x * dx >= screen_x)):
delta_x = 0
if ((det_choice[i].y + dy >= screen_y) or (grid[int(det_choice[i].x//dx)][int(det_choice[i].y//dy) + 1][0] == 0)):
delta_y = 0
for i in range(4):
x = int(det_choice[i].x // dx)
y = int(det_choice[i].y // dy)
grid[x][y][0] = 0 #закрашиваем квадратик
grid[x][y][2] = pygame.Color("White")
detail.x = 0
detail.y = 0
det_choice = copy.deepcopy(random.choice(det))
#передвижение по x
for i in range(4):
det_choice[i].x += delta_x*dx
count += fps
#передвижение по y
if count > 30 * fps:
for i in range(4):
det_choice[i].y += delta_y*dy
count = 0
for i in range(4):
detail.x = det_choice[i].x
detail.y = det_choice[i].y
pygame.draw.rect(screen, pygame.Color("White"), detail)
C = det_choice[2] #центр СК
if rotate == True:
for i in range(4):
x = det_choice[i].y - C.y
y = det_choice[i].x - C.x
det_choice[i].x = C.x - x
det_choice[i].y = C.y + y
rotate = False
for j in range(J_max - 1, -1, -1): #цикл по рядам снизу вверх
count_cells = 0
for i in range(0, I_max): #цикл по столбцам справа налево
if grid[i][j][0] == 0:
count_cells += 1
elif grid[i][j][0] == 1:
break
if count_cells == (I_max - 1):
for l in range(0, I_max):
grid[l][0][0] = 1 #все ячейки первого ряда
for k in range(j, -1, -1):
for l in range(0, I_max):
grid[l][k][0] = grid[l][k-1][0]
pygame.display.flip()
clock.tick(fps)