catalogue
2, The complete code can be copied and run directly
3, Required development environment
4, Specific project realization
① Game welcome interface (welcome)
② Game background initGameScreen()
③ The square represents int block [] [] []
④ The new box represents nextBlock()
⑥ Build user operation framework move (I)
⑦ Determine whether the box can move in the specified direction
⑨ Clear the box during descent (clearblock)
⑩ Judging box rotation rotatable()
① ① draw the block during descent (drawblock)
① ④ improvement of user operation framework Ⅱ mov()
① ⑤ eliminate block check() + down()
① ⑥ update score and grade addscore() + updategrade()
1, Game effect display
The orientation of each square is represented by a two-dimensional array
2, The complete code can be copied and run directly
#include<graphics.h> #include<stdio.h> #include<time.h> #include<conio.h> //kbhit() int score = 0; //Total score int rank = 0; //Grade #define BLOCK_COUNT 5 #define BLOCK_WIDTH 5 #define BLOCK_HEIGHT 5 #define UNIT_SIZE 20 // Small square width #define START_X 130 // Square landing box, square landing starting position #define START_Y 30 #define KEY_UP 87 // User operation #define KEY_LEFT 65 #define KEY_RIGHT 68 #define KEY_DOWN 83 #define KEY_SPACE 32 #define MinX 30 // Top left corner of the game #define MinY 30 int speed = 500; //Block landing speed int NextIndex = -1; //Next box int BlockIndex = -1; //Current box typedef enum { //Square orientation BLOCK_UP, BLOCK_RIGHT, BLOCK_LEFT, BLOCK_DOWN }block_dir_t; typedef enum { //Square movement direction MOVE_DOWN, MOVE_LEFT, MOVE_RIGHT }move_dir_t; //Square color int color[BLOCK_COUNT] = { GREEN, CYAN, MAGENTA, YELLOW, BROWN }; int visit[30][15]; //Access array visit[i][j] = 1 indicates that there are squares in this position int markColor[30][15]; //Corresponding position color int block[BLOCK_COUNT * 4][BLOCK_WIDTH][BLOCK_HEIGHT] = { // |Shaped square { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, // L-shaped square { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, // Field shaped square { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, // T-shaped square { 0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, // Z-shaped square { 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, }; /*************************** * Function: Welcome page * Input: * nothing * return: * nothing **************************/ void welcome() { //1. Initialize canvas initgraph(550, 660); system("pause"); //2. Set window title HWND window = GetHWnd();//Get window, get current window SetWindowText(window, _T("Tetris Xiaoming is coming")); //Set title //3. Set the game initial page setfont(40, 0, _T("Microsoft YaHei ")); //Set the font style of text (height, width (0 means adaptive), font) setcolor(WHITE); // Set color outtextxy(205, 200, _T("Russian method")); setfont(20, 0, _T("Regular script")); setcolor(WHITE); // Set color outtextxy(175, 300, _T("Programming, starting with Tetris")); Sleep(3000); } /*************************** * Function: initialize game scene * Input: * nothing * return: * nothing **************************/ void initGameSceen() { char str[16]; //Storage score //1. Clear the screen cleardevice(); //2. Painting scenes rectangle(27, 27, 336, 635); //Box landing frame outer frame rectangle(29, 29, 334, 633); //Box landing box rectangle(370, 50, 515, 195); //Box prompt setfont(24, 0, _T("Regular script")); //Write "next" setcolor(LIGHTGRAY); //grey outtextxy(405, 215, _T("next:")); setcolor(RED); //Write score outtextxy(405, 280, _T("fraction:")); //Write the score to str in the specified format sprintf_s(str, 16, "%d", score); //Here, set the character set to multi character to ensure that outextxy can write out the variable str outtextxy(415, 310, str); outtextxy(405, 375, _T("Grade:")); //Grade //Writes rank to str in the specified format sprintf_s(str, 16, "%d", rank); //Here, set the character set to multi character to ensure that outextxy can write out the variable str outtextxy(415, 405, str); setcolor(LIGHTBLUE); //Operating instructions outtextxy(390, 475, "Operating instructions:"); outtextxy(390, 500, "↑: rotate"); outtextxy(390, 525, "↓: decline"); outtextxy(390, 550, "←: Shift left"); outtextxy(390, 575, "→: Shift right"); outtextxy(390, 600, "Space: suspend"); } /***************************************** * Function: clear the box in the box prompt box * Input: * nothing * return: * nothing ****************************************/ void clearBlock() { setcolor(BLACK); setfont(23, 0, "Regular script"); for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { int x = 391 + j * UNIT_SIZE; int y = 71 + i * UNIT_SIZE; outtextxy(x, y, "■"); } } } /***************************************** * Function: clear blocks during landing * Input: * x,y - Coordinates of the square (position of the upper left corner of the two-dimensional array) * block_dir_t - Square direction * return: * nothing ****************************************/ void clearBlock(int x, int y, block_dir_t blockDir) { setcolor(BLACK); // setfont(23, 0, "italics"); int id = BlockIndex * 4 + blockDir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { int x2 = x + j * UNIT_SIZE; int y2 = y + i * UNIT_SIZE; outtextxy(x2, y2, "■"); } } } } /***************************************** * Function: draw a square at the beginning of the prompt box and landing box * Input: * x,y - Coordinates of the square (position of the upper left corner of the two-dimensional array) * return: * nothing ****************************************/ void drawBlock(int x, int y) { setcolor(color[NextIndex]); setfont(23, 0, "Regular script"); for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[NextIndex * 4][i][j] == 1) { int x2 = x + j * UNIT_SIZE; int y2 = y + i * UNIT_SIZE; outtextxy(x2, y2, "■"); } } } } /***************************************** *Function: draw the square in the descending process *Input: * x,y - Coordinates of the square (position of the upper left corner of the two-dimensional array) * block_dir_t - Square direction * return: * nothing ****************************************/ void drawBlock(int x, int y, block_dir_t dir) { setcolor(color[BlockIndex]); setfont(23, 0, "Regular script"); int id = BlockIndex * 4 + dir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { //Erase the i-th row and j-th column of the box outtextxy(x + j * UNIT_SIZE, y + i * UNIT_SIZE, "■"); } } } } /***************************************** *Function: generate a new box in the box prompt box *Input: * nothing *return: * nothing ****************************************/ void nextblock() { clearBlock(); //Generate random numbers and randomly select blocks srand((unsigned)time(NULL)); //Use the return value of the time function as a random seed NextIndex = rand() % BLOCK_COUNT; //Generate random numbers from 0 to 5 drawBlock(391, 71); } /***************************************** *Function: judge whether it can move to the specified direction at the specified position *Input: * x,y - Square position * moveDir - The direction you want to move next * blockDir - The direction of the current square * return: * true - Can move * false - Cannot move ****************************************/ bool moveable(int x0, int y0, move_dir_t moveDir, block_dir_t blockDir) { //The upper left corner of the calculation box is 30 × 15 game area location (rows and columns) int x = (y0 - MinY) / UNIT_SIZE; int y = (x0 - MinX) / UNIT_SIZE; int ret = 1; int id = BlockIndex * 4 + blockDir; if (moveDir == MOVE_DOWN) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //Conditions for not moving down: the solid block has reached the bottom (x+i+1==30), or there is a block at the bottom if (block[id][i][j] == 1 && (x + i + 1 == 30 || visit[x + i + 1][y + j] == 1)) { ret = 0; } } } } else if (moveDir == MOVE_LEFT) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //Conditions for not moving to the left: the solid box has reached the left boundary (y+j==0), or there is a box on the left if (block[id][i][j] == 1 && (y + j <= 0 || visit[x + i][y + j - 1] == 1)) { ret = 0; } } } } else if (moveDir == MOVE_RIGHT) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //Conditions under which the downward movement is not possible: the solid block has reached the right boundary (y + j + 1 > = 15), or there is a block on the right if (block[id][i][j] == 1 && (y + j + 1 >= 15 || visit[x + i][y + j + 1] == 1)) { ret = 0; } } } } return ret; } /***************************** *Function: check whether the game is over *Input: * nothing * return: * nothing *****************************/ void failCheck() { //The end condition of the game is that the newly drawn box on the top will be "solidified", and the newly drawn box on the top will face up and move down if (!moveable(START_X, START_Y, MOVE_DOWN, (block_dir_t)BLOCK_UP)) { setcolor(WHITE); setfont(45, 0, "Official style"); outtextxy(75, 300, "Game Over!"); Sleep(1000); system("pause"); closegraph(); exit(0); } } /************************** * Function: delay waiting * Input: * * return: * nothing *************************/ void wait(int interval) { int count = interval / 10; for (int i = 0; i < count; ++i) { Sleep(10); //If the user presses a key during hibernation, the hibernation ends if (_kbhit()) { return; } } } /***************************************** * Function: judge whether the current box can rotate in the specified direction * Input: * x,y - Square position (2D array coordinates) * dir - Square rotation direction * return: * true - Can rotate * false - Cannot rotate ****************************************/ bool rotatable(int x, int y, block_dir_t dir) { //First, judge whether you can continue to move down if (!moveable(x, y, MOVE_DOWN, dir)) { return false; } int x2 = (y - MinY) / UNIT_SIZE; int y2 = (x - MinX) / UNIT_SIZE; int id = BlockIndex * 4 + dir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //No rotation condition: the left and right boundaries are out of bounds or there is a block "blocking" if (block[id][i][j] == 1 && (y2 + j < 0 || y2 + j >= 15 || visit[x2 + i][y2 + j] == 1)) { return false; } } } return true; } /***************************************** * Function: * Input: * * return: * nothing ****************************************/ void mark(int x, int y, block_dir_t dir) { int id = BlockIndex * 4 + dir; int x2 = (y - MinY) / UNIT_SIZE; int y2 = (x - MinX) / UNIT_SIZE; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { visit[x2 + i][y2 + j] = 1; markColor[x2 + i][y2 + j] = color[BlockIndex]; } } } } /***************************************** * Function: read the user's operation and update the landing box from time to time * Input: * nothing * return: * nothing ****************************************/ void move() { int x = START_X; //Box start position int y = START_Y; int k = 0; block_dir_t blockDir = (block_dir_t)BLOCK_UP; int curSpeed = speed; //Define the current square landing speed //Judge whether the game is over before reading the user's operation failCheck(); //Continuous descent while (1) { int curSpeed = speed; //Define the current square landing speed //Clear box clearBlock(x, k + y, blockDir); //Judge the selected direction if (_kbhit()) { int key = _getch(); if (key == KEY_SPACE) { system("pause"); } else if (key == KEY_UP) { block_dir_t nextDir = (block_dir_t)((blockDir + 1) % 4); if (rotatable(x, y + k, nextDir)) { blockDir = nextDir; } } else if (key == KEY_LEFT) { if (moveable(x, y + k + 20, MOVE_LEFT, blockDir)) { x -= UNIT_SIZE; } } else if (key == KEY_RIGHT) { if (moveable(x, y + k + 20, MOVE_RIGHT, blockDir)) { x += UNIT_SIZE; } } else if (key == KEY_DOWN) { curSpeed = 50; } } k += 20; //Draw square drawBlock(x, y + k, blockDir); //dormancy wait(curSpeed); //The curing process of the block ends the cycle after the block is fixed, and the move of the current block is completed if (!moveable(x, y + k, MOVE_DOWN, blockDir)) { mark(x, y + k, blockDir); break; } } } /***************************************** *Function: draw the box just landed from the top, update the box in the prompt box, and call the box landing function move() *Input: * nothing * return: * nothing ****************************************/ void newblock() { BlockIndex = NextIndex; //Draw the square that just fell from the top drawBlock(START_X, START_Y); //Pause for the new box to appear Sleep(200); //Draw the next box in the upper right corner nextblock(); //Block landing move(); } /***************************************** * Function: eliminate the i-th line and move the upper line down * Input: * nothing * return: * nothing ****************************************/ void down(int x) { for (int i = x; i > 0; --i) { for (int j = 0; j < 15; ++j) { if (visit[i - 1][j] == 1) { visit[i][j] = 1; markColor[i][j] = markColor[i - 1][j]; setcolor(markColor[i][j]); outtextxy(20 * j + MinX, 20 * i + MinY, "■"); } else { visit[i][j] = 0; setcolor(BLACK); outtextxy(20 * j + MinX, 20 * i + MinY, "■"); } } } //Clear topmost grid setcolor(BLACK); for (int j = 0; j < 15; ++j) { visit[0][j] = 0; outtextxy(20 * j + MinX, MinY, "■"); } } /***************************************** * Function: update score * Input: * nothing * return: * nothing ****************************************/ void addScore(int lines) { char str[32]; score += lines * 10; sprintf_s(str, 32, "%d", score); setcolor(RED); outtextxy(415, 310, str); } /************************* * Function: update level * Input: * nothing * return: * nothing *************************/ void updateGrade() { //Update level //Assume 50 points rank = score / 50; char str[32]; sprintf_s(str, 32, "%d", rank); setcolor(RED); outtextxy(415, 405, str); //Update speed if (speed <= 100) { speed = 100; } else { speed = 500 - rank * 20; } } /************************* * Function: check whether there are full rows of squares * Input: * nothing * return: * nothing *************************/ void check() { int i, j; int clearLines = 0; for (i = 29; i >= 0; i--) { // Check if line i is full for (j = 0; j < 15 && visit[i][j]; j++); //When you do this, there are two situations: // 1. If line i is not full, it means there is a vacancy. At this time, J < 15 // 2. Line i is full, and j > = 15 if (j >= 15) { // At this time, line i is full, so you need to eliminate line i down(i); //Eliminate line i and move all the upper lines down i++; // Because there is i -- in the outermost loop, let's first i + +, so that we can check this line again in the next loop clearLines++; } } // Update score addScore(clearLines); // Update level (update level prompt, update speed) updateGrade(); } int main() { welcome(); initGameSceen(); //Generate new block nextblock(); // system("pause"); Sleep(800); //Initialize access array memset(visit, 0, sizeof(visit)); while (1) { newblock(); //Eliminate full lines and update scores and speeds check(); } system("pause"); closegraph(); return 0; }
3, Required development environment
1) Install VS2019, or other versions of VS
2) Install easyX graphics library
4, Specific project realization
① Game welcome interface (welcome)
There will be a game welcome page before the game starts. The whole page will last about three seconds, and then enter the game scene. In the game welcome page, in addition to displaying the game name, you should also modify the window title.
/*************************** * Function: Welcome page * Input: * nothing * return: * nothing **************************/ void welcome() { //1. Initialize canvas initgraph(550, 660); //2. Set window title HWND window = GetHWnd();//Get window, get current window SetWindowText(window, _T("Tetris Xiaoming is coming")); //Set title //3. Set the game initial page setfont(40, 0, _T("Microsoft YaHei ")); //Set the font style of text (height, width (0 means adaptive), font) setcolor(WHITE); // Set color outtextxy(205, 200, _T("Russian method")); setfont(20, 0, _T("Regular script")); setcolor(WHITE); // Set color outtextxy(175, 300, _T("Programming, starting with Tetris")); Sleep(3000); }
② Game background initGameScreen()
Draw game scenes
/*************************** * Function: initialize game scene * Input: * nothing * return: * nothing **************************/ void initGameSceen() { char str[16]; //Storage score //1. Clear the screen cleardevice(); //2. Painting scenes rectangle(27, 27, 336, 635); //Box landing frame outer frame rectangle(29, 29, 334, 633); //Box landing box rectangle(370, 50, 515, 195); //Box prompt setfont(24, 0, _T("Regular script")); //Write "next" setcolor(LIGHTGRAY); //grey outtextxy(405, 215, _T("next:")); setcolor(RED); //Write score outtextxy(405, 280, _T("fraction:")); //Write the score to str in the specified format sprintf_s(str, 16, "%d", score); //Here, set the character set to multi character to ensure that outextxy can write out the variable str outtextxy(415, 310, str); outtextxy(405, 375, _T("Grade:")); //Grade //Writes rank to str in the specified format sprintf_s(str, 16, "%d", rank); //Here, set the character set to multi character to ensure that outextxy can write out the variable str outtextxy(415, 405, str); setcolor(LIGHTBLUE); //Operating instructions outtextxy(390, 475, "Operating instructions:"); outtextxy(390, 500, "W: rotate"); outtextxy(390, 525, "S: decline"); outtextxy(390, 550, "A: Shift left"); outtextxy(390, 575, "D: Shift right"); outtextxy(390, 600, "Space: suspend"); system("pause"); }
③ The square represents int block [] [] []
The orientation of each square is represented by a two-dimensional array. This time, a total of five kinds of blocks are designed, each block has four orientations, and each rigid block and its orientation are stored in a three-dimensional array. 1 means that the point is a square.
#define BLOCK_COUNT 5 #define BLOCK_WIDTH 5 #define BLOCK_HEIGHT 5 int block[BLOCK_COUNT * 4][BLOCK_WIDTH][BLOCK_HEIGHT] = { // |Shaped square { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, // L-shaped square { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, // Field shaped square { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 }, // T-shaped square { 0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, // Z-shaped square { 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, };
④ The new box represents nextBlock()
In the box prompt box, each generation of a new box consists of two actions: first, erase the box, and then draw a new box.
/***************************************** * Function: clear the box in the box prompt box * Input: * nothing * return: * nothing ****************************************/ void clearBlock() { setcolor(BLACK); setfont(23, 0, "Regular script"); for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { int x = 391 + j * UNIT_SIZE; int y = 71 + i * UNIT_SIZE; outtextxy(x, y, "■"); } } } /***************************************** * Function: draw a square at the beginning of the prompt box and landing box * Input: * x,y - Coordinates of the square (position of the upper left corner of the two-dimensional array) * return: * nothing ****************************************/ void drawBlock(int x, int y) { setcolor(color[NextIndex]); setfont(23, 0, "Regular script"); for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[NextIndex * 4][i][j] == 1) { int x2 = x + j * UNIT_SIZE; int y2 = y + i * UNIT_SIZE; outtextxy(x2, y2, "■"); } } } } /***************************************** *Function: generate a new box in the box prompt box *Input: * nothing *return: * nothing ****************************************/ void nextblock() { clearBlock(); //Generate random numbers and randomly select blocks srand((unsigned)time(NULL)); //Use the return value of the time function as a random seed NextIndex = rand() % BLOCK_COUNT; //Generate random numbers from 0 to 5 drawBlock(391, 71); }
⑤ Design game loop (main)
In the game box, each time a new box is generated, it will enter the landing processing of the new box. After processing, it will cycle
int main() { welcome(); initGameSceen(); //Generate new block nextblock(); Sleep(800); //Initialize access array memset(visit, 0, sizeof(visit)); while (1) { newblock(); } system("pause"); closegraph(); return 0; } /***************************************** *Function: draw the box just landed from the top, update the box in the prompt box, and call the box landing function move() *Input: * nothing * return: * nothing ****************************************/ void newblock() { BlockIndex = NextIndex; //Draw the square that just fell from the top drawBlock(START_X, START_Y); //Pause for the new box to appear Sleep(200); //Draw the next box in the upper right corner nextblock(); //Block landing move(); }
⑥ Build user operation framework move (I)
User operation frame: break whether the game is over → erase the current box → user key operation → draw a new box → delay waiting → whether the box needs to be solidified (solidification indicates that the operation on the current box is over).
#define KEY_UP 87 // User operation #define KEY_LEFT 65 #define KEY_RIGHT 68 #define KEY_DOWN 83 #define KEY_SPACE 32 /***************************************** * Function: read the user's operation and update the landing box from time to time * Input: * nothing * return: * nothing ****************************************/ void move() { //Judge whether the game is over before reading the user's operation failCheck(); //Continuous descent while (1) { //Clear box //to do //Judge the selected direction if (_kbhit()) { int key = _getch(); if (key == KEY_SPACE) { //to do } else if (key == KEY_UP) { //to do } else if (key == KEY_LEFT) { //to do } else if (key == KEY_RIGHT) { //to do } else if (key == KEY_DOWN) { //to do } } //Draw square //to do //dormancy //to do //The curing process of the block ends the cycle after the block is fixed, and the move of the current block is completed //to do } }
⑦ Determine whether the box can move in the specified direction
When the new box just touches the "solidified" box when it is drawn from the top, the game is over, so we just need to judge whether the box can move down. Here, the function of judging whether the box can move in the specified direction is realized first.
/***************************************** *Function: judge whether it can move to the specified direction at the specified position *Input: * x,y - Square position * moveDir - The direction you want to move next * blockDir - The direction of the current square * return: * true - Can move * false - Cannot move ****************************************/ bool moveable(int x0, int y0, move_dir_t moveDir, block_dir_t blockDir) { //The upper left corner of the calculation box is 30 × 15 game area location (rows and columns) int x = (y0 - MinY) / UNIT_SIZE; int y = (x0 - MinX) / UNIT_SIZE; int ret = 1; int id = BlockIndex * 4 + blockDir; if (moveDir == MOVE_DOWN) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //Conditions for not moving down: the solid block has reached the bottom (x+i+1==30), or there is a block at the bottom if (block[id][i][j] == 1 && (x + i + 1 == 30 || visit[x + i + 1][y + j] == 1)) { ret = 0; } } } } else if (moveDir == MOVE_LEFT) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //Conditions for not moving to the left: the solid box has reached the left boundary (y+j==0), or there is a box on the left if (block[id][i][j] == 1 && (y + j <= 0 || visit[x + i][y + j - 1] == 1)) { ret = 0; } } } } else if (moveDir == MOVE_RIGHT) { for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //Conditions under which the downward movement is not possible: the solid block has reached the right boundary (y + j + 1 > = 15), or there is a block on the right if (block[id][i][j] == 1 && (y + j + 1 >= 15 || visit[x + i][y + j + 1] == 1)) { ret = 0; } } } } return ret; }
⑧ Game failure check ()
Game failure detection. When the newly drawn box cannot move down, it indicates that the game has failed.
/***************************** *Function: check whether the game is over *Input: * nothing * return: * nothing *****************************/ void failCheck() { //The end condition of the game is that the newly drawn box on the top will be "solidified", and the newly drawn box on the top will face up and move down if (!moveable(START_X, START_Y, MOVE_DOWN, (block_dir_t)BLOCK_UP)) { setcolor(WHITE); setfont(45, 0, "Official style"); outtextxy(75, 300, "Game Over!"); Sleep(1000); system("pause"); closegraph(); exit(0); } }
⑨ Clear the box during descent (clearblock)
If the game does not fail, it indicates that the user can continue the operation. Clear the box in the landing box before reading the user's operation.
/***************************************** * Function: clear blocks during landing * Input: * x,y - Coordinates of the square (position of the upper left corner of the two-dimensional array) * block_dir_t - Square direction * return: * nothing ****************************************/ void clearBlock(int x, int y, block_dir_t blockDir) { setcolor(BLACK); // setfont(23, 0, "italics"); int id = BlockIndex * 4 + blockDir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { int x2 = x + j * UNIT_SIZE; int y2 = y + i * UNIT_SIZE; outtextxy(x2, y2, "■"); } } } }
⑩ Judging box rotation rotatable()
If the block can move downward in the direction to be turned, it indicates that the block can rotate. Therefore, it only needs to make less use of the moveable function.
/***************************************** * Function: judge whether the current box can rotate in the specified direction * Input: * x,y - Square position (2D array coordinates) * dir - Square rotation direction * return: * true - Can rotate * false - Cannot rotate ****************************************/ bool rotatable(int x, int y, block_dir_t dir) { //First, judge whether you can continue to move down if (!moveable(x, y, MOVE_DOWN, dir)) { return false; } int x2 = (y - MinY) / UNIT_SIZE; int y2 = (x - MinX) / UNIT_SIZE; int id = BlockIndex * 4 + dir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { //No rotation condition: the left and right boundaries are out of bounds or there is a block "blocking" if (block[id][i][j] == 1 && (y2 + j < 0 || y2 + j >= 15 || visit[x2 + i][y2 + j] == 1)) { return false; } } } return true; }
① ① draw the block during descent (drawblock)
Draw a new square according to the user's operation each time
/***************************************** *Function: draw the square in the descending process *Input: * x,y - Coordinates of the square (position of the upper left corner of the two-dimensional array) * block_dir_t - Square direction * return: * nothing ****************************************/ void drawBlock(int x, int y, block_dir_t dir) { setcolor(color[BlockIndex]); setfont(23, 0, "Regular script"); int id = BlockIndex * 4 + dir; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { //Erase the i-th row and j-th column of the box outtextxy(x + j * UNIT_SIZE, y + i * UNIT_SIZE, "■"); } } } }
① ② delay wait ()
After each user operation, it will enter the delay waiting, and the waiting time will be determined according to the landing speed of the current box. During the delay waiting, if the user is detected to have a key operation, the waiting will end.
/************************** * Function: delay waiting * Input: * * return: * nothing *************************/ void wait(int interval) { int count = interval / 10; for (int i = 0; i < count; ++i) { Sleep(10); //If the user presses a key during hibernation, the hibernation ends if (_kbhit()) { return; } } }
① ③ fixed block mark()
Each time a new block is drawn, judge whether the block can continue to move. If it cannot move, it indicates that the block needs to be solidified.
/***************************************** * Function: block fixing * Input: * x,y - Block coordinates * dir - Square orientation * return: * nothing ****************************************/ void mark(int x, int y, block_dir_t dir) { int id = BlockIndex * 4 + dir; int x2 = (y - MinY) / UNIT_SIZE; int y2 = (x - MinX) / UNIT_SIZE; for (int i = 0; i < BLOCK_HEIGHT; ++i) { for (int j = 0; j < BLOCK_WIDTH; ++j) { if (block[id][i][j] == 1) { visit[x2 + i][y2 + j] = 1; markColor[x2 + i][y2 + j] = color[BlockIndex]; } } } }
① ④ improvement of user operation framework Ⅱ mov()
Add the above implementation functions to the operation framework
void move() { int x = START_X; //Box start position int y = START_Y; int k = 0; block_dir_t blockDir = (block_dir_t)BLOCK_UP; int curSpeed = speed; //Define the current square landing speed //Judge whether the game is over before reading the user's operation failCheck(); //Continuous descent while (1) { int curSpeed = speed; //Define the current square landing speed //Clear box clearBlock(x, k + y, blockDir); //Judge the selected direction if (_kbhit()) { int key = _getch(); if (key == KEY_SPACE) { system("pause"); } else if (key == KEY_UP) { block_dir_t nextDir = (block_dir_t)((blockDir + 1) % 4); if (rotatable(x, y + k, nextDir)) { blockDir = nextDir; } } else if (key == KEY_LEFT) { if (moveable(x, y + k + 20, MOVE_LEFT, blockDir)) { x -= UNIT_SIZE; } } else if (key == KEY_RIGHT) { if (moveable(x, y + k + 20, MOVE_RIGHT, blockDir)) { x += UNIT_SIZE; } } else if (key == KEY_DOWN) { curSpeed = 50; } } k += 20; //Draw square drawBlock(x, y + k, blockDir); //dormancy wait(curSpeed); //The curing process of the block ends the cycle after the block is fixed, and the move of the current block is completed if (!moveable(x, y + k, MOVE_DOWN, blockDir)) { mark(x, y + k, blockDir); break; } } }
① ⑤ eliminate block check() + down()
When the descending operation of a box is completed, find the "full line" box in the solidified box array. If there is a "full line" box, clear it, and then update the user score and level.
g
/************************* * Function: check whether there are full rows of squares * Input: * nothing * return: * nothing *************************/ void check() { int i, j; int clearLines = 0; for (i = 29; i >= 0; i--) { // Check if line i is full for (j = 0; j < 15 && visit[i][j]; j++); //When you do this, there are two situations: // 1. If line i is not full, it means there is a vacancy. At this time, J < 15 // 2. Line i is full, and j > = 15 if (j >= 15) { // At this time, line i is full, so you need to eliminate line i down(i); //Eliminate line i and move all the upper lines down i++; // Because there is i -- in the outermost loop, let's first i + +, so that we can check this line again in the next loop clearLines++; } } // Update score addScore(clearLines); // Update level (update level prompt, update speed) updateGrade(); } /***************************************** * Function: eliminate the i-th line and move the upper line down * Input: * nothing * return: * nothing ****************************************/ void down(int x) { for (int i = x; i > 0; --i) { for (int j = 0; j < 15; ++j) { if (visit[i - 1][j] == 1) { visit[i][j] = 1; markColor[i][j] = markColor[i - 1][j]; setcolor(markColor[i][j]); outtextxy(20 * j + MinX, 20 * i + MinY, "■"); } else { visit[i][j] = 0; setcolor(BLACK); outtextxy(20 * j + MinX, 20 * i + MinY, "■"); } } } //Clear topmost grid setcolor(BLACK); for (int j = 0; j < 15; ++j) { visit[0][j] = 0; outtextxy(20 * j + MinX, MinY, "■"); } }
① ⑥ update score and grade addscore() + updategrade()
Update the user score and level according to the number of clear box lines.
/***************************************** * Function: update score * Input: * nothing * return: * nothing ****************************************/ void addScore(int lines) { char str[32]; score += lines * 10; sprintf_s(str, 32, "%d", score); setcolor(RED); outtextxy(415, 310, str); } /************************* * Function: update level * Input: * nothing * return: * nothing *************************/ void updateGrade() { //Update level //Assume 50 points rank = score / 50; char str[32]; sprintf_s(str, 32, "%d", rank); setcolor(RED); outtextxy(415, 405, str); //Update speed if (speed <= 100) { speed = 100; } else { speed = 500 - rank * 20; } }
Code integration operation
5, Deficiencies
- Use easyX drawing and import game pictures to make the game effect more realistic
- Preservation of game achievements
- Operation control slightly stuck