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

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

服务器之家 - 编程语言 - C/C++ - C语言+MySQL实现推箱子游戏

C语言+MySQL实现推箱子游戏

2022-09-27 15:27霸道小明 C/C++

经典的推箱子是一个来自日本的古老游戏,目的是在训练你的逻辑思考能力。本文将通过C语言和MySQL实现推箱子这一经典游戏,感兴趣的可以了解一下

图片素材及源码

下载链接

数据库SQL语句

?
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
create database box_man if not exists;
use box_man;
create table users (
    id int not null auto_increment,
    username varchar(64) not null,
    password varchar(32) not null,
    level_id int default 1,
    primary key(id),
    unique key username(username)
);
 
create table levels(
    id int not null auto_increment,
    name varchar(64) not null,
    map_row int not null,
    map_column int not null,
    map_data varchar(4096) not null,
    next_level_id int default 0 comment '下一关的id,0代表通关',
    primary key(id)
);
insert into users
    values(1,'Jack',md5(123456),1);
insert into levels
    values(1,'小试牛刀→第一关',8,10,'-1,0,0,0,0,0,0,0,-1,-1|-1,0,2,2,2,2,1,0,-1,-1|0,0,0,2,2,2,4,0,0,0|0,1,1,4,0,4,1,4,1,0|0,1,4,4,1,1,0,4,1,0|0,1,1,1,3,0,1,1,1,0|0,0,0,0,1,1,1,0,0,0,|-1,-1,-1,0,0,0,0,0,-1,-1',2),
    (2,'绝地强者→第二关',6,6,'0,0,0,0,0,0|0,2,2,1,1,0|0,4,4,1,3,0|0,2,1,4,0,0|0,1,1,1,0,-1|0,0,0,0,0,-1',3),
    (3,'天外强人→第三关',8,10,'-1,-1,0,0,0,0,0,0,-1,-1|0,0,0,1,1,1,0,0,0,0|0,1,1,1,4,1,4,1,1,0|0,1,4,1,1,1,4,1,3,0|0,0,0,4,4,0,0,0,0,0,|-1,-1,0,1,1,2,2,0,-1,-1|-1,-1,0,2,2,2,2,0,-1,-1|-1,-1,0,0,0,0,0,0,-1,-1',4),
    (4,'死而复生→BOSS关',8,8,'-1,0,0,0,0,0,0,-1|-1,0,2,1,2,2,0,-1|-1,0,2,1,4,2,0,-1|0,0,0,1,1,4,0,0|0,1,4,1,1,4,1,0|0,1,0,4,0,0,1,0|0,1,1,1,3,1,1,0|0,0,0,0,0,0,0,0',0);

操作数据库C++代码

database.h

?
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
#pragma once
#include<string>
using namespace std;
 
#define LINE 48
#define COLUMN 48
 
typedef struct _userinfo {
    //这里将字符串初始为空
    _userinfo() :id(0), username(""), password(""), level_id(0) {};
    int id; // 用户id
    string username;
    string password;    // 密码
    int level_id;
}userinfo;
 
typedef struct _levelinfo {
    _levelinfo() :id(0), name(""), map_row(0), map_column(0), map_data(""), next_level(0) {};
    int id; // 关卡id
    string name;    // 关卡名字
    int map_row;    // 地图行数
    int map_column; //地图列数
    string map_data;    // 二维地图数据
    int next_level; //下一关卡id
}levelinfo;
 
bool fetch_user_info(userinfo& user);
int fetch_level_info(levelinfo& level, int level_id);
bool transform_map_db2array(levelinfo& level, int map[][COLUMN]);
bool update_user_level(userinfo& user, int next_level_id);
bool resiting_data(userinfo& user);

database.cpp

记得更改自己的用户名和密码

?
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
#include"database.h"
#include<mysql.h>
#include<stdio.h> // 会使用C的接口
 
#define DB_NAME "box_man"    //数据库名
#define DB_HOST "127.0.0.1"    //IP地址
#define DB_PORT 3306            //端口号
#define DB_USER "root"           //用户名
#define DB_USER_PASSWORD "123456"    //密码
 
 
 
/******************************
 *功能:数据库链接
 * 输入:
 *      mysql - 数据库访问句柄
 *
 * 返回值:
 *      true - 连接成功
 *      false - 连接失败
 *****************************/
bool connect_db(MYSQL& mysql) {
    //1.初始数据库句柄
    mysql_init(&mysql);
 
    //2.设置字符编码(为句柄设置)
    //windows支持中文,在windows上一般是gbk字符集,gbk包含中文简体和繁体,简体是gbk2312
    mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, "gbk");
 
    //3.连接数据库
    //句柄,主机,用户名,密码,数据库,端口号,——,——
    if (mysql_real_connect(&mysql, DB_HOST, DB_USER, DB_USER_PASSWORD, DB_NAME, DB_PORT, NULL, 0) == NULL) {
        printf("数据库连接失败,错误原因:%s\n", mysql_error(&mysql));
        return false;
    }
    return true;
}
 
/******************************
 *功能:通过用户名和密码获取用户信息
 * 输入:
 *      user - 用户信息结构体
 *
 * 返回值:
 *      获取成功返回true,失败false
 *****************************/
bool fetch_user_info(userinfo& user) {
    MYSQL mysql;    //定义一个句柄,对mysql的访问都是通过这个句柄(就是一个集合)
    MYSQL_RES* res;     // 查询的结果集
    MYSQL_ROW row;      // 如果结果集有多条,可以使用row获取
    char sql[256];
    bool ret = false;
    //1.连接数据库
    if (connect_db(mysql) == false) {
        return false;
    }
    //2.设置SQL查询语句
    //snprintf函数,设将可变参数(...)按照 format 格式化成字符串,并将字符串复制到 str 中,size 为要写入的字符的最大数目,超过 size 会被截断。
    snprintf(sql, 256, "select id,level_id from users where username='%s' and password = md5('%s');", user.username.c_str(), user.password.c_str());
    ret = mysql_query(&mysql, sql); //mysql查询,成功返回0
    if (ret) {
        printf("数据库查询出错,%s错误原因:%s\n", sql, mysql_error(&mysql));
        // 关闭数据库
        mysql_close(&mysql);
        return false;
    }
    
    //3.获取查到的结果
    res = mysql_store_result(&mysql);
    //row获得结果集里的一行记录,再通过下标访问记录里的数据,如果结果集有多行时可以通过 while(row = mysql_fetch_row(res)){ }循环获得
    row = mysql_fetch_row(res);
 
    if (row == NULL) {  //没有查到记录
        //1.释放结果集
        mysql_free_result(res);
        //1.关闭数据库
        mysql_close(&mysql);
        return false;
    }
    user.id = atoi(row[0]); // 字符串转整数
    user.level_id = atoi(row[1]);
    //4.返回结果
    mysql_free_result(res);
    mysql_close(&mysql);
    return true;
}
 
/*********************************
 *功能:根据关卡id获取完整的关卡信息(如:地图,下一关等)
 * 输入:
 *      level - 保存关卡信息的结构体
 *      level_id - 获取关卡id
 * 返回值:
 *      -1 - 数据库连接或查询出错
 *      0  - 查找结果为空
 *      1 - 查找成功
 *********************************/
int fetch_level_info(levelinfo& level, int level_id) {
    MYSQL mysql;    //定义一个句柄,对mysql的访问都是通过这个句柄(就是一个集合)
    MYSQL_RES* res;     // 查询的结果集
    MYSQL_ROW row;      // 如果结果集有多条,可以使用row获取
    char sql[256];
    bool ret = false;
    //1.连接数据库
    if (connect_db(mysql) == false) {
        return -1;
    }
    //2.编写SQL语句
    snprintf(sql, 256, "select name,map_row,  map_column,map_data,next_level_id from levels where id=%d;", level_id);
    ret = mysql_query(&mysql, sql); //mysql查询,成功返回0
    if (ret) {
        printf("数据库查询出错,%s错误原因:%s\n", sql, mysql_error(&mysql));
        // 关闭数据库
        mysql_close(&mysql);
        return -1;
    }
 
    //3.获取查到的结果
    res = mysql_store_result(&mysql);
    //row获得结果集里的一行记录,再通过下标访问记录里的数据,如果结果集有多行时可以通过 while(row = mysql_fetch_row(res)){ }循环获得
    row = mysql_fetch_row(res);
 
    if (row == NULL) {  //没有查到记录
        //1.释放结果集
        mysql_free_result(res);
        //1.关闭数据库
        mysql_close(&mysql);
        return 0;
    }
    level.id = level_id;
    level.name = row[0];
    level.map_row = atoi(row[1]);
    level.map_column = atoi(row[2]);
    level.map_data = row[3];
    level.next_level = atoi(row[4]);
    //string用printf输出,使用.c_str()可以返回一个const char* 的指针
    //printf("level id: %d  name: %s map row: %d  map column: %d map data: %s next level: %d\n", level.id, level.name.c_str(), level.map_row, level.map_column, level.map_data.c_str(), level.next_level);
    
    //4.返回结果
    mysql_free_result(res);
    mysql_close(&mysql);
    return 1;
}
 
/******************************
 *功能:将获得的关卡数据转换到map地图数组中
 * 输入:
 *      level - 关卡数据
 *      map - 二维地图数组
 *
 * 返回值:
 *      false - 转换失败
 *      true - 转换成功
 *****************************/
bool transform_map_db2array(levelinfo& level, int map[][COLUMN]) {
    if (level.map_row > LINE || level.map_column > COLUMN) {
        printf("地图过大,请重新设置\n");
        return false;
    }
    if (level.map_data.length() < 1) {
        printf("地图数据有误,请重新设置!\n");
        return false;
    }
    long long start = 0, end = 0;
    int row = 0, column = 0;
    do {
        /******************************
         *find返回size_t类型值,size_t在64位下是8字节长度,因此这里把start和end设置成long long类型(不设置会有“数据丢失”警告)。当然也可以简单粗暴的修改警告设置
        ******************************/
        end = level.map_data.find('|', start);
        if (end == -1) {
            end = level.map_data.length();
        }
        //合法性检查,结束时end=level.map_data.length,start=end+1
        if (start >= end)
            break;
        string line = level.map_data.substr(start, end - start);
        //printf("get-line:%s\n", line.c_str());
        // 对行数据进行解析:0,1,0,1,1,1,1,1,1,1,0,0
        char* next_token = NULL;
        //line转为const char*(C语言中的字符串),strtok_s会修改line里的值,按照某个特定字符,所以需要将line.c_str()转为char *
        // strtok_s第一个参数是要劈开的字符串(要求是一个char *类型的);第二个参数是按某个字符劈开(会把“,”逗号变成‘\0'结束符,也就是说会修改原来字符串,所以要把原来的const char*转为char*);第三个参数是这个接口
        // 需要的,用来做定位功能,
        //printf("%p\n", &line[2]);
        char* item = strtok_s((char*)line.c_str(), ",", &next_token);
        column = 0;
        //printf("%p", next_token);
        //::system("pause");
        //如果某行数据个数多余这个地图列数,控制只读取level.map_column-1个
        while (item&&column<level.map_column) {
            map[row][column] = atoi(item);
            column++;
            //再使用strtok_s接口第一个参数可以写NULL,应该是next_token参数上一次做定位功能时已经记录了要劈开字符串给的某个位置
            item = strtok_s(NULL, ",", &next_token);
        }
 
        //合法性检查
        if (column < level.map_column) { // 某行的数据个数小于这个地图的列数
            printf("地图数据解析出错,终止!\n");
            return false;
        }
        row++;
 
        //列数多余的话直接舍弃
        if (row >= level.map_row) {
            break;
        }
        start = end + 1;
    } while (1 == 1);
    //列数多余则直接报错
    if (row < level.map_row) {
        printf("地图行数少于设定,终止!");
        return false;
    }
    return true;
}
 
/******************************
 *功能:更新用户游戏进度信息
 * 输入:
 *      user - 用户信息
 *      next_level_id - 游戏进度
 *
 * 返回值:
 *      获取成功返回true,失败false
 *****************************/
bool update_user_level(userinfo& user, int next_level_id) {
    MYSQL mysql;    //定义一个句柄,对mysql的访问都是通过这个句柄(就是一个集合)
    char sql[256];
    bool ret = false;
    //1.连接数据库
    if (connect_db(mysql) == false) {
        return false;
    }
    //2.编写SQL语句
    snprintf(sql, 256, "update users set level_id=%d where id=%d", next_level_id, user.id);
    ret = mysql_query(&mysql, sql);
 
    if (ret) {
        printf("数据库更新出错,%s错误原因:%s\n", sql, mysql_error(&mysql));
        // 关闭数据库
        mysql_close(&mysql);
        return false;
    }
    user.level_id = next_level_id;
    mysql_close(&mysql);
    return true;
}
 
/******************************
 *功能:重置用户游戏进度信息
 * 输入:
 *      user - 用户信息
 *
 * 返回值:
 *      false - 重置失败
 *      true - 重置成功
 *****************************/
bool resiting_data(userinfo& user) {
    MYSQL mysql;    //定义一个句柄,对mysql的访问都是通过这个句柄(就是一个集合)
    char sql[256];
    bool ret = false;
    //1.连接数据库
    if (connect_db(mysql) == false) {
        return false;
    }
    //2.编写SQL语句
    snprintf(sql, 256, "update users set level_id=1 where id=%d", user.id);
    ret = mysql_query(&mysql, sql);
    //3.判断查询结果
    if (ret) {
        printf("数据库更新出错,%s错误原因:%s\n", sql, mysql_error(&mysql));
        // 关闭数据库
        mysql_close(&mysql);
        return false;
    }
    user.level_id = 1;
    //4.关闭数据库
    mysql_close(&mysql);
    return true;
}

推箱子游戏代码

box_man.h

?
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
#pragma once
#define RATIO 40
 
#define SCREEN_WIDTH 740
#define SCREEN_HEIGHT 500
 
#define START_X 50
#define START_Y 75
 
#define KEY_UP 'W'
#define KEY_LEFT 'A'
#define KEY_RIGHT 'D'
#define KEY_DOWN 'S'
#define KEY_OUT 'Q'
#define GAME_AGAIN 'R'
 
#define MAX_RETRY_TIMES 4
#define BG_IMAGE -1
#define isValid(next_pos) next_pos.x > 0 && next_pos.x < LINE && next_pos.y>0 && next_pos.y < COLUMN
 
typedef enum _PROPS     PROPS;
typedef enum _DIRECTION DIRECTION;
typedef struct _POS     POS;
 
enum  _PROPS  {
    WALL,   //墙
    FLOOR,  //地板
    BOX_DES,//箱子目的地
    MAN,    //小人
    BOX,    //箱子
    HIT,    //箱子命中目标
    MAN_DES,    //人站在目标上
    VECTOR  //通关图片
};
 
enum  _DIRECTION {
    UP,
    DOWN,
    LEFT,
    RIGHT
};
 
struct _POS {
    int x;  //小人所在二维数组的行数
    int y;  //小人所在二维数组的列数
};

box_man.cpp

?
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
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
#include<graphics.h>
#include<iostream>
#include<stdlib.h>
#include<string>
#include<conio.h> //???
#include"box_man.h"
#include"database.h"
using namespace std;
 
 
int map[LINE][COLUMN] = { 0 };
 
POS man;
IMAGE images[9];
 
/*****************************
* 功能:判断游戏是否结束
* 输入:
*       无
* 输出:
*       true - 未结束
*       flase - 结束
*****************************/
bool isGameOver() {
    for (int i = 0; i < LINE; ++i) {
        for (int j = 0; j < COLUMN; ++j) {
            if (map[i][j] == BOX_DES)
                return false;
        }
    }
    return true;
}
 
/*****************************
* 功能:加载游戏结束图片
* 输入:
*       无
* 输出:
*       无
*****************************/
void show_over() {
    cleardevice();
    IMAGE game_over;
    loadimage(&game_over, _T("gameover.png"), SCREEN_WIDTH, SCREEN_HEIGHT, true);
    putimage(0, 0, &game_over);
}
 
/*****************************
* 功能:在指定位置更改地图信息 显示指定图片
* 输入:
*       next_pos - 指定位置
*       prop - 指定图片
* 输出:  
*       无
*****************************/
void changeMap(POS* next_pos, PROPS prop) {
    map[next_pos->x][next_pos->y] = prop;
    putimage(START_X + next_pos->y * RATIO, START_Y + next_pos->x * RATIO, &images[prop]);
}
 
/*****************************
* 功能:在指定位置显示指定图片
* 输入:
*       next_pos - 指定位置
*       prop - 指定图片
* 输出:
*       无
*****************************/
void changeMap2(POS* next_pos, PROPS prop) {
    putimage(START_X + next_pos->y * RATIO, START_Y + next_pos->x * RATIO, &images[prop]);
}
 
/*****************************
* 功能:控制小人向指定方向移动
* 输入:
*       direct - 指定方向
* 输出:
*       无
*****************************/
void gameControl(DIRECTION direct) {
    POS next_pos = man;
    POS next_next_pos = man;
    switch (direct) {
    case UP:
        next_pos.x--;
        next_next_pos.x -= 2;
        break;
    case DOWN:
        next_pos.x++;
        next_next_pos.x += 2;
        break;
    case LEFT :
        next_pos.y--;
        next_next_pos.y -= 2;
        break;
    case RIGHT:
        next_pos.y++;
        next_next_pos.y += 2;
        break;
    }  
    //宏展开next_pos.x > 0 && next_pos.x < LINE && next_pos.y>0 && next_pos.y < COLUMN
    // 人的前方是地板
    if (isValid(next_pos) && map[next_pos.x][next_pos.y] == FLOOR) {
        //人的脚下是目的地
        if (map[man.x][man.y] == BOX_DES) {
            changeMap(&next_pos, MAN);
            changeMap2(&man, BOX_DES);
        }
        else {
            changeMap(&man, FLOOR);
            changeMap(&next_pos, MAN);
        }  
        man = next_pos;
    }
    // 人的前方是箱子
    else if (isValid(next_next_pos) && map[next_pos.x][next_pos.y] == BOX) {   
        if (map[next_next_pos.x][next_next_pos.y] == FLOOR) {   // 箱子的前方是地板
            changeMap(&next_next_pos, BOX);
            changeMap(&next_pos, MAN);
            if (map[man.x][man.y] == BOX_DES) {
                changeMap(&man, BOX_DES);
            }
            else {
                changeMap(&man, FLOOR);
            }
            man = next_pos;
        }
        else if (map[next_next_pos.x][next_next_pos.y] == BOX_DES) {    // 箱子前面是目的地
            changeMap(&next_next_pos, HIT);
            changeMap(&next_pos, MAN);
            if (map[man.x][man.y] == BOX_DES) {
                changeMap(&man, BOX_DES);
            }
            else {
                changeMap(&man, FLOOR);
            }
            man = next_pos;
        }  
    }
    //前方是目的地
    else if (isValid(next_pos) && map[next_pos.x][next_pos.y] == BOX_DES) {
        //小人脚下也是目的地
        if (map[man.x][man.y] == BOX_DES) {
            changeMap2(&next_pos, MAN_DES);
            changeMap(&man, BOX_DES);
        }
        else {
            changeMap(&man, FLOOR);
            changeMap2(&next_pos, MAN_DES);
            
        }
        man = next_pos;
    }
    //前方是箱子命中点
    else if (isValid(next_pos) && map[next_pos.x][next_pos.y] == HIT) {
        if (map[next_next_pos.x][next_next_pos.y] == FLOOR) {   // 箱子的前方是地板
            changeMap(&next_next_pos, BOX);
            changeMap(&next_pos, BOX_DES);
            changeMap2(&next_pos, MAN_DES);
            //人的脚下是目的地
            if (map[man.x][man.y] == BOX_DES) {
                changeMap(&man, BOX_DES);
            }
            else {
                changeMap(&man, FLOOR);
            }
            man = next_pos;
        }
        else if (map[next_next_pos.x][next_next_pos.y] == BOX_DES) {    // 箱子前面是目的地
            changeMap(&next_next_pos, HIT);
            changeMap(&next_pos, BOX_DES);
            changeMap2(&next_pos, MAN_DES);
            //人的脚下是目的地
            if (map[man.x][man.y] == BOX_DES) {
                changeMap(&man, BOX_DES);
            }
            else {
                changeMap(&man, FLOOR);
            }
            man = next_pos;
        }
    }
}
 
/*****************************
* 功能:用户登录
* 输入:
*       user - 用户信息
* 输出:
*       false - 登录失败
*       true - 登录成功
*****************************/
bool login(userinfo& user) {
    int times = 0;
    bool ret = false;
    do {
        cout << "请输入用户名:";
        cin >> user.username;
        cout << "请输入密码:";
        cin >> user.password;
        ret = fetch_user_info(user);
        times++;
        if (times >= MAX_RETRY_TIMES)
            break;
        if (ret == false) {
            cout << "登录失败,请重新输入!" << endl;
        }
    } while (!ret);
    return ret;
}
 
/*****************************
* 功能:加载游戏素材
* 输入:
*       无
* 输出:
*       无
*****************************/
void init_graph() {
    initgraph(SCREEN_WIDTH, SCREEN_HEIGHT);
    loadimage(&images[WALL], _T("wall_right.bmp"), RATIO, RATIO, true);
    loadimage(&images[FLOOR], _T("floor.bmp"), RATIO, RATIO, true);
    loadimage(&images[BOX_DES], _T("des.bmp"), RATIO, RATIO, true);
    loadimage(&images[MAN], _T("man.bmp"), RATIO, RATIO, true);
    loadimage(&images[BOX], _T("box.jpg"), RATIO, RATIO, true);
    loadimage(&images[HIT], _T("box_des.jpg"), RATIO, RATIO, true);
    loadimage(&images[MAN_DES], _T("man_des.bmp"), RATIO, RATIO, true);
    loadimage(&images[VECTOR], _T("vector.png"), 6*RATIO, 6*RATIO, true);
}
 
/*****************************
* 功能:显示指定地图
* 输入:
*       level - 指定地图
* 输出:
*       无
*****************************/
void show_images(levelinfo& level) {
    cleardevice();  // 每次贴图清屏一下
    IMAGE bg_img;
    //图片的宽,高最后一个参数“是否拉伸”
    loadimage(&bg_img, _T("bgimage.png"), SCREEN_WIDTH, SCREEN_HEIGHT, true);
    putimage(0, 0, &bg_img);
    for (int i = 0; i < level.map_row; ++i) {
        for (int j = 0; j < level.map_column; ++j) {
            if (map[i][j] == MAN) {
                man.x = i;
                man.y = j;
            }
            if (map[i][j] == BG_IMAGE)
                continue;
            putimage(START_X + j * RATIO, START_Y + i * RATIO, &images[map[i][j]]);
        }
    }
    const char* str = level.name.c_str();
    setbkmode(TRANSPARENT);
    setfont(RATIO, 0, _T("华文楷体"));
    setcolor(WHITE);
    outtextxy(200, 25, str);
}
 
/*****************************
* 功能:载入PNG图并去透明部分
* 输入:
*       无
* 输出:
*       无
*****************************/
void drawAlpha(IMAGE* picture, int  picture_x, int picture_y) //x为载入图片的X坐标,y为Y坐标
{
 
    // 变量初始化
    DWORD* dst = GetImageBuffer();    // GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带
    DWORD* draw = GetImageBuffer();
    DWORD* src = GetImageBuffer(picture); //获取picture的显存指针
    int picture_width = picture->getwidth(); //获取picture的宽度,EASYX自带
    int picture_height = picture->getheight(); //获取picture的高度,EASYX自带
    int graphWidth = getwidth();       //获取绘图区的宽度,EASYX自带
    int graphHeight = getheight();     //获取绘图区的高度,EASYX自带
    int dstX = 0;    //在显存里像素的角标
 
    // 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算
    for (int iy = 0; iy < picture_height; iy++)
    {
        for (int ix = 0; ix < picture_width; ix++)
        {
            int srcX = ix + iy * picture_width; //在显存里像素的角标
            int sa = ((src[srcX] & 0xff000000) >> 24); //0xAArrggbb;AA是透明度
            int sr = ((src[srcX] & 0xff0000) >> 16); //获取RGB里的R
            int sg = ((src[srcX] & 0xff00) >> 8);   //G
            int sb = src[srcX] & 0xff;              //B
            if (ix >= 0 && ix <= graphWidth && iy >= 0 && iy <= graphHeight && dstX <= graphWidth * graphHeight)
            {
                dstX = (ix + picture_x) + (iy + picture_y) * graphWidth; //在显存里像素的角标
                int dr = ((dst[dstX] & 0xff0000) >> 16);
                int dg = ((dst[dstX] & 0xff00) >> 8);
                int db = dst[dstX] & 0xff;
                draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16)  //公式: Cp=αp*FP+(1-αp)*BP  ; αp=sa/255 , FP=sr , BP=dr
                    | ((sg * sa / 255 + dg * (255 - sa) / 255) << 8)         //αp=sa/255 , FP=sg , BP=dg
                    | (sb * sa / 255 + db * (255 - sa) / 255);              //αp=sa/255 , FP=sb , BP=db
            }
        }
    }
}
 
/*****************************
* 功能:载入“恭喜通关”图片
* 输入:
*       无
* 输出:
*       无
*****************************/
void promot_over() {
    setbkmode(TRANSPARENT);
    drawAlpha(&images[VECTOR], 250, 130);     // 载入PNG图并去透明部分
    Sleep(1100);
}
 
/*****************************
* 功能:休眠
* 输入:
*       interval - 休眠时间
* 输出:
*       无
*****************************/
void wait(int interval) {
    int count = interval / 10;
    for (int i = 0; i < count; ++i) {
        Sleep(10);
        if (_kbhit())
            return;
    }
}
 
/*****************************
* 功能:游戏控制
* 输入:
*       level - 挡墙关卡数据
*       user - 用户信息
* 输出:
*       无
*****************************/
void game_operation(levelinfo& level, userinfo& user) {
    bool quit = false;
    do {
        //判断是否有按键按下
        if (_kbhit()) {
            //无缓冲读取
            char ch = _getch();
            if (ch == KEY_UP) {
                gameControl(UP);
            }
            else if (ch == KEY_DOWN) {
                gameControl(DOWN);
            }
            else if (ch == KEY_LEFT) {
                gameControl(LEFT);
            }
            else if (ch == KEY_RIGHT) {
                gameControl(RIGHT);
            }
            else if (ch == GAME_AGAIN) {
                fetch_level_info(level, user.level_id);
                transform_map_db2array(level, map);
                show_images(level);
            }
            else if (ch == KEY_OUT) {
                closegraph();
                exit(0);
            }
            if (isGameOver()) {
                //更新用户下一关关卡信息(用户通关后直接跳转下一关)
                update_user_level(user, level.next_level);
                quit=true;
            }
        }
        wait(100);
    } while (quit == false);
}
 
/*****************************
* 功能:根据“由用户ID获取关卡数据”的返回结果进行判断
* 输入:
*       result - 返回结果(-1:获取失败  0:用户已通关  1:获取成功)
*       level - 关卡数据
*       user - 用户信息
* 输出:
*       无
*****************************/
void judge_by_result(int result, levelinfo& level, userinfo& user) {
    if (result == 1) {
        return;
    }
    else if (result == -1) {
        closegraph();
        cout << "获取关卡数据失败,请重试!" << endl;
        std::system("pause");
        exit(-1);
    }
    else if (result == 0) {
        show_over();
        do {
            //判断是否有按键按下
            if (_kbhit()) {
                //无缓冲读取
                char ch = _getch();
                if (ch == KEY_OUT) {
                    closegraph();
                    exit(0);
                }
                else if (ch == GAME_AGAIN) {
                    if (!resiting_data(user)) {
                        std::system("pause");
                        closegraph();
                        exit(0);
                    }
                    break;
                }
            }
            Sleep(50);
        } while (1);
        fetch_level_info(level, user.level_id);
    }
}
int main() {
    //用户身份验证
    userinfo user;
    levelinfo level;
    if (!login(user)) {
        cout << "登录失败,请重新登录!" << endl;
        ::system("pause");
        exit(-1);
    }
    init_graph();
    //循环(读取关卡→用户操作)
    do {
        //根据用户信息加载关卡数据
        int result = fetch_level_info(level, user.level_id);
        judge_by_result(result, level, user);
        //将关卡数据数据转换到map游戏地图中
        transform_map_db2array(level, map);
        //加载游戏图片
        show_images(level);
        //小人移动
        game_operation(level, user);
        //恭喜通关
        promot_over();
    } while (1);
    std::system("pause");
    closegraph();
    return 0;
}

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

原文链接:https://blog.csdn.net/qq_54169998/article/details/122990017

延伸 · 阅读

精彩推荐
  • C/C++C++取得当前时间的方法

    C++取得当前时间的方法

    这篇文章主要介绍了C++取得当前时间的方法,实例展示了通过系统函数获取当前时间的实现方法,非常简单易懂,需要的朋友可以参考下...

    C++教程网8102021-02-14
  • C/C++C语言中对数组赋值的三种形式

    C语言中对数组赋值的三种形式

    这篇文章主要给大家介绍了关于C语言中对数组赋值的3种形式,文中通过示例代码介绍的非常详细,对大家学习或者使用C语言具有一定的参考学习价值,需...

    onClick1238172021-08-02
  • C/C++C++实现LeetCode(124.求二叉树的最大路径和)

    C++实现LeetCode(124.求二叉树的最大路径和)

    这篇文章主要介绍了C++实现LeetCode(124.求二叉树的最大路径和),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参...

    Grandyang10272021-12-03
  • C/C++详解C++中StringBuilder类的实现及其性能优化

    详解C++中StringBuilder类的实现及其性能优化

    在Java和C#中,StringBuilder可以创造可变字符序列来动态地扩充字符串,那么在C++中我们同样也可以实现一个StringBuilder并且用来提升性能,下面就来详解C++中Stri...

    jimmyjmh, monkee7612021-04-04
  • C/C++如何基于 Blueprint 在游戏中创建实时音视频功能

    如何基于 Blueprint 在游戏中创建实时音视频功能

    我们在本文先来讲讲如何在 Unreal 中用 Blueprint 快速实现。稍后会分享基于 C++的实现步骤。感兴趣的朋友跟随小编一起看看吧...

    声网Agora8922021-09-08
  • C/C++C++实现算法两个数字相加详解

    C++实现算法两个数字相加详解

    这篇文章主要介绍了C++实现算法两个数字相加详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...

    Grandyang9862021-11-23
  • C/C++C++函数中return语句的使用方法

    C++函数中return语句的使用方法

    C++中的return语句是函数中一个重要的语句,return语句用于结束当前正在执行的函数,并将控制权返回给调用此函数的函数,需要的朋友可以了解下 ...

    C++教程网3322020-11-15
  • C/C++C++设计模式之迭代器模式(Iterator)

    C++设计模式之迭代器模式(Iterator)

    这篇文章主要为大家详细介绍了C++设计模式之迭代器模式Iterator,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    chencarl8132021-06-23