参考
- 《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;
}