文章目录
- 一、项目信息
-
- [ 1、开发环境](# 1、开发环境)
- [ 2、项目模块](# 2、项目模块)
- [ 3、源码](# 3、源码)
- 二、游戏初始化
-
- [ 1、加载相关资源](# 1、加载相关资源)
- [ 2、配置随机数种子](# 2、配置随机数种子)
- [ 3、创建游戏窗口](# 3、创建游戏窗口)
- 三、地图构建
-
- [ 1、地图结构设计](# 1、地图结构设计)
- [ 2、图形示例](# 2、图形示例)
- [ 3、构建地图](# 3、构建地图)
- 四、游戏渲染
-
- [ 刷新窗口](# 刷新窗口)
- 五、游戏交互
-
- [ 检测用户输入](# 检测用户输入)
- [ 处理用户点击后工作](# 处理用户点击后工作)
- 六、游戏状态控制
- 七、帧率控制
一、项目信息
1、开发环境
VSCode + MinGw + EasyX
2、项目模块
游戏初始化
地图构建
游戏渲染
游戏交互
帧率控制
3、源码
通过网盘分享的文件:Game_YLGY.rar
链接: https://pan.baidu.com/s/1jal6Yt_mHL4MEu2quwna_g?pwd=91ju 提取码: 91ju
二、游戏初始化
1、加载相关资源
c
// 图片
IMAGE g_imgWelcomeBg; // 欢迎 图片
IMAGE g_imgBg[2]; // 背景图片
IMAGE g_imgBlocks[15][2]; // 图形块
IMAGE g_imgLoseBg; // 失败背景图片
IMAGE g_imgWinBg; // 成功背景图片
// 加载背景图片
loadimage(&g_imgBg[0], "res/bg1.png");
loadimage(&g_imgBg[1], "res/bg2.png");
loadimage(&g_imgLoseBg, "res/false.png"); // 失败背景图片
loadimage(&g_imgWinBg, "res/win.png"); // 成功背景图片
loadimage(&g_imgWelcomeBg, "res/Welcome.png");
2、配置随机数种子
c
// 配置随机种子
srand(time(NULL)); // 配置随机种子
3、创建游戏窗口
c
// 创建窗口===========================================
initgraph(WIN_WIDTH, WIN_HEIGHT);
三、地图构建
1、地图结构设计
使用数据结构描述一个图形块:
c
// 图形块结构体
struct block
{
int layer; // 图形块所在层
int type; // 图形块类型
int row, col; // 图形块所在层中,是第几行,第几列
int x, y; // 图形块所在坐标
bool top; // 图形块是否为顶层图形
int off_x, off_y; // 图形块所在层第一个方块的边界
};
struct block * layer = (struct block *)malloc(sizeof(struct block));
使用一个指针n维数组描述单层图形块指针数组。
c
g_Maps[layer] = (struct block **)malloc(sizeof(struct block *) * (rows * cols + 1));
使用一个全局二级指针数组指向所有的单层图形块指针数组表示全局地图。
c
// 图形块保存数据体
struct block **g_Maps[MAX_LAYER];
2、图形示例

3、构建地图
设计第一关地图和第二关地图共两关。
c
// 创建图形块单层
void initLayer(int layer, int rows, int cols, int off_x, int off_y, int type)
{
// 分配单层指针存储内存空间
g_Maps[layer] = (struct block **)malloc(sizeof(struct block *) * (rows * cols + 1));
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
g_Maps[layer][row * cols + col] = (struct block *)malloc(sizeof(struct block));
g_Maps[layer][row * cols + col]->off_x = off_x;
g_Maps[layer][row * cols + col]->off_y = off_y;
g_Maps[layer][row * cols + col]->row = row;
g_Maps[layer][row * cols + col]->col = col;
if (type == 0)
{
g_Maps[layer][row * cols + col]->type = 1 + rand() % 15;
}
else
{
g_Maps[layer][row * cols + col]->type = type;
}
g_Maps[layer][row * cols + col]->top = true;
g_Maps[layer][row * cols + col]->x = off_x + col * BLOCK_WIDTH;
g_Maps[layer][row * cols + col]->y = off_y + row * BLOCK_HEIGHT;
g_Maps[layer][row * cols + col]->layer = layer;
}
}
// 最后一个设为空,方便遍历
g_Maps[layer][rows * cols] = NULL;
}
// 初始化地图
void initMap()
{
int layer = 0;
if (GemaStatus == GAME_STATUS_INIT) // 在初始化状态 初始地图为第一关
{
int off = 10; // 图片间隔
int offsetX = (WIN_WIDTH - BLOCK_WIDTH * 3 - off * 2) / 2; // 横起始坐标 (背景宽度-图片宽度*2-图片间隔*2)/2
int offsetY = 270;
/*先初始化为
1 1 1
2 2 2
3 3 3
*/
for (int r = 0; r < 3; r++)
{
for (int c = 0; c < 3; c++)
{
int x = offsetX + c * (BLOCK_WIDTH + off);
int y = offsetY + r * (BLOCK_HEIGHT + off);
initLayer(layer++, 1, 1, x, y, r + 1);
}
}
// 然后打乱
for (int i = 0; i < g_Max_layer; i++)
{
int S = rand() % 9;
int D = rand() % 9;
int tmp = g_Maps[S][0]->type;
g_Maps[S][0]->type = g_Maps[D][0]->type;
g_Maps[D][0]->type = tmp;
}
}
if (GemaStatus == GAME_STATUS_LEVEL1) // 在第一关 初始地图为第二关
{
for (; layer < 12; layer++)
initLayer(layer, 1, 1, 30, 440 + layer * 10, 0);
for (; layer < 24; layer++)
initLayer(layer, 1, 1, 414, 440 + (layer - 12) * 10, 0);
for (; layer < 30; layer++)
initLayer(layer, 1, 1, 94, 583 + (layer - 24) * 10, 0);
for (; layer < 36; layer++)
initLayer(layer, 1, 1, 346, 583 + (layer - 30) * 10, 0);
for (; layer < 42; layer++)
initLayer(layer, 1, 1, 30 + (layer - 36) * 10, 130, 0);
for (; layer < 48; layer++)
initLayer(layer, 1, 1, 413 - (layer - 42) * 10, 130, 0);
initLayer(layer++, 4, 3, 159, 309, 0);
initLayer(layer++, 5, 3, 159, 228, 0);
initLayer(layer++, 5, 4, 129, 204, 0);
initLayer(layer++, 5, 5, 99, 180, 0);
initLayer(layer++, 3, 6, 70, 209, 0);
initLayer(layer++, 1, 4, 134, 133, 0);
initLayer(layer++, 1, 4, 134, 118, 0);
initLayer(layer++, 1, 4, 134, 103, 0);
initLayer(layer++, 2, 5, 100, 258, 0);
initLayer(layer++, 2, 5, 100, 243, 0);
initLayer(layer++, 2, 5, 100, 228, 0);
initLayer(layer++, 2, 5, 100, 228, 0);
initLayer(layer++, 2, 5, 100, 228, 0);
initLayer(layer++, 2, 5, 100, 228, 0);
//将地图校验并修正
checkMap();
}
}
四、游戏渲染
刷新窗口
c
void updateWindow()
{
// 开始双缓冲
BeginBatchDraw();
// 绘制背景图:实现背景图片切换,实现小草动态图
static int timecnt = 0;
if (timecnt < 20)
{
putimage(0, 0, &g_imgBg[0]);
}
else
{
putimage(0, 0, &g_imgBg[1]);
}
timecnt++;
if (timecnt == 40)
timecnt = 0;
// 绘制图形块
for (int i = 0; i < g_Max_layer; i++)
{
for (int k = 0; g_Maps[i][k]; k++)
{
struct block *p = g_Maps[i][k];
IMAGE *img = &g_imgBlocks[p->type - 1][p->top];
putimagePNG(p->x, p->y, img);
}
}
// 绘制羊槽
for (int i = 0; i < 7; i++)
{
if (store[i])
{
int x = 26 + i * 64.7;
int y = 778;
putimagePNG(x, y, &g_imgBlocks[store[i] - 1][1]);
}
}
// 结束双缓冲
EndBatchDraw();
}
五、游戏交互
检测用户输入
c
bool userClick()
{
ExMessage msg;
if (peekmessage(&msg) && msg.message == WM_LBUTTONDOWN)
{
for (int layer = g_Max_layer - 1; layer >= 0; layer--)
{
for (int k = 0; g_Maps[layer][k]; k++)
{
struct block *p = g_Maps[layer][k];
if (p->type &&
msg.x > p->x && msg.x < p->x + BLOCK_WIDTH &&
msg.y > p->y && msg.y < p->y + BLOCK_HEIGHT)
{
clickedBlock = p;
return p->top;
}
}
}
}
return false;
}
处理用户点击后工作
c
void work()
{
clearBlock(clickedBlock);
int i = 0;
for (; store[i] && i < 7; i++)
;
if (i < 7)
{
store[i] = clickedBlock->type;
}
else
{
GemaStatus = GAME_STATUS_LOSE;
}
// 如果有三个一样的则消除
int cnt = 0;
for (int i = 0; i < 7; i++)
{
if (store[i] == clickedBlock->type)
{
cnt++;
}
}
if (cnt == 3)
{
for (int i = 0; i < 7; i++)
{
if (store[i] == clickedBlock->type)
{
store[i] = 0;
}
}
// 重新排序
int j = 0;
for (int i = 0; i < 7; i++)
{
if (store[i] != 0)
{
store[j] = store[i];
j++;
}
}
for (; j < 7; j++)
{
store[j] = 0;
}
}
clickedBlock->type = 0;
setTops();
}
六、游戏状态控制
c
// 游戏状态
#define GAME_STATUS_INIT 0
#define GAME_STATUS_LEVEL1 1
#define GAME_STATUS_LEVEL2 2
#define GAME_STATUS_LOSE 3
#define GAME_STATUS_WIN 4
int GemaStatus = GAME_STATUS_INIT; // 0为初始化 1为第1关 2 为第2关 3为失败 4 为胜制
//省略.......其它代码
while (true)
{
switch (GemaStatus)
{
case GAME_STATUS_INIT:
{
GemaStatus = GAME_STATUS_LEVEL1;
}
break;
case GAME_STATUS_LEVEL1:
{
if (isWin())
{
GemaStatus = GAME_STATUS_LEVEL2;
}
}
break;
case GAME_STATUS_LEVEL2:
{
if (isWin())
{
GemaStatus = GAME_STATUS_WIN;
}
}
break;
case GAME_STATUS_LOSE:
{
putimage(0, 0, &g_imgLoseBg);
}
break;
case GAME_STATUS_WIN:
{
putimage(0, 0, &g_imgWinBg);
}
break;
default:
break;
}
}
七、帧率控制
c
while (true)
{
clock_t start_Time = clock(); // 记录帧开始时间
//省略.......其它代码
// 计算本帧已用时间
clock_t cost_time = clock() - start_Time;
// 如果本帧绘制用时少于目标时间,则休眠剩余时间
if (cost_time < FRAME_INTERVAL)
Sleep(FRAME_INTERVAL - cost_time);
}