Пример исходного кода можно загрузить ЗДЕСЬ. Пример разработан на С++ WinAPI Visual Studio 2019. Код хорошо прокомментирован.
На игровом поле Тетрис находятся фигуроки, которые называються Тетрамино (как сказано в Википедии).
Тетрамино из игры Тетрис представлены на рисунке ниже.
Всего в игре Тетрис может быть 7 таких фигурок.
Каждая фигурка Тетрамино состоит из 4 квадратов.
Самая большая фигрука Тетрамино по высоте занимает 4 квадрата, и самая большая фигурка по ширине занимает 2 квадрата. Поэтому очень удобно хранить описание одного Тетрамино так как представлено на рисунке ниже.
Для хранения всех фигурок тетрамино можно задать массив 7х4 где 7 это количество Тетрамино в Тетрисе, а 4 это максимальное количество квадратов из которых состоит одно Тетрамино.
Причем не важно в каком порядке следуют закрашенные квадраты на рисунке выше, важен лишь сам факт- закрашен квадрат или нет. Поэтому для первой фигуры к квадратами (3, 5, 4, 6) в соответствие можно поставить как набор (5, 3, 4, 6) так и (3, 5, 6, 4).
Игровое поле можно представить в виде массива целых чисел. В нашем примере по ширине поле имеет 10 квадратов, и по высоте 20 квадратов (рисунок ниже).
Подитожим наши знания и рассмотрим исходный код примера (файл MyApp.h).
#define SCALE 30 //размер одного квадрата на игровом поле в пикселях #define N 10 //ширина игрового поля в квадратах #define M 20 //высота игрового поля в квадратах //вычисляем размер окна для игры Тетрис в пикселях //вычисления необходимы для задания размера //окна приложения WinAPI функция CreateWindow() #define WINDOW_WIDTH N * SCALE #define WINDOW_HEIGHT M * SCALE //задаем игровое поле Тетриса (размер в квадратах не в пикселях) int field[M][N] = { 0 }; //массив с описанием всех 7 фигурок Тетрамино int figures[7][4]= { 1,3,5,7, // I 2,4,5,7, // S 3,5,4,6, // Z 3,5,4,7, // T 2,3,5,7, // L 3,5,7,6, // J 2,3,4,5, // O };
Следущий шаг - привязка Тетрамино к игровому полю. Для этого нужно локальные координаты Тетрамино перевести в координаты игрового поля. На экране отсчет координат начинаеться в верхнем левом углу, как на рис. ниже.
Для примера размещения Тетрамино на игровом поле возьмем Z тетрамино (рис.выше). Видно, что квадрат Тетрамино №5 будет иметь координаты (1;2), квадрат №6 будет иметь координаты (0;3) и т.д.
Добавим немного математики - если номер клетки Тетрамино разделить с остатком на 2 получим столбец, колонку если делить 2 нацело. Тогда квадратик Тетрамино №5 будет иметь координаты (1;2) = (5 % 2; 5 / 2). В С++ программировании оператор % — это остаток от деления, а оператор / — частное от деления (деление нацело)
Следущий шаг отображение Тетрамино на игровом поле. Для этого создадим структуру Point.
struct Point { int x, y; } a[4],b[4];
Затем с помощью первого цикла for мы переведем «локальные» координаты каждого отдельного кусочка тетрамино в «глобальные», а затем с помощью второго цикла for отобразим это всё на игровом поле. При этом стоит учитывать, что размеры отдельного квадрата Тетрамино составляют 30×30 пикселей.
void CMyApp::RenderScene() { static bool begin_Game = true; //1)создаем тетримино на игровом поле //переводим локальные координаты тетрамино //в глобальные координаты игрового поля //перемення n хранит номер тетрамино //в массиве (т.е. какой тетрамино рисовать) if(begin_Game) { begin_Game = false; srand((unsigned)time( NULL )); n = rand() % 7; for (int i = 0; i < 4; i++) { a[i].x = figures[n][i] % 2; a[i].y = figures[n][i] / 2; } } //2)рисуем тетрамино которое движется вниз используя функцию WinAPI Rectangle() for (int i = 0; i < 4; i++) { hBrush = CreateSolidBrush(RGB(colors[n].r, colors[n].g, colors[n].b)); hOldBrush = (HBRUSH)SelectObject(hBackBuffer, hBrush); // Устанавливаем позицию каждого кусочка тетрамино Rectangle( hBackBuffer, a[i].x * SCALE, a[i].y * SCALE, (a[i].x + 1) * SCALE, (a[i].y + 1) * SCALE ); SelectObject(hBackBuffer, hOldBrush); DeleteObject(hBrush); DeleteObject(hBrush); }
На данном этапе мы нарисовали одно тетрамино (см.рисунок ниже).
Как устроено вращение одного тетрамино. Тут пригодятся знания линейной алгебры- вращение 2D.
//вращение вокруг точки с координатами x_0 и y_0 //вращаем точку с координатами x и y //вращение 2D то есть вокруг оси Z //результат поворота точки (x,y) вокруг (x_0, y_0) будет храниться в (X,Y) X = x_0 + (x − x_0) * cos(a) − (y − y_0 ) * sin(a); Y = y_0 + (y − y_0) * cos(a) + (x − x_0 ) * sin(a);
В Тетрисе все повороты фигур идут на 90 градусов. Известно что:
sin(90 degree) = 1 cos(90 degree) = 0
Поэтому вышеприведенную формулу поворота вокруг оси Z можно сократить, и она будет выглядеть так:
X = x_0 − (y − y_0); Y = y_0 + (x − x_0);
Вертикальное перемещение тетрамино осуществляется 1 клетка вниз раз в 20 кадров. В игре стоит ограничивающая FPS функция до 30 кадров в секунду. В ускоренном режиме тетрамино движеться 1 клетка вниз за 1 кадр.
Сначала в игре рисуется все игровое поле, а после рисуется одно текущее тетрамино которое движется вниз.
Данный пример в статье реализован на C++ WinAPI Visual Studio 2019. Оригинал статьи предлагает реализацию на Visual Studio 2015 и при помощи библиотеки SFML.
Часть 1 оригинальной статьи находиться здесь Часть №1: Создание игры «Тетрис» на С++/SFML
Часть 2 оригинальной статьи находиться здесь Часть №2: Создание игры «Тетрис» на C++/SFML
Часть 3 оригинальной статьи находиться здесь Часть №3: Создание игры «Тетрис» на С++/SFML