服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言实现消消乐游戏的代码分享

C语言实现消消乐游戏的代码分享

2023-03-06 15:24编程小鱼六六六 C/C++

本章我们将编写十字消除游戏,用户点击空白方块,沿其上下左右方向寻找第一个彩色方块,如果有两个或两个以上颜色一致,就将其消除,感兴趣的可以了解一下

C和C++游戏趣味编程》一书各个章节的案例代码,每章案例逐步利用学到的语法知识。

本章我们将编写十字消除游戏,用户点击空白方块,沿其上下左右方向寻找第一个彩色方块,如果有两个或两个以上颜色一致,就将其消除。在进度条时间结束前消除足够的方块,可以进入下一关,效果如图所示。

首先实现随机颜色方块的表示与绘制,鼠标点击与十字消除算法;然后绘制了提示框,绘制倒计时进度条;接着进行了得分计算、胜负判断、多关卡功能的开发;学习了地址与指针的概念,并利用地址传递使得程序更加模块化;最后学习了指针和数组的知识,应用动态数组实现了游戏尺寸的动态大小调整。

C语言实现消消乐游戏的代码分享

源码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#include <graphics.h> 
#include <conio.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
 
# define BlockSize 40 // 小方块的长宽大小
# define ColorTypeNum 9 // 除了空白方块外,其他方块的颜色的个数
 
struct Block // 小方块结构体
{
    int x,y; // x y坐标
    int colorId; // 对应颜色的下标
    int i,j;  // 小方块在二维数组中的i j下标
};
 
// 全局变量
int RowNum; // 游戏画面一共RowNum行小方块
int ColNum; // 游戏画面一共ColNum列小方块
Block **blocks = NULL; // 动态二维数组指针,存储所有方块数据
COLORREF  colors[ColorTypeNum+1]; // 颜色数组,小方块可能的几种颜色
int score; // 得分数,也就是消去的方块的个数
float maxTime; // 这一关游戏的总时长
float totalTime; // 减去扣分项后的游戏总时长
float remainTime; // 剩余时间
clock_t start, finish; // 用于计时的变量
int level = 1; // 当前关卡序号
int noZeroBlockNum; // 非空白区域的砖块的个数
 
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) // 上下左右四个方向找到的4个方块,画线框提示
        rectangle(blocks[i][j].x,blocks[i][j].y,blocks[i][j].x+BlockSize,blocks[i][j].y+BlockSize);
}
 
 
void writeRecordFile(int recordScore)  //保存最高分数据文件
{
    FILE *fp;
    fp = fopen(".\\gameRecord.dat","w");
    fprintf(fp,"%d",recordScore);
    fclose(fp);
}
 
int readRecordFile()  //读取最高分数据文件
{
    int recordScore;
    FILE *fp;
    fp = fopen(".\\gameRecord.dat","r");
 
    // 如果打不开的话,就新建一个文件,其得分记录为0分
    if (fp==NULL)
    {
        writeRecordFile(0);
        return 0;
    }
    else // 能打开这个文件,就读取下最高分记录
    {
        fscanf(fp,"%d",&recordScore);
        fclose(fp);
        return recordScore;
    }
}
 
void startup() // 初始化函数
{
    int i,j;
    start = clock(); // 记录当前运行时刻
 
    if (level>1) // 如果不是第1关,则先清除二维数组内存,再重新开辟内存空间
    {
        for (i=0;i<RowNum;i++)
            free(blocks[i]);
        free(blocks);
    }
 
    // 根据是第几关,调整这一关对应的游戏画面的大小
    RowNum = 12 + level/2;  // 行数添加的慢一些,是一个长方形的形状
    ColNum = 20 + level;
 
    // 开辟动态二维数组
    blocks = (Block **) malloc(RowNum*sizeof(Block *));
    for (i=0;i<RowNum;i++)
        blocks[i] = (Block *) malloc(ColNum*sizeof(Block));
 
    maxTime = 200 + level*10; // 这一关游戏设定的总时长,每关时长+10秒
    totalTime = maxTime; // 游戏总时长,每次出错,会扣10秒钟
 
    int width = BlockSize*ColNum;      // 设定游戏画面的大小
    int height = BlockSize*(RowNum+3); // 最下面用来显示一些提示信息
    initgraph(width,height);
    setbkcolor(RGB(220,220,220));
    setlinestyle(PS_SOLID,2);
    cleardevice();
    srand(time(0));
    BeginBatchDraw(); // 开始批量绘制
 
    score = 0; // 得分数,也就是消去的方块的个数
    noZeroBlockNum = 0; // 非空白区域的砖块的个数
 
    colors[0] = RGB(220,220,220); // 颜色数组第一种颜色为灰白色,表示空白小方块
    for (i=1;i<ColorTypeNum+1;i++) // 其他几种颜色为彩色
        colors[i] = HSVtoRGB((i-1)*40,0.6,0.8);
 
    // 对blocks二维数组进行初始化
    for (i=0;i<RowNum;i++)
    {
        for (j=0;j<ColNum;j++)
        {
            // 取随机数,1-6设为彩色色块,其他为空白色块,这样为空白色块的几率高一些
            // 初始化时,空白色块多一些,符合游戏的设定
            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++; // 统计随机产生的方块中,非零方块的个数
        }
    }
}
 
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);
        }
    }
 
    // 根据剩余时间,绘制一个倒计时进度条,进度条按最大时间maxTime秒绘制
    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);
    _stprintf(s, _T("%d"), score); 
    settextcolor(RGB(0,0,0));
    settextstyle(22, 0, _T("宋体"));
    outtextxy(BlockSize*(ColNum/2-0.1), BlockSize*(RowNum+0.2), s);
    // 输出一些游戏提示信息
    _stprintf(s, _T("点击空白方块,其十字区域有两个或以上相同颜色方块则消除;不能消除扣时间"));   
    outtextxy(BlockSize*(ColNum/15.0), BlockSize*(RowNum+1.2), s);
    _stprintf(s, _T("目前第 %d 关,时间结束前得分达到 %d 可进入下一关"),level,int(noZeroBlockNum*0.9));
    outtextxy(BlockSize*(ColNum/5.0), BlockSize*(RowNum+2.2), s);
 
    FlushBatchDraw(); // 批量绘制
}  
 
void updateWithoutInput() // 和输入无关的更新
{
    // 倒计时减少
    finish = clock(); // 当前时刻
    // 从startup运行后,这一关游戏运行了多少秒
    double duration = (double)(finish - start) / CLOCKS_PER_SEC;
    remainTime = totalTime-duration; // 游戏剩余的时间
 
    // 如果时间到了
    if (remainTime<=0)
    {
        // 读一下文件记录,如果当前得分超过记录
        if (score > readRecordFile())
        {
            // 更新下得分记录
            writeRecordFile(score);
 
            // 显示恭喜超过记录
            show();
            settextcolor(RGB(255,0,0));
            settextstyle(100, 0, _T("黑体"));
            outtextxy(BlockSize*(ColNum/30.0), BlockSize*(RowNum/3.0), _T("恭喜打破得分记录"));
            FlushBatchDraw(); // 批量绘制
            Sleep(2000);
        }
 
        if (score>=int(noZeroBlockNum*0.9))
        {
            level ++; // 如果得分达到要求,消除掉非空白方块数目的90%,关卡加1
        }
        startup(); // 调用初始化函数,重新开始游戏
        return;
    }
}
 
void updateWithInput() // 和输入有关的更新
{
    if (remainTime<=0) // 时间到了,不要操作
        return;
 
    int i,j;
    MOUSEMSG m;    
    if (MouseHit()) 
    {
        m = GetMouseMsg();     
        if(m.uMsg == WM_LBUTTONDOWN) // 当按下鼠标左键时
        {
            // 获得点击的小方块的下标
            int clicked_i = int(m.y)/BlockSize;
            int clicked_j = int(m.x)/BlockSize;
            // 点击到下面提示部分了,不用处理,函数返回
            if (clicked_i>=RowNum)
                return;
            // 如果当前点击的不是空白方块,不需要处理,返回
            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;
                }
            }
 
            // 统计fourBlocks的四个小方块,有没有同样颜色数目大于等于2的
            int colorStatistics[ColorTypeNum+1] = {0}; // 初始化个数为0
            int isBadClick = 1; // 假设点击的方块不合适,十字区域没有有效消除的方块
            for (i=1;i<ColorTypeNum+1;i++) // i=0是空白颜色,不要统计
            {
                for (j=0;j<4;j++) // 遍历fourBlocks
                {
                    if (fourBlocks[j].colorId==i)
                        colorStatistics[i]++; // 方块颜色为非零的i的话,把对应的统计个数+1
                }
                if (colorStatistics[i]>=2) // 如果这种颜色方块个数大于等于2
                {                  
                    isBadClick = 0; // 能消除了,这次点击是好的操作
                    // 把对应十字区域要消除的方块颜色改成空白颜色
                    for (j=0;j<4;j++) // 遍历fourBlocks
                    {
                        if (fourBlocks[j].colorId==i)
                        {
                            // 要消除的方块区域绘制提示框                           
                            drawBlockHint(fourBlocks[j].i,fourBlocks[j].j,RGB(0,0,0),0);   
                            // 颜色序号设为0,也就是空白的灰白色
                            blocks[fourBlocks[j].i][fourBlocks[j].j].colorId = 0;
                        }
                    }
                    score += colorStatistics[i]; // 得分加上消除的方块数
                }              
            }
 
            // 点击的方块,十字区域没有能消除的方块,为错误点击,减去10秒钟时间
            if (isBadClick==1)
                totalTime -= 10;
 
            FlushBatchDraw(); // 批量绘制
            Sleep(300); // 绘制好提示框后暂停300毫秒
 
        } // while 当按下鼠标左键时
    }
}
 
int main() // 主函数运行
{
    startup();     
    while (1)
    {
        show();
        updateWithoutInput();
        updateWithInput(); 
 
    }
    closegraph();
    return 0;
}

这一章主要讲解了指针的相关语法知识,学习了倒计时的方法,实现了十字消除游戏。读者可以尝试在本章代码基础上继续改进:

1、实现随着游戏的进行,通过关卡要求消除方块的比例越来越高;

2、利用文件读写,实现关卡数据与最高分的记录与读取。

到此这篇关于C语言实现消消乐游戏的代码分享的文章就介绍到这了,更多相关C语言消消乐游戏内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/yx5666/article/details/129025995

延伸 · 阅读

精彩推荐
  • C/C++C++的运算符你真的了解吗

    C++的运算符你真的了解吗

    这篇文章主要为大家详细介绍了C++的运算符,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮...

    没有省略号9792022-09-23
  • C/C++Qt5.9实现简单复合图形

    Qt5.9实现简单复合图形

    这篇文章主要为大家详细介绍了Qt5.9实现简单复合图形,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    比卡丘不皮9482021-09-16
  • C/C++C语言链表实现简易通讯录

    C语言链表实现简易通讯录

    这篇文章主要为大家详细介绍了C语言链表实现简易通讯录,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    陈年梦,越人心10282021-11-11
  • C/C++C++ 对多线程/并发的支持(上)

    C++ 对多线程/并发的支持(上)

    这篇文章主要介绍的是C++ 对多线程/并发的支持,并发,即同时执行多个任务,常用来提高吞吐量或者改善响应性,下面我们就来看文章详细介绍C++ 对多线...

    Zijian/TENG6842022-01-21
  • C/C++C语言堆栈入门指南

    C语言堆栈入门指南

    我身边的一些编程的朋友以及在网上看帖遇到的朋友中有好多也说不清堆栈,所以我想有必要给大家分享一下我对堆栈的看法,有说的不对的地方请朋友们...

    C语言教程网8452021-01-13
  • C/C++C语言实现翻译功能

    C语言实现翻译功能

    这篇文章主要为大家详细介绍了C语言实现简单的翻译功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    你爱吃泡芙。4212021-07-20
  • C/C++C++使用cuBLAS加速矩阵乘法运算的实现代码

    C++使用cuBLAS加速矩阵乘法运算的实现代码

    这篇文章主要介绍了C++使用cuBLAS加速矩阵乘法运算,将cuBLAS库的乘法运算进行了封装,方便了算法调用,具体实现代码跟随小编一起看看吧...

    白水baishui5062021-12-30
  • C/C++C与C++ 无参函数的区别解析

    C与C++ 无参函数的区别解析

    在《C++ 编程思想》:“关于无参函数声明,C与C++有很大的差别。在C语言中,声明int fun1(),意味着一个可以有任意数目和类型的函数;而在C++中,指的却是...

    C语言教程网2422020-12-18