Графическая библиотека SFML. Создание Тетриса – Часть №1

  Дмитрий Бушуев  | 

  Обновл. 8 Ноя 2019  | 

 1685

 ǀ   7 

На написание данной статьи меня побудил ролик c YouTube-а, в котором автор за 4 минуты с нуля, используя графическую библиотеку SFML, создаёт рабочий прототип Тетриса. Всё это происходило в ускоренном воспроизведении под веселую вариацию оригинальной тетрис-мелодии «Коробейники». За всё это время автор не произнёс ни слова, а сам ролик содержал несколько довольно неочевидных моментов, которые требовали пояснений. В связи с этим я решил взять на себя ответственность и восполнить данный пробел.

Первые шаги

Как вы уже знаете, практически любое SFML-приложение начинается с минимального каркаса:

Далее мы создаём спрайт для представления различных элементов фигур в нашей игре и присваиваем ему файл текстуры — tiles.png. Сам файл текстуры у меня располагается по пути C:\dev\SFML_Tutorial\images\. Текстура являет собой 8 разноцветных квадратов, горизонтально стоящих друг за другом. Размер каждого такого квадрата составляет 18х18 пикселей.

Ниже представлен код, который подгружает эту текстуру в программу:

Если мы скомпилируем и запустим наш проект, то увидим следующее:

Теперь нам нужно выделить из всего спрайта отдельный квадратик, чтобы можно было его использовать для построения нужных нам фигур Тетриса. Для этого воспользуемся методом setTextureRect():

В результате мы получим следующее:

Работа с фигурками Тетриса


Следует вопрос: «А как мы можем программным образом представить наши фигурки?». Давайте рассмотрим их детальнее. Из Википедии можно узнать, что наши фигурки называются тетрамино, т.к. каждая такая фигура состоит из 4 квадратов. Всего же в Тетрисе используется 7 таких фигурок-тетрамино:

Поэтому вполне логично описать их в виде целочисленного двумерного массива с 7 строками (по числу фигур) и 4 столбцами, задающих форму каждой конкретной фигуры. Возникает ещё один вопрос: «А каким образом мы будем задавать форму тетрамино?».

Ответ вы найдёте ниже:

Заметьте, нам не важен порядок следования закрашенных квадратов. Важен лишь сам факт: закрашен квадрат или нет. Поэтому первой фигуре с множеством «точек» (3, 5, 4, 6) в соответствие можно поставить как набор (5, 3, 4, 6), так и набор (3, 5, 6, 4).

Игровое поле тоже можно представить в виде целочисленного массива field[M][N], где M — это высота поля, а N — его ширина. Добавим эти элементы в наш код:

Привязка тетрамино к координатам игрового поля

Итак, у нас есть массив, в котором содержатся все тетрамино. Следующим шагом будет их отображение на игровом поле. А для этого нужно каким-то образом связать локальные координаты, которыми задаются фигурки-тетратимино, с глобальными координатами игрового поля. Для решения данной задачи в качестве примера рассмотрим Z-тетрамино. Напомню, что отсчёт координат игрового поля начинается с верхнего левого угла:

Видно, что, например, квадрат №5 будет иметь координаты (1;2), квадрат №6 будет иметь координаты (3;0) и т.д. Но как описать данные факты математически? Чтобы разобраться с этим вопросом, сделаем небольшое лирическое отступление. Для этого возьмите листик и начните выписывать в строчку числа от 0 до 40 так, чтобы в каждой строке было ровно 10 чисел.

У вас должно получиться что-то следующее:

0 1 2 3 9
10 11 12 13 19
20 21 22 23 29
30 31 32 33 39

Сразу бросается в глаза, что в числах каждой отдельно взятой строки содержится одинаковое количество десятков. Например, в первой строке (числа 0, 1, 2, 3, … ) у нас 0 десятков, во второй строке (числа 10, 11, 12, 13, …) у нас по одному десятку в каждом числе, в третьей строке (числа 20, 21, 22, 23, …) у нас уже по 2 десятка в каждом числе и т.д.

Если теперь рассмотреть столбцы, то номера столбцов соответствуют остатку от деления на 10, содержащемуся в числе из этого столбца. Например, возьмём столбец №1 и число 21. Если мы 21 нацело разделим на 10, то получим в результате 2 с остатком 1 (21 = 2 * 10 + 1), что соответствует номеру столбца. Если возьмем столбец №9 и число 39, также разделим его нацело на 10, то получим в результате 3 с остатком 9 (39 = 3 * 10 + 9), что также соответствует номеру столбца. В итоге, если продолжать этот процесс, то наша исходная таблица примет следующий вид:

0 1 2 3 9
0 0=0*0+0 1=0*0+1 2=0*0+2 3=0*0+3 9=0*0+9
10 10=1*10+0 11=1*10+1 12=1*10+2 13=1*10+3 19=1*10+9
20 20=2*10+0 21=2*10+1 22=2*10+2 23=2*10+3 29=2*10+9
30 30=3*10+0 31=3*10+1 32=3*10+2 33=3*10+3 39=3*10+9

Здесь уже становится понятно, что остаток от деления на 10 задаёт расположение числа по горизонтали, а количество десятков (частное от деления на 10) — задаёт расположение по вертикали. Если вспомнить, что в программировании оператор % — это остаток от деления, а оператор / — частное от деления (деление нацело), то, например, число 32 в нашей таблице будет иметь координаты (2;3) = (32 % 10; 32 / 10).

Вернёмся к нашей исходной задаче:

Т.к. здесь в каждой строке не 10 чисел, а 2 числа, то делить нужно на 2, а не на 10. И тогда квадратик №5 будет иметь координаты (1;2) = (5 % 2; 5 / 2).

Отображение тетрамино на игровом поле


А сейчас попробуем отобразить наши фигурки на игровом поле. Для этого, прежде всего, нужно создать структуру Point, которая будет представлять точку с целыми координатами + два вспомогательных массива a[] и b[]:

Затем, с помощью одного цикла for, мы переведём «локальные» координаты каждого отдельного кусочка тетрамино в «глобальные», а затем, с помощью второго цикла for, отобразим это всё на игровом поле. При этом стоит учитывать, что размеры отдельного кусочка равны 18х18 пикселей:

Результат:

Заключение

На этом предлагаю пока остановиться. В следующем уроке мы разберёмся с реализацией поворотов тетрамино и их движением по горизонтали. Надеюсь, что вам понравилось. До встречи! 🙂

Исходный код. Создание Тетриса — Часть №1


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (7 оценок, среднее: 5,00 из 5)
Загрузка...

Комментариев: 7

  1. Аватар Василий:

    Здравствуйте. Скажите пожалуйста, будет ли продолжение урока создания тетриса?

    1. Дмитрий Бушуев Дмитрий Бушуев:

      Добрый день. Конечно будет. Вторая часть статьи уже передана на корректуру. В ближайшее время она появится на сайте.

  2. Аватар Penguin:

    Ох-ох-ох! Что то понимание отрисовки даётся мне тяжко) Ну и вот эта запись a[i].x — я вот не понимаю её) а — массив — i элемент, а вот точка x мне не понять) Эх) Буду разбираться)

    1. Дмитрий Бушуев Дмитрий Бушуев:

      Вот смотрите. У вас же a[i] — это массив структур типа Point. А у каждой структуры Point есть свои внутренние поля: int x; int y. Вот как раз таки доступ к этим полям и осуществляется через точку: a[i].x или a[i].y

      Советую почитать https://ravesli.com/urok-61-struktury/
      🙂

      1. Аватар Penguin:

        Вот спасибо большое, перечитаю ещё пару раз, вдруг дойдёт)

      2. Аватар Penguin:

        Разобрался! Спасибо большое за ваш труд) И за ответ)

        Очень буду ждать ваши следующие работы. Очень понятным языком, очень приятно читать. Прям вот мана небесная ещё и прожёванная)

        Надеюсь у вас не возникнут проблем с написанием статей, а то как это мы без таких знаний? Английский то ещё до сих пор хромает на обе ноги, если не в коляске инвалидной катается))

        1. Дмитрий Бушуев Дмитрий Бушуев:

          Всегда пожалуйста 🙂

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *