テトリスゲームに必要な処理は、
次の通りです。
1.ピースを用意する処理
2.ピースを移動する処理
3.ピースを回転する処理
4.ピースを固定する処理
5.行を消す処理
6.描画する処理
〇ピースの種類
5x5の配列を使って、TRUE(赤)、FALSEで表現しています。
配列の内容
ピースは(2,2)を軸に時計方向に回転します。
〇ゲーム画面
〇プログラム例#include <windows.h>
/* フィールドの横と縦のマス数*/#define FIELD_WIDTH 14#define FIELD_HEIGHT 24/* MovePieceの引数*/#define PIECE_LEFT 2#define PIECE_RIGHT 4#define PIECE_DOWN 8/* フィールドのマスのテキスト表現*/#define PIECE_FALSE TEXT("□")#define PIECE_TRUE TEXT("■")HWND hMainWindow = NULL;int field[FIELD_WIDTH][FIELD_HEIGHT] = { 0 }; /* ゲームフィールド*/int currentPiece[5][5] = { 0 }; /* 現在移動中のブロック*/POINT pieceLocation = { 0,0 }; /* ブロックの位置*/int currentPieceNumber; /*現在のピース番号*/BOOL MovePiece(int); /* ブロックの移動*/BOOL TurnPiece(VOID); /* ブロックの回転*/VOID NextPiece(VOID); /*次のブロックを出現*/VOID PieceToField(VOID); /*ブロックの位置と内容をfield配列変数にコピーする*/int AdjustLine(VOID); /* 横一列がピースで埋まっている場合の処理を実行する*/int GetPieceTop(VOID); /*ブロック内のピースの最上部の位置を返す*/int GetPieceBottom(VOID);/*ブロック内のピースの最下部の位置を返す*/int GetPieceLeft(VOID);/*ブロック内のピースの最左側の位置を返す*/int GetPieceRight(VOID);/*ブロック内のピースの最右側の位置を返す*//* ブロック内のピースの最上部の位置を返す*/int GetPieceTop() {int x, y, length = 3; // 中央の位置を3とするfor (y = 1; y >= 0; y--) { // 上に向かって調べるfor (x = 0; x < 5; x++) {if (currentPiece[x][y]) {length--;break; //ピースが見つかったらx方向ループから出る。}}}return length;// 最小1}/* ブロック内のピースの最下部の位置を返す*/int GetPieceBottom() {int x, y, length = 3; // 中央の位置を3とするfor (y = 3; y < 5; y++) { // 下へ向かって調べるfor (x = 0; x < 5; x++) {if (currentPiece[x][y]) {length++;break; //ピースが見つかったらy方向ループから出る。}}}return length; //最大5}/* ブロック内のピースの一番左側の位置を 返す */int GetPieceLeft() {int x, y, length = 3; // 中央の位置を3とするfor (x = 1; x >= 0; x--) { //左へ向かって調べるfor (y = 0; y < 5; y++) {if (currentPiece[x][y]) {length--;break; //ピースが見つかったらy方向ループから出る。}}}return length; //最小1}/* ブロック内のピースの一番右側の位置を返す*/int GetPieceRight() {int x, y, length = 3; // 中央の位置を3とするfor (x = 3; x < 5; x++) { //右へ向かって調べるfor (y = 0; y < 5; y++) {if (currentPiece[x][y]) {length++;break; //ピースが見つかったらy方向ループから出る。}}}return length; //最大5}/* ピースを位置情報に従ってフィールドにコピーする */VOID PieceToField(VOID) {int x, y;for (y = 0; y < 5; y++) {for (x = 0; x < 5; x++) {if (currentPiece[x][y])field[pieceLocation.x + x][pieceLocation.y + y] = currentPiece[x][y];}}}/* ピースの移動判定*/BOOL MovePiece(int move) {if (move == PIECE_LEFT) { // 左に対してint left = GetPieceLeft();if (pieceLocation.x > -(left - 1)) { // ブロック内のピースの左の位置が左壁のどうなのかint x, y, count = 0;for (y = 0; y < 5; y++) {for (x = 0; x < 5; x++) {// 各ピースを確認if (currentPiece[x][y] && field[pieceLocation.x + x - 1][pieceLocation.y + y]) {return FALSE; // 左に何かピースがある。}}}pieceLocation.x--; // ブロックを左に移動}else {return FALSE; // 左枠に当たった。}}else if (move == PIECE_RIGHT) { // 右側にたいしてint right = GetPieceRight();if (pieceLocation.x < (FIELD_WIDTH - right)) { // 右側の枠より右かどうかint x, y, count = 0;for (y = 0; y < 5; y++) {for (x = 0; x < 5; x++) {if (currentPiece[x][y] && field[pieceLocation.x + x + 1][pieceLocation.y + y]) {return FALSE; // 右に何かピースがある。}}}pieceLocation.x++; // ブロックを右に移動}else {return FALSE; // 左側の枠に当たった。}}else if (move == PIECE_DOWN) { // 下へ移動できるかどうかint bottom = GetPieceBottom();// 枠内かどうか確認するif (pieceLocation.y < (FIELD_HEIGHT - bottom)) {int x, y, count = 0;for (y = 0; y < 5; y++) {for (x = 0; x < 5; x++) {// 現在のブロック内の各ピースを確認if (currentPiece[x][y] && field[pieceLocation.x + x][pieceLocation.y + y + 1]) {return FALSE; // 下にブロックがある。}}}pieceLocation.y++; // ブロックの位置を下げる。}else {return FALSE; // 下の枠に当たった。}}return TRUE;}/* ブロックを回転させる*/BOOL TurnPiece() {int x, y;int copy[5][5];/*回転したブロックを生成する*/for (y = 0; y < 5; y++) { // 右回りに回転させる。for (x = 0; x < 5; x++) {copy[4 - y][x] = currentPiece[x][y];}}/* 回転可能かどうか調べる*/for (y = 0; y < 5; y++) {for (x = 0; x < 5; x++) {if (copy[x][y]) { // ピースがある場合int offsetX = pieceLocation.x + x;int offsetY = pieceLocation.y + y;if ((offsetX < 0) || // 左枠(offsetX >= FIELD_WIDTH) || // 右枠(offsetY >= FIELD_HEIGHT) || // 下枠field[offsetX][offsetY]) // ピースが重なる場合return FALSE; // 回転できない}}}memcpy(currentPiece, copy, sizeof(int) * 25); //回転したブロックを現在のブロックにするreturn TRUE; // 回転できた}/*各行を調べ、行が埋まっている場合は行を削除する*/int AdjustLine() {int x, y, delCount = 0;for (y = FIELD_HEIGHT - 1; y >= 0;) { // 行でループ(下から)int iy, lineCount = 0;for (x = 0; x < FIELD_WIDTH; x++) { // 列でループif (field[x][y]) // ピースがあるlineCount++; // ピースのカウント}if (lineCount == 0) /* これより上にピースはない*/break; // ピースが一つもないif (lineCount != FIELD_WIDTH) {y--; // ピースがあるので上の行へcontinue;}/*1行削除して、削除した上の行を一行下へずらす*/delCount++;for (iy = y; iy >= 0; iy--) {for (x = 0; x < FIELD_WIDTH; x++) {if (iy > 0)field[x][iy] = field[x][iy - 1];elsefield[x][0] = 0; // 0行目は0で埋める}}}return delCount; //削除した行数を返す。}/* 次のブロックへ*/VOID NextPiece() {int num;ZeroMemory(currentPiece, sizeof(int) * 25);num = (int)(((double)rand() / (RAND_MAX + 1)) * 7);currentPieceNumber = num; //確認用currentPiece[2][2] = 1;currentPiece[2][3] = 1;switch (num) {case 0:currentPiece[2][1] = 1;currentPiece[2][4] = 1;break;case 1:currentPiece[2][1] = 1;currentPiece[3][3] = 1;break;case 2:currentPiece[2][1] = 1;currentPiece[1][3] = 1;break;case 3:currentPiece[3][2] = 1;currentPiece[1][3] = 1;break;case 4:currentPiece[1][2] = 1;currentPiece[3][3] = 1;break;case 5:currentPiece[1][2] = 1;currentPiece[1][3] = 1;break;case 6:currentPiece[1][3] = 1;currentPiece[3][3] = 1;break;}// ブロックが現れる初期値を設定pieceLocation.x = 5; // ブロックの左の位置pieceLocation.y = -2; // ブロックの上の位置}VOID CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) {if (!MovePiece(PIECE_DOWN)) {int line, top;PieceToField();line = AdjustLine();top = GetPieceTop();if (field[7][1] || (pieceLocation.y + line + top - 1) < 0) {/*ゲーム終了*/KillTimer(hWnd, idEvent);MessageBox(hWnd, TEXT("GAME OVER"), TEXT("GAME OVER"), MB_OK);}NextPiece();}InvalidateRect(hMainWindow, NULL, FALSE);}VOID Paint(HDC hdc) {LOGFONT logFont;HFONT hFont;int x, y, pieceFalseLength, pieceTrueLength;pieceFalseLength = lstrlen(PIECE_FALSE);pieceTrueLength = lstrlen(PIECE_TRUE);hFont = GetStockObject(SYSTEM_FONT);GetObject(hFont, sizeof(LOGFONT), &logFont);SelectObject(hdc, hFont);for (y = 0; y < FIELD_HEIGHT; y++) { // 行でループするint ptx, pty; // 文字の描画位置(座標)ptx = 1;pty = 1 + y * logFont.lfHeight;for (x = 0; x < FIELD_WIDTH; x++) { // 列でループするif (field[x][y]) { // fieldの要素がTRUEのときSetTextColor(hdc, RGB(0, 0xFF, 0));TextOut(hdc, ptx, pty, PIECE_TRUE, pieceTrueLength);}else {// fieldの要素がTRUEではないときint px, py;px = x - pieceLocation.x; // ブロック内のピース位置に変換x方向py = y - pieceLocation.y; // ブロック内のピース位置に変換y方向SetTextColor(hdc, RGB(0xFF, 0, 0));if (px >= 0 && px < 5 && py >= 0 && py < 5 && currentPiece[px][py])// ピースを描画するTextOut(hdc, ptx, pty, PIECE_TRUE, pieceTrueLength);else// 単なる□を描画するTextOut(hdc, ptx, pty, PIECE_FALSE, pieceFalseLength);SetTextColor(hdc, RGB(0, 0, 0));}ptx += logFont.lfHeight; // 文字の高さ分を幅として次の表示する位置にする。}}}LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){HDC hdc;PAINTSTRUCT ps;switch (uMsg){case WM_DESTROY:PostQuitMessage(0);return 0;case WM_CREATE:ShowWindow(hWnd, SW_SHOW);NextPiece();SetTimer(hWnd, 1, 1000, TimerProc);return 0;case WM_KEYDOWN: {int move;if (wParam == VK_LEFT) {move = PIECE_LEFT;MovePiece(move);}else if (wParam == VK_RIGHT) {move = PIECE_RIGHT;MovePiece(move);}else if (wParam == VK_DOWN) {move = PIECE_DOWN;MovePiece(move);}else if (wParam == VK_SPACE) {TurnPiece();}InvalidateRect(hWnd, NULL, FALSE);break;}case WM_PAINT:hdc = BeginPaint(hWnd, &ps);Paint(hdc);EndPaint(hWnd, &ps);return 0;}return DefWindowProc(hWnd, uMsg, wParam, lParam);}int APIENTRY wWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPWSTR ipCmdLine,_In_ int nCmdShow) {WNDCLASSEXW wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc = WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = hInstance;wcex.hIcon = NULL;wcex.hCursor = NULL;wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);wcex.lpszMenuName = NULL;wcex.lpszClassName = TEXT("TestWindow");wcex.hIconSm = NULL;RegisterClassExW(&wcex);hMainWindow = CreateWindowEx(0UL, TEXT("TestWindow"), TEXT("日本"), WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, 640, 480, NULL, NULL, hInstance, NULL);if (hMainWindow == NULL){return FALSE;}MSG msg;while (GetMessage(&msg, NULL, 0, 0) > 0) {DispatchMessage(&msg);}return (int)msg.wParam;}
※参考:windowsゲームプログラミン
〇実行結果









