C++入门——实现十字消除游戏

发布时间:2025-12-09 20:13:17 浏览次数:7

参考

  • 《C和C++游戏趣味编程》 童晶
  • 十字消除游戏

    用户点击空白方块,沿其上、下、左、右方向寻找一个彩色方块,如果有两个或两个以上颜色一致,就将其消除。在进度条时间结束前消除足够的方块,可以进入下一关

    红色方块的表示与绘制

    定义Block结构体,利用Block类型的二维数组存储画面中所有小方块的信息。在startup()中将所有方块设置为红色填充、白色线条,在show()中绘制出所有方块:

    #include <graphics.h>#include <conio.h>#include <stdio.h>#define BlockSize 40 // 小方块的边长#define RowNum 13 // 画面一共RowNum行#define ColNum 21 // 画面一共ColNum列struct Block{int x, y; // 在画面中的x, y坐标int i, j; // 在二维数组中的i, j下标};// 全局变量Block blocks[RowNum][ColNum];void startup(){int i, j;int width = BlockSize * ColNum;int height = BlockSize * RowNum;initgraph(width, height);setbkcolor(RGB(220, 220, 220));setfillcolor(RGB(255, 0, 0));setlinestyle(PS_SOLID, 2); // 设置线型、线宽cleardevice(); // 以背景颜色清屏BeginBatchDraw();for (i = 0; i < RowNum; i++){for (j = 0; j < ColNum; j++){blocks[i][j].x = j * BlockSize;blocks[i][j].y = i * BlockSize;blocks[i][j].i = i;blocks[i][j].j = j;}}}void show(){cleardevice();setlinecolor(RGB(255, 255, 255));int i, j;for (i = 0; i < RowNum; i++){for (j = 0; j < ColNum; j++){fillrectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);}}FlushBatchDraw();}int main(){startup();while (1){show();}return 0;}

    随机颜色方块的实现

    定义colors数组记录所有可能的颜色,其中第0种为灰白色,其他为彩色:

    COLORREF colors[ColorTypeNum + 1];

    在startup()中对colors数组进行初始化:

    colors[0] = RGB(220, 220, 220);for (i = 1; i < ColorTypeNum + 1; i++){colors[i] = HSVtoRGB((i - 1) * 40, 0.6, 0.8);}

    在结构体Block中添加成员变量colorId:

    struct Block{int x, y; // 在画面中的x, y坐标int i, j; // 在二维数组中的i, j下标int colorId; // 对应颜色下标};

    在startup()中对blocks初始化,设置其颜色序号为[0, ColorTypeNum]的随机数:

    int t = rand() % (ColorTypeNum + 1);blocks[i][j].colorId = t;

    为了让空白比例更高,可以调整一下:

    int t = rand() % (int(ColorTypeNum * 1.5));if (t < ColorTypeNum + 1){blocks[i][j].colorId = t;}else{blocks[i][j].colorId = 0;}

    在show()中绘制对应颜色的小方块:

    setfillcolor(colors[blocks[i][j].colorId);fillrectangle(blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);

    鼠标点击与十字消除

    添加updateWithInput()函数,根据鼠标点击位置(e.x, e.y)计算点中的小方块在二维数组中的行列号(clicked_i, clicked_j)

    void updateWithInput(){ExMessage e;if (peekmessage(&e)){if (e.message == WM_LBUTTONDOWN){int clicked_i = int(e.y) / BlockSize;int clicked_j = int(e.x) / BlockSize;}}}

    先判断被鼠标点击的方块是否为空白方块,如果不是则直接返回:

    if (blocks[clicked_i][clicked_j].colorId != 0){return;}

    定义数组fourBlocks[4]存储上下左右4个方向找到的第一个不是空白的方块:

    Block fourBlocks[4] = { blocks[clicked_i][clicked_j] }; // 初始化为点击的方块

    首先向上寻找,找到第一个不是空白的方块,存储到fourBlocks[4]中:

    int search;for (search = 0; clicked_i - search >= 0; search++) // 向上搜索{if (blocks[clicked_i - search][clicked_j].colorId != 0){fourBlocks[0] = blocks[clicked_i - search][clicked_j];break;}}

    同理,向下、左、右分别找到第一个不是空白的方块:

    for (search = 0; clicked_i + search < RowNum; search++) // 向下搜索{if (blocks[clicked_i + search][clicked_j].colorId != 0){fourBlocks[1] = blocks[clicked_i + search][clicked_j];break;}}for (search = 0; clicked_j - search >= 0; search++) // 向左搜索 {if (blocks[clicked_i][clicked_j - search].colorId != 0){fourBlocks[2] = blocks[clicked_i][clicked_j - search];break;}}for (search = 0; clicked_j + search < ColNum; search++){if (blocks[clicked_i][clicked_j + search].colorId != 0){fourBlocks[3] = blocks[clicked_i][clicked_j + search];break;}}

    进一步,遍历fourBlocks,统计对应颜色方块的个数。如果某种颜色的方块个数colorStatistics[i] >= 2,则将对应方块的颜色序号设为0:

    int colorStatistics[ColorTypeNum + 1] = { 0 };for (i = 1; i <= ColorTypeNum; i++){for (j = 0; j < 4; j++){if (fourBlocks[j].colorId == i){colorStatistics[i]++;}}if (colorStatistics[i] >= 2){for (j = 0; j < 4; j++){if (fourBlocks[j].colorId == i){blocks[fourBlocks[j].i][fourBlocks[j].j].colorId = 0;}}}}

    方块提示框的绘制

    首先定义绘制提示框的函数:

    void drawBlockHint(int i, int j, COLORREF color, int isfill){setlinecolor(color);setfillcolor(color);if (isfill == 1) // 画填充方块{fillrectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);}if (isfill == 0) // 画非填充的方块线框{rectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);}}

    在updateWithInput()中,如果点击的是空白方块,则执行:

    show(); // 先绘制其他方块drawBlockHint(clicked_i, clicked_j, RGB(100, 100, 100), 1); // 对被点击的空白方块,绘制填充灰色的空白方块

    如果十字区域有要消除的彩色方块,则执行:

    if (fourBlocks[j].colorId == i){drawBlockHint(fourBlocks[j].i, fourBlocks[j].j, RGB(0, 0, 0), 0); // 要消除的方块绘制提示框blocks[fourBlocks[j].i][fourBlocks[j].j].colorId = 0;}

    倒计时与进度条

    添加全局变量:

    float maxTime; // 游戏允许的总时长float remainTime; // 游戏剩余时长在startup()中,加大窗口高度用于显示倒计时进度条:int height = BlockSize * (RowNum + 2);

    在updateWithoutInput()中,定义静态变量start,每次运行时获得当前时刻now,计算程序已运行的时间duration,求出游戏剩余时间:

    void updateWithoutInput(){static clock_t start = clock(); // 记录第一次运行时刻clock_t now = clock(); // 获取当前时刻double duration = double(now - start) / CLOCKS_PER_SEC; // 程序运行时间remainTime = maxTime - duration;}

    在show()中绘制倒计时进度条:

    setlinecolor(RGB(255, 0, 0)); // 设置进度条颜色setfillcolor(RGB(255, 0, 0));fillrectangle(0, BlockSize * (RowNum + 0.2), remainTime * BlockSize * ColNum / maxTime, BlockSize * (RowNum + 0.8)); // 绘制进度条

    得分计算与胜负判断

    定义score记录玩家消去的方块个数,noZeroBlockNum记录游戏开始时彩色砖块的总数,设定当score >= 0.9 * noZeroBlockNum时游戏胜利:

    int score;int noZeroBlockNum; // 彩色方块个数

    在startup()中统计彩色砖块的总数:

    if (blocks[i][j].colorId != 0){noZeroBlockNum++;}

    在updateWithInput()中,更新十字区域消除的方块个数:

    score += colorStatistics[i];

    在show()中,显示当前得分score:

    TCHAR s[80]; // 定义字符数组setbkmode(TRANSPARENT);settextcolor(RGB(0, 0, 0));settextstyle(25, 0, _T("宋体"));swprintf_s(s, _T("当前%d分,达到%d分游戏胜利"), level, score, int(0.9 * noZeroBlockNum));outtextxy(BlockSize * (ColNum / 4.5), BlockSize * (RowNum + 1.1), s);

    多关卡与增加游戏难度

    在startup()中,随着level的增加,当前关的游戏总时长越来越短:

    maxTime = 200 - level * 10;

    在show()中,显示当前为第几关、已得分数、得到多少分可以进入下一关:

    swprintf_s(s, _T("当前第%d关,已得%d分,达到%d分进入下一关"), level, score, int(0.9 * noZeroBlockNum));

    在updateWithoutInput()中,如果得分达到要求,则将level加1,重新计时;否则重新开始:

    if (score >= int(0.9 * noZeroBlockNum)){level++; // 进入下一关start = clock(); // 重新开始计时startup();}else if (remainTime <= 0){start = clock();startup();}

    完整代码

    #include <graphics.h>#include <conio.h>#include <stdio.h>#include <time.h>#define BlockSize 40 // 小方块的边长#define RowNum 13 // 画面一共RowNum行#define ColNum 21 // 画面一共ColNum列#define ColorTypeNum 9 // 方块彩色颜色的个数struct Block{int x, y; // 在画面中的x, y坐标int i, j; // 在二维数组中的i, j下标int colorId; // 对应颜色下标};// 全局变量Block blocks[RowNum][ColNum];COLORREF colors[ColorTypeNum + 1];float maxTime; // 游戏允许的总时长float remainTime; // 游戏剩余时长float punishTime; // 点错扣除的时间int score;int noZeroBlockNum; // 彩色方块个数int level = 1; // 当前关卡序号void drawBlockHint(int i, int j, COLORREF color, int isfill){setlinecolor(color);setfillcolor(color);if (isfill == 1) // 画填充方块{fillrectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);}if (isfill == 0) // 画非填充的方块线框{rectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);}}void startup(){int i, j;int width = BlockSize * ColNum;int height = BlockSize * (RowNum + 2);initgraph(width, height);setbkcolor(RGB(220, 220, 220));setfillcolor(RGB(255, 0, 0));setlinestyle(PS_SOLID, 2); // 设置线型、线宽cleardevice(); // 以背景颜色清屏BeginBatchDraw();srand(time(0));maxTime = 300 - level * 10;remainTime = maxTime;punishTime = 0;colors[0] = RGB(220, 220, 220);for (i = 1; i < ColorTypeNum + 1; i++){colors[i] = HSVtoRGB((i - 1) * 40, 0.6, 0.8);}noZeroBlockNum = 0;for (i = 0; i < RowNum; i++){for (j = 0; j < ColNum; j++){int t = rand() % (int(ColorTypeNum * 1.5));if (t < ColorTypeNum + 1){blocks[i][j].colorId = t;}else{blocks[i][j].colorId = 0;}blocks[i][j].x = j * BlockSize;blocks[i][j].y = i * BlockSize;blocks[i][j].i = i;blocks[i][j].j = j;if (blocks[i][j].colorId != 0){noZeroBlockNum++;}}}score = 0;}void show(){cleardevice();setlinecolor(RGB(255, 255, 255));int i, j;for (i = 0; i < RowNum; i++){for (j = 0; j < ColNum; j++){setfillcolor(colors[blocks[i][j].colorId]);fillrectangle(blocks[i][j].x, blocks[i][j].y, blocks[i][j].x + BlockSize, blocks[i][j].y + BlockSize);}}setlinecolor(RGB(255, 0, 0)); // 设置进度条颜色setfillcolor(RGB(255, 0, 0));fillrectangle(0, BlockSize * (RowNum + 0.2), remainTime * BlockSize * ColNum / maxTime, BlockSize * (RowNum + 0.8)); // 绘制进度条TCHAR s[80]; // 定义字符数组setbkmode(TRANSPARENT);settextcolor(RGB(0, 0, 0));settextstyle(25, 0, _T("宋体"));swprintf_s(s, _T("当前第%d关,已得%d分,达到%d分进入下一关"), level, score, int(0.9 * noZeroBlockNum));outtextxy(BlockSize * (ColNum / 4.5), BlockSize * (RowNum + 1.1), s);FlushBatchDraw();}void updateWithoutInput(){static clock_t start = clock(); // 记录第一次运行时刻clock_t now = clock(); // 获取当前时刻double duration = double(now - start) / CLOCKS_PER_SEC; // 程序运行时间remainTime = maxTime - duration - punishTime;if (score >= int(0.9 * noZeroBlockNum)){level++; // 进入下一关start = clock(); // 重新开始计时startup();}else if (remainTime <= 0){start = clock();startup();}}void updateWithInput(){if (remainTime <= 0){return;}int i, j;ExMessage e;if (peekmessage(&e)){if (e.message == WM_LBUTTONDOWN){int clicked_i = int(e.y) / BlockSize;int clicked_j = int(e.x) / BlockSize;if (blocks[clicked_i][clicked_j].colorId != 0){return;}show(); // 先绘制其他方块drawBlockHint(clicked_i, clicked_j, RGB(100, 100, 100), 1); // 对被点击的空白方块,绘制填充灰色的空白方块Block fourBlocks[4] = { blocks[clicked_i][clicked_j] }; // 初始化为点击的方块int search;for (search = 0; clicked_i - search >= 0; search++) // 向上搜索{if (blocks[clicked_i - search][clicked_j].colorId != 0){fourBlocks[0] = blocks[clicked_i - search][clicked_j];break;}}for (search = 0; clicked_i + search < RowNum; search++) // 向下搜索{if (blocks[clicked_i + search][clicked_j].colorId != 0){fourBlocks[1] = blocks[clicked_i + search][clicked_j];break;}}for (search = 0; clicked_j - search >= 0; search++) // 向左搜索 {if (blocks[clicked_i][clicked_j - search].colorId != 0){fourBlocks[2] = blocks[clicked_i][clicked_j - search];break;}}for (search = 0; clicked_j + search < ColNum; search++) // 向右搜索{if (blocks[clicked_i][clicked_j + search].colorId != 0){fourBlocks[3] = blocks[clicked_i][clicked_j + search];break;}}int colorStatistics[ColorTypeNum + 1] = { 0 };int isBadClick = 1;for (i = 1; i <= ColorTypeNum; i++){for (j = 0; j < 4; j++){if (fourBlocks[j].colorId == i){colorStatistics[i]++;}}if (colorStatistics[i] >= 2){isBadClick = 0;for (j = 0; j < 4; j++){if (fourBlocks[j].colorId == i){drawBlockHint(fourBlocks[j].i, fourBlocks[j].j, RGB(0, 0, 0), 0);blocks[fourBlocks[j].i][fourBlocks[j].j].colorId = 0;}}score += colorStatistics[i];}}if (isBadClick == 1) // 如果错误点击{punishTime += 10;}FlushBatchDraw();Sleep(300);}}}int main(){startup();while (1){show();updateWithoutInput();updateWithInput();}return 0;}
    需要做网站?需要网络推广?欢迎咨询客户经理 13272073477