テトリスゲームに必要な処理は、

次の通りです。

1.ピースを用意する処理
2.ピースを移動する処理
3.ピースを回転する処理
4.ピースを固定する処理
5.行を消す処理
6.描画する処理

 

〇ピースの種類

5x5の配列を使って、TRUE(赤)、FALSEで表現しています。

      

      

 

配列の内容

ピースは(2,2)を軸に時計方向に回転します。

 

〇ゲーム画面

 

〇プログラム例#include <windows.h>

  1.  
  2. /* フィールドの横と縦のマス数*/
  3. #define FIELD_WIDTH 14
  4. #define FIELD_HEIGHT 24
  5.  
  6. /* MovePieceの引数*/
  7. #define PIECE_LEFT 2
  8. #define PIECE_RIGHT 4
  9. #define PIECE_DOWN 8
  10.  
  11. /* フィールドのマスのテキスト表現*/
  12. #define PIECE_FALSE TEXT("□")
  13. #define PIECE_TRUE TEXT("■")
  14.  
  15. HWND hMainWindow = NULL;
  16.  
  17. int field[FIELD_WIDTH][FIELD_HEIGHT] = { 0 }; /* ゲームフィールド*/
  18. int currentPiece[5][5] = { 0 }; /* 現在移動中のブロック*/
  19. POINT pieceLocation = { 0,0 }; /* ブロックの位置*/
  20.  
  21. int currentPieceNumber; /*現在のピース番号*/
  22.  
  23. BOOL MovePiece(int); /* ブロックの移動*/
  24. BOOL TurnPiece(VOID); /* ブロックの回転*/
  25. VOID NextPiece(VOID); /*次のブロックを出現*/
  26. VOID PieceToField(VOID); /*ブロックの位置と内容をfield配列変数にコピーする*/
  27. int AdjustLine(VOID); /* 横一列がピースで埋まっている場合の処理を実行する*/
  28. int GetPieceTop(VOID); /*ブロック内のピースの最上部の位置を返す*/
  29. int GetPieceBottom(VOID);/*ブロック内のピースの最下部の位置を返す*/
  30. int GetPieceLeft(VOID);/*ブロック内のピースの最左側の位置を返す*/
  31. int GetPieceRight(VOID);/*ブロック内のピースの最右側の位置を返す*/
  32.  
  33. /* ブロック内のピースの最上部の位置を返す*/
  34. int GetPieceTop() {
  35.     int x, y, length = 3; // 中央の位置を3とする
  36.     for (y = 1; y >= 0; y--) { // 上に向かって調べる
  37.         for (x = 0; x < 5; x++) {
  38.             if (currentPiece[x][y]) {
  39.                 length--;
  40.                 break; //ピースが見つかったらx方向ループから出る。
  41.             }
  42.         }
  43.     }
  44.     return length;// 最小1
  45. }
  46.  
  47. /* ブロック内のピースの最下部の位置を返す*/
  48. int GetPieceBottom() {
  49.     int x, y, length = 3; // 中央の位置を3とする
  50.     for (y = 3; y < 5; y++) { // 下へ向かって調べる
  51.         for (x = 0; x < 5; x++) {
  52.             if (currentPiece[x][y]) {
  53.                 length++;
  54.                 break; //ピースが見つかったらy方向ループから出る。
  55.             }
  56.         }
  57.     }
  58.     return length; //最大5
  59. }
  60.  
  61. /* ブロック内のピースの一番左側の位置を 返す */
  62. int GetPieceLeft() {
  63.     int x, y, length = 3; // 中央の位置を3とする
  64.     for (x = 1; x >= 0; x--) { //左へ向かって調べる
  65.         for (y = 0; y < 5; y++) {
  66.             if (currentPiece[x][y]) {
  67.                 length--;
  68.                 break; //ピースが見つかったらy方向ループから出る。
  69.             }
  70.         }
  71.     }
  72.     return length; //最小1
  73. }
  74.  
  75. /* ブロック内のピースの一番右側の位置を返す*/
  76. int GetPieceRight() {
  77.     int x, y, length = 3; // 中央の位置を3とする
  78.     for (x = 3; x < 5; x++) { //右へ向かって調べる
  79.         for (y = 0; y < 5; y++) {
  80.             if (currentPiece[x][y]) {
  81.                 length++;
  82.                 break; //ピースが見つかったらy方向ループから出る。
  83.             }
  84.         }
  85.     }
  86.     return length; //最大5
  87. }
  88.  
  89. /* ピースを位置情報に従ってフィールドにコピーする */
  90. VOID PieceToField(VOID) {
  91.     int x, y;
  92.     for (y = 0; y < 5; y++) {
  93.         for (x = 0; x < 5; x++) {
  94.             if (currentPiece[x][y])
  95.                 field[pieceLocation.x + x][pieceLocation.y + y] = currentPiece[x][y];
  96.  
  97.         }
  98.     }
  99. }
  100.  
  101. /* ピースの移動判定*/
  102. BOOL MovePiece(int move) {
  103.     if (move == PIECE_LEFT) { // 左に対して
  104.         int left = GetPieceLeft();
  105.  
  106.  
  107.         if (pieceLocation.x > -(left - 1)) { // ブロック内のピースの左の位置が左壁のどうなのか
  108.             int x, y, count = 0;
  109.             for (y = 0; y < 5; y++) {
  110.                 for (x = 0; x < 5; x++) {
  111.                     // 各ピースを確認
  112.                     if (currentPiece[x][y] && field[pieceLocation.x + x - 1][pieceLocation.y + y]) {
  113.                         return FALSE; // 左に何かピースがある。
  114.                         
  115.                     }
  116.                 }
  117.             }
  118.             pieceLocation.x--; // ブロックを左に移動
  119.         }
  120.         else {
  121.             return FALSE; // 左枠に当たった。
  122.         }
  123.     }
  124.     else if (move == PIECE_RIGHT) { // 右側にたいして
  125.         int right = GetPieceRight();
  126.         if (pieceLocation.x < (FIELD_WIDTH - right)) { // 右側の枠より右かどうか
  127.  
  128.             int x, y, count = 0;
  129.             for (y = 0; y < 5; y++) {
  130.                 for (x = 0; x < 5; x++) {
  131.                     if (currentPiece[x][y] && field[pieceLocation.x + x + 1][pieceLocation.y + y]) {
  132.                         return FALSE; // 右に何かピースがある。
  133.                     }
  134.                 }
  135.             }
  136.             pieceLocation.x++; // ブロックを右に移動
  137.         }
  138.         else {
  139.             return FALSE; // 左側の枠に当たった。
  140.         }
  141.     }
  142.     else if (move == PIECE_DOWN) { // 下へ移動できるかどうか
  143.         int bottom = GetPieceBottom();
  144.  
  145.         // 枠内かどうか確認する
  146.         if (pieceLocation.y < (FIELD_HEIGHT - bottom)) {
  147.             int x, y, count = 0;
  148.             for (y = 0; y < 5; y++) {
  149.                 for (x = 0; x < 5; x++) {
  150.                     // 現在のブロック内の各ピースを確認
  151.                     if (currentPiece[x][y] && field[pieceLocation.x + x][pieceLocation.y + y + 1]) {
  152.                         return FALSE; // 下にブロックがある。
  153.                     }
  154.                 }
  155.             }
  156.             pieceLocation.y++; // ブロックの位置を下げる。
  157.  
  158.         }
  159.         else {
  160.             return FALSE; // 下の枠に当たった。
  161.  
  162.         }
  163.     }
  164.  
  165.     return TRUE;
  166. }
  167.  
  168. /* ブロックを回転させる*/
  169. BOOL TurnPiece() {
  170.     int x, y;
  171.     int copy[5][5];
  172.  
  173.     /*回転したブロックを生成する*/
  174.     for (y = 0; y < 5; y++) { // 右回りに回転させる。
  175.         for (x = 0; x < 5; x++) {
  176.             copy[4 - y][x] = currentPiece[x][y];
  177.         }
  178.     }
  179.  
  180.     /* 回転可能かどうか調べる*/
  181.     for (y = 0; y < 5; y++) {
  182.         for (x = 0; x < 5; x++) {
  183.             if (copy[x][y]) { // ピースがある場合
  184.                 int offsetX = pieceLocation.x + x;
  185.                 int offsetY = pieceLocation.y + y;
  186.                 if ((offsetX < 0) || // 左枠
  187.                     (offsetX >= FIELD_WIDTH) || // 右枠
  188.                     (offsetY >= FIELD_HEIGHT) || // 下枠
  189.                     field[offsetX][offsetY]) // ピースが重なる場合
  190.                     return FALSE; // 回転できない
  191.             }
  192.         }
  193.     }
  194.  
  195.     memcpy(currentPiece, copy, sizeof(int) * 25); //回転したブロックを現在のブロックにする
  196.     return TRUE; // 回転できた
  197. }
  198.  
  199. /*各行を調べ、行が埋まっている場合は行を削除する*/
  200. int AdjustLine() {
  201.     int x, y, delCount = 0;
  202.     for (y = FIELD_HEIGHT - 1; y >= 0;) { // 行でループ(下から)
  203.         int iy, lineCount = 0;
  204.         for (x = 0; x < FIELD_WIDTH; x++) { // 列でループ
  205.             if (field[x][y]) // ピースがある
  206.                 lineCount++; // ピースのカウント
  207.         }
  208.         if (lineCount == 0) /* これより上にピースはない*/
  209.             break; // ピースが一つもない
  210.         if (lineCount != FIELD_WIDTH) {
  211.             y--; // ピースがあるので上の行へ
  212.             continue;
  213.         }
  214.  
  215.         /*1行削除して、削除した上の行を一行下へずらす*/
  216.         delCount++;
  217.         for (iy = y; iy >= 0; iy--) {
  218.             for (x = 0; x < FIELD_WIDTH; x++) {
  219.                 if (iy > 0)
  220.                     field[x][iy] = field[x][iy - 1];
  221.                 else 
  222.                     field[x][0] = 0; // 0行目は0で埋める
  223.             }
  224.         }
  225.     }
  226.     return delCount; //削除した行数を返す。
  227. }
  228.  
  229. /* 次のブロックへ*/
  230. VOID NextPiece() {
  231.     int num;
  232.     ZeroMemory(currentPiece, sizeof(int) * 25);
  233.  
  234.     num = (int)(((double)rand() / (RAND_MAX + 1)) * 7);
  235.  
  236.     currentPieceNumber = num; //確認用
  237.  
  238.     currentPiece[2][2] = 1;
  239.     currentPiece[2][3] = 1;
  240.     switch (num) {
  241.     case 0:
  242.         currentPiece[2][1] = 1;
  243.         currentPiece[2][4] = 1;
  244.         break;
  245.     case 1:
  246.         currentPiece[2][1] = 1;
  247.         currentPiece[3][3] = 1;
  248.         break;
  249.     case 2:
  250.         currentPiece[2][1] = 1;
  251.         currentPiece[1][3] = 1;
  252.         break;
  253.     case 3:
  254.         currentPiece[3][2] = 1;
  255.         currentPiece[1][3] = 1;
  256.         break;
  257.     case 4:
  258.         currentPiece[1][2] = 1;
  259.         currentPiece[3][3] = 1;
  260.         break;
  261.     case 5:
  262.         currentPiece[1][2] = 1;
  263.         currentPiece[1][3] = 1;
  264.         break;
  265.     case 6:
  266.         currentPiece[1][3] = 1;
  267.         currentPiece[3][3] = 1;
  268.         break;
  269.     }
  270.  
  271.     // ブロックが現れる初期値を設定
  272.     pieceLocation.x = 5; // ブロックの左の位置
  273.     pieceLocation.y = -2; // ブロックの上の位置
  274. }
  275.  
  276. VOID CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) {
  277.  
  278.     if (!MovePiece(PIECE_DOWN)) {
  279.         int line, top;
  280.  
  281.         PieceToField();
  282.         line = AdjustLine();
  283.         top = GetPieceTop();
  284.         if (field[7][1] || (pieceLocation.y + line + top - 1) < 0) {
  285.             /*ゲーム終了*/
  286.             KillTimer(hWnd, idEvent);
  287.             MessageBox(hWnd, TEXT("GAME OVER"), TEXT("GAME OVER"), MB_OK);
  288.         }
  289.         NextPiece();
  290.     }
  291.     InvalidateRect(hMainWindow, NULL, FALSE);
  292. }
  293.  
  294. VOID Paint(HDC hdc) {
  295.     LOGFONT logFont;
  296.     HFONT hFont;
  297.  
  298.     int x, y, pieceFalseLength, pieceTrueLength;
  299.  
  300.     pieceFalseLength = lstrlen(PIECE_FALSE);
  301.     pieceTrueLength = lstrlen(PIECE_TRUE);
  302.  
  303.     hFont = GetStockObject(SYSTEM_FONT);
  304.     GetObject(hFont, sizeof(LOGFONT), &logFont);
  305.     SelectObject(hdc, hFont);
  306.  
  307.     for (y = 0; y < FIELD_HEIGHT; y++) { // 行でループする
  308.         int ptx, pty; // 文字の描画位置(座標)
  309.         ptx = 1;
  310.         pty = 1 + y * logFont.lfHeight;
  311.         for (x = 0; x < FIELD_WIDTH; x++) { // 列でループする
  312.             if (field[x][y]) { // fieldの要素がTRUEのとき
  313.                 SetTextColor(hdc, RGB(0, 0xFF, 0));
  314.                 TextOut(hdc, ptx, pty, PIECE_TRUE, pieceTrueLength);
  315.             }
  316.             else {
  317.                 // fieldの要素がTRUEではないとき
  318.                 int px, py;
  319.                 px = x - pieceLocation.x; // ブロック内のピース位置に変換x方向
  320.                 py = y - pieceLocation.y; // ブロック内のピース位置に変換y方向
  321.                 SetTextColor(hdc, RGB(0xFF, 0, 0));
  322.                 if (px >= 0 && px < 5 && py >= 0 && py < 5 && currentPiece[px][py])
  323.                     // ピースを描画する
  324.                     TextOut(hdc, ptx, pty, PIECE_TRUE, pieceTrueLength);
  325.                 else
  326.                     // 単なる□を描画する
  327.                     TextOut(hdc, ptx, pty, PIECE_FALSE, pieceFalseLength);
  328.                 SetTextColor(hdc, RGB(0, 0, 0));
  329.             }
  330.             ptx += logFont.lfHeight; // 文字の高さ分を幅として次の表示する位置にする。
  331.         }
  332.     }
  333. }
  334.  
  335.  
  336. LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  337. {
  338.  
  339.     HDC hdc;
  340.     PAINTSTRUCT ps;
  341.  
  342.     switch (uMsg)
  343.     {
  344.     case WM_DESTROY:
  345.         PostQuitMessage(0);
  346.         return 0;
  347.  
  348.     case WM_CREATE:
  349.         ShowWindow(hWnd, SW_SHOW);
  350.         NextPiece();
  351.         SetTimer(hWnd, 1, 1000, TimerProc);
  352.  
  353.         return 0;
  354.  
  355.     case WM_KEYDOWN: {
  356.         int move;
  357.         if (wParam == VK_LEFT) {
  358.             move = PIECE_LEFT;
  359.             MovePiece(move);
  360.         }
  361.         else if (wParam == VK_RIGHT) {
  362.             move = PIECE_RIGHT;
  363.             MovePiece(move);
  364.         }
  365.         else if (wParam == VK_DOWN) {
  366.             move = PIECE_DOWN;
  367.             MovePiece(move);
  368.         }
  369.         else if (wParam == VK_SPACE) {
  370.             TurnPiece();
  371.         }
  372.         InvalidateRect(hWnd, NULL, FALSE);
  373.         break;
  374.     }
  375.     case WM_PAINT:
  376.         hdc = BeginPaint(hWnd, &ps);
  377.         Paint(hdc);
  378.         EndPaint(hWnd, &ps);
  379.         return 0;
  380.     }
  381.     return DefWindowProc(hWnd, uMsg, wParam, lParam);
  382.  
  383. }
  384.  
  385. int APIENTRY wWinMain(
  386.     _In_ HINSTANCE hInstance,
  387.     _In_opt_ HINSTANCE hPrevInstance,
  388.     _In_ LPWSTR ipCmdLine,
  389.     _In_ int nCmdShow
  390. ) {
  391.  
  392.     WNDCLASSEXW wcex;
  393.  
  394.     wcex.cbSize = sizeof(WNDCLASSEX);
  395.  
  396.     wcex.style = CS_HREDRAW | CS_VREDRAW;
  397.     wcex.lpfnWndProc = WndProc;
  398.     wcex.cbClsExtra = 0;
  399.     wcex.cbWndExtra = 0;
  400.     wcex.hInstance = hInstance;
  401.     wcex.hIcon = NULL;
  402.     wcex.hCursor = NULL;
  403.     wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  404.     wcex.lpszMenuName = NULL;
  405.     wcex.lpszClassName = TEXT("TestWindow");
  406.     wcex.hIconSm = NULL;
  407.  
  408.     RegisterClassExW(&wcex);
  409.  
  410.     hMainWindow = CreateWindowEx(0UL, TEXT("TestWindow"), TEXT("日本"), WS_OVERLAPPEDWINDOW,
  411.         CW_USEDEFAULT, 0, 640, 480, NULL, NULL, hInstance, NULL);
  412.  
  413.     if (hMainWindow == NULL)
  414.     {
  415.         return FALSE;
  416.     }
  417.  
  418.     MSG msg;
  419.  
  420.     while (GetMessage(&msg, NULL, 0, 0) > 0) {
  421.         DispatchMessage(&msg);
  422.     }
  423.  
  424.     return (int)msg.wParam;
  425. }

※参考:windowsゲームプログラミン

〇実行結果