数据库表设计
用户表
表名 |
Users |
||||
字段名 |
类型 |
是否为空 |
默认值 |
主、外键 |
备注 |
id |
int(11) |
NOT |
1,自增长 |
PK |
用户id |
username |
varchar(64) |
NOT |
用户名:英文字符、数字和特殊符号的组合 |
password |
varchar(32) |
NOT |
密码:英文字符、数字和特殊符号的组合,8-16位 |
||
level_id |
int |
1 |
当前关卡,关联Levels表中的id |
关卡表
表名 |
Levels |
|||||
字段名 |
类型 |
是否为空 |
默认值 |
主、外键 |
备注 |
id |
int |
NOT |
1 |
PK |
游戏关卡序号,从1开始 |
|
name |
varchar(64) |
NOT |
地图名称 |
map_row |
int |
NOT |
地图二位组的总行数 |
|||
map_column |
int |
NOT |
地图二维组的总列数 |
mysql> create database box_man; #创建数据库box_man mysql> mysql> create table users( #创建用户表 id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, username varchar(64) NOT NULL UNIQUE, password varchar(32) NOT NULL , level_id int default 1 ); mysql> create table levels( #创建关卡表 id int NOT NULL PRIMARY KEY default 1, name varchar(64) NOT NULL UNIQUE, map_row int NOT NULL, map_column int NOT NULL, map_data varchar(4096) NOT NULL, next_level_id int default 0 ); mysql> insert into users values(1000, 'martin', md5('123456qweQWE'), 1); mysql> insert into levels values(1, '牛刀小试', 9, 12, '0,0,0,0,0,0,0,0,0,0,0,0|0,1,0,1,1,1,1,1,1,1,0,0|0,1,4,1,0,2,1,0,2,1,0,0|0,1,0,1,0,1,0,0,1,1,1,0|0,1,0,2, 0,1,1,4,1,1,1,0|0,1,1,1,0,3,1,1,1,4,1,0|0,1,2,1,1,4,1,1,1,1,1,0|0,1,0,0,1,0,1,1,0,0,1,0|0,0,0,0,0,0,0,0,0,0, 0,0',0); |
代码优化
登录认证
database.cpp
#include "database.h" #include <mysql.h> #include <stdio.h> #define DB_NAME "box_man" #define DB_HOST "127.0.0.1" #define DB_PORT 3306 #define DB_USER "root" #define DB_USER_PASSWD "123456qweQWE" static bool connect_db(MYSQL& mysql); /*************************************************** *功能:通过用户名和密码从数据库获取用户信息 *输入: * user - 用户信息结构体 * *返回值: * 获取成功返回true, 失败false ***************************************************/ bool fetch_user_info(userinfo& user) { MYSQL mysql; MYSQL_RES* res; //查询结果集 MYSQL_ROW row; //记录结构体 char sql[256]; bool ret = false; //1.连接到数据库 if (connect_db(mysql) == false) { return false; } //2.根据用户名和密码获取用户信息(id, level_id) snprintf(sql, 256, "select id, level_id from users where username='%s' and password=md5('%s');", user.username.c_str(), user.passwd.c_str()); ret = mysql_query(&mysql, sql); //成功返回0 if (ret) { printf("数据库查询出错,%s 错误原因: %s\n", sql, mysql_error(&mysql)); mysql_close(&mysql); return false; } //3.获取结果 res = mysql_store_result(&mysql); row = mysql_fetch_row(res); if (row == NULL) {//没有查找到记录 mysql_free_result(res); mysql_close(&mysql); return false; } user.id = atoi(row[0]); user.level_id = atoi(row[1]); printf("userid: %d level_id: %d\n", user.id, user.level_id); //打印ID //4.返回结果 //释放结果集 mysql_free_result(res); //关闭数据库 mysql_close(&mysql); return true; } bool connect_db(MYSQL& mysql) { //1.初始化数据库句柄 mysql_init(&mysql); //2.设置字符编码 mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, "gbk"); //3.连接数据库 if (mysql_real_connect(&mysql, DB_HOST, DB_USER, DB_USER_PASSWD, DB_NAME, DB_PORT, NULL, 0) == NULL) { printf("数据库连接出错, 错误原因: %s\n", mysql_error(&mysql)); return false; } return true; } |
database.h
#pragma once #include <string> using namespace std; //用户信息 typedef struct _userinfo{ int id; //用户id string username; //用户名 string passwd; //密码 int level_id; //关卡id }userinfo; bool fetch_user_info(userinfo &user); |
boxman.cpp
bool login(userinfo& user) { int times = 0; bool ret = false; do{ cout << "请输入用户名: "; cin >> user.username; cout << "请输入密码: "; cin >> user.passwd; //返回 bool ,成功返回true ,失败返回false . ret = fetch_user_info(user); times++; if (times >= MAX_RETRY_TIMES) { break; } if (ret == false) { cout << "登陆失败,请重新输入!" << endl; } } while (!ret); return ret; } |
获取关卡
//database.h typedef struct _levelinfo { int id; //关卡的id string name; //关卡的名字 int map_row; //地图总行数 int map_column; //地图总列数 string map_data; //二维地图数据 int next_level; //下一关卡的id }levelinfo; bool fetch_level_info(levelinfo &level, int level_id); //database.cpp /*************************************************** *功能:根据关卡id 获取完整的关卡信息(如: 地图,下一关等) *输入: * level - 保存关卡信息的结构体变量 * level_id - 要获取详细关卡信息的关卡id *返回值: * 获取成功返回true, 失败false ***************************************************/ bool fetch_level_info(levelinfo& level, int level_id) { MYSQL mysql; MYSQL_RES* res; //查询结果集 MYSQL_ROW row; //记录结构体 char sql[256]; bool ret = false; //1.连接到数据库 if (connect_db(mysql) == false) { return false; } //2.根据关卡id查询数据库获取关卡地图信息 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); //成功返回0 if (ret) { printf("数据库查询出错,%s 错误原因: %s\n", sql, mysql_error(&mysql)); mysql_close(&mysql); return false; } //3.获取结果 res = mysql_store_result(&mysql); row = mysql_fetch_row(res); if (row == NULL) {//没有查找到记录 mysql_free_result(res); mysql_close(&mysql); return false; } 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[5]); if(debug) 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);
//释放结果集 mysql_free_result(res); //关闭数据库 mysql_close(&mysql); return true; } |
地图适配
//database.h bool transform_map_db2array(levelinfo &level, int map[LINE][COLUMN]); //database.cpp bool transform_map_db2array(levelinfo& level, int map[LINE][COLUMN]) { if (level.map_row > LINE || level.map_column > COLUMN) { printf("地图超大,请重新设置!\n"); return false; } if (level.map_data.length() < 1) { printf("地图数据有误,请重新设置!\n"); return false; } int start = 0, end = 0; int row = 0, column = 0; do { end = level.map_data.find('|', start); if (end < 0) { end = level.map_data.length(); } if (start >= end) break; string line = level.map_data.substr(start, end - start); printf("get line: %s\n", line.c_str()); //对行地图数据进行解析 char *next_token = NULL; char* item = strtok_s((char*)line.c_str(), ",", &next_token); column = 0; while (item && column < level.map_column) { printf("%s ", item); map[row][column] = atoi(item); column++; item = strtok_s(NULL, ",", &next_token); } if (column < level.map_column) { printf("地图数据解析出错,终止!\n"); return false; } printf("\n"); row++; if (row >= level.map_row) { break; } start = end + 1; } while (1 == 1); if (row < level.map_row) { printf("地图行数少于设定, %d(need: %d),终止!\n", row, level.map_row); return false; } return true; } //boxman.cpp //把数据库中的地图数据转换到map 中 ret = transform_map_db2array(level, map); |
下一关跳转
//database.h bool update_user_level(userinfo& user, int next_level_id); //database.cpp bool update_user_level(userinfo& user, int next_level_id) { MYSQL mysql; MYSQL_RES* res; //查询结果集 MYSQL_ROW row; //记录结构体 char sql[256]; bool ret = false; //1.连接到数据库 if (connect_db(mysql) == false) { return false; } //2.根据用户id 更新下一关的level_id 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; } //关闭数据库 mysql_close(&mysql); return true; } //boxman.cpp //...............前面省略N行代码.................... void gameNextScene(IMAGE* bg) { putimage(0, 0, bg); settextcolor(WHITE); RECT rec = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT }; settextstyle(20, 0, _T("宋体")); drawtext(_T("恭喜您~ \n此关挑战成功,任意键跳转到下一关!"), &rec, DT_CENTER | DT_VCENTER | DT_SINGLELINE); ::system("pause"); cleardevice(); } //...............中间省略N行代码.................... if (isGameOver()) { if (level.next_level < 1) { gameOverScene(&bg_img); quit = true; break; } gameNextScene(&bg_img); //更新用户下一关的关卡信息 if (update_user_level(user, level.next_level)) { user.level_id = level.next_level; }
break; //quit = true; } |
以下是完整代码实现:
box_man.h
#pragma once #include <graphics.h> #define KEY_UP 'w' #define KEY_DOWN 's' #define KEY_LEFT 'a' #define KEY_RIGHT 'd' #define KEY_QUIT 'q' #define START_X 100 //x轴偏移 #define START_Y 150 //y轴偏移 #define WEIGHT 960 //舞台长 #define HEIGHT 768 //舞台宽 #define RATIO 61 #define isValid(pos) pos.x>=0 && pos.x<MAP_X && pos.y>=0 && pos.y<MAP_Y //判断小人在地图行走后是否有效 #define MAX_REIRY_TIMES 4 #define MAP_X 48 #define MAP_Y 12 enum _RES { WALL, FLOOR, BOX_DES, MAN, BOX, HIT, ALL }; enum _DIRECT { UP, DOWN, LEFT, RIGHT }; struct _POS //小人坐标 { int x; int y; };
box_man.cpp
#include <iostream> #include <Windows.h> #include <string> #include <conio.h> #include "box_man.h" #include "database.h" using namespace std; int map[MAP_X][MAP_Y] = { 0 }; int hold = 0; IMAGE pic[ALL]; //资源图片 bool fetch_user_info(userinfo& user); bool fetch_level_info(levelinfo& level, int level_id); bool transform_map_dbtoarry(levelinfo& level, int map[MAP_X][MAP_Y]); bool update_user_level(userinfo& user, int next_level_id); void changeMap(struct _POS* pos, enum _RES prop) { map[pos->x][pos->y] = prop; putimage(START_X + pos->y * RATIO, START_Y + pos->x * RATIO, &pic[prop]); } struct _POS man; bool isGameOver() { int i, j; if (hold == 1) { return false; } for (i = 0; i < MAP_X; i++) { for (j = 0; j < MAP_Y; j++) { if (map[i][j] == BOX_DES) { return false; } } } return true; } void gameOverScene(IMAGE* bg) { putimage(0, 0, bg); settextcolor(WHITE); RECT rec = { 0,0,WEIGHT,HEIGHT }; settextstyle(20, 0, _T("宋体")); drawtext(_T("恭喜您~\n通关了!再见"), &rec, DT_CENTER | DT_VCENTER | DT_SINGLELINE); } void gameNextScene(IMAGE* bg) { putimage(0, 0, bg); settextcolor(WHITE); RECT rec = { 0,0,WEIGHT,HEIGHT }; settextstyle(20, 0, _T("宋体")); drawtext(_T("恭喜您~\n此关挑战成功,任意键跳转到下一关!"), &rec, DT_CENTER | DT_VCENTER | DT_SINGLELINE); ::system("pause"); cleardevice(); } void gameCotrol(enum _DIRECT direct) { struct _POS next_pos = man; struct _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; } if (isValid(next_pos) && map[next_pos.x][next_pos.y] == FLOOR) //小人前是地板 { if (hold == 0) { changeMap(&next_pos, MAN); changeMap(&man, FLOOR); man = next_pos; } else { changeMap(&next_pos, MAN); changeMap(&man, BOX_DES); man = next_pos; hold = 0; } } else if (isValid(next_pos) && map[next_pos.x][next_pos.y] == BOX_DES) //小人前是箱子目的地 { changeMap(&next_pos, MAN); changeMap(&man, FLOOR); man = next_pos; hold = 1; } else if (isValid(next_pos) && map[next_pos.x][next_pos.y] == BOX) //小人前是箱子 { if (isValid(next_next_pos) && map[next_next_pos.x][next_next_pos.y] == BOX_DES) { changeMap(&next_next_pos, HIT); changeMap(&next_pos, MAN); changeMap(&man, FLOOR); man = next_pos; } else if (isValid(next_next_pos) && map[next_next_pos.x][next_next_pos.y] == FLOOR) //箱子前面是地板 { if (hold == 1) { changeMap(&next_next_pos, BOX); changeMap(&next_pos, MAN); changeMap(&man, BOX_DES); man = next_pos; hold = 0; } else { changeMap(&next_next_pos, BOX); changeMap(&next_pos, MAN); changeMap(&man, FLOOR); man = next_pos; } } } else if (isValid(next_next_pos) && map[next_pos.x][next_pos.y] == HIT) //小人前方箱子和目的地重合 { changeMap(&next_next_pos, BOX); changeMap(&next_pos, MAN); changeMap(&man, FLOOR); man = next_pos; hold = 1; } } bool login(userinfo& user) { int times = 0; //登录次数 int ret = false; do { cout << "请输入用户名:"; cin >> user.username; cout << "请输入登录密码:"; cin >> user.passwd; if (times > MAX_REIRY_TIMES) { cout << "输入用户名或密码错误过多!" << endl; return false; } ret = fetch_user_info(user); } while (ret == false); return ret; } void init_game_graph(IMAGE &bg_img) { initgraph(WEIGHT, HEIGHT); loadimage(&bg_img, L"blackground.bmp", WEIGHT, HEIGHT, true); putimage(0, 0, &bg_img); loadimage(&pic[WALL], L"wall_right.bmp", RATIO, RATIO, true); loadimage(&pic[FLOOR], L"floor.bmp", RATIO, RATIO, true); loadimage(&pic[BOX_DES], L"des.bmp", RATIO, RATIO, true); loadimage(&pic[MAN], L"man.bmp", RATIO, RATIO, true); loadimage(&pic[BOX], L"box.bmp", RATIO, RATIO, true); loadimage(&pic[HIT], L"box.bmp", RATIO, RATIO, true); } int main(void) { userinfo user; levelinfo level; bool ret = false; IMAGE bg_img; if (!login(user)) { cout << "登录失败" << endl; } else { cout << "登录成功!请开始你的表演" << endl; } //初始化游戏舞台 init_game_graph(bg_img); ::system("pause"); bool quit = false; do { hold = 0; //每次到新的一关人物肯定不在目的地 //根据用户所在的关卡id获取关卡数据 ret = fetch_level_info(level, user.level_id); if (!ret) { cout << "获取关卡数据失败,请重试!" << endl; ::system("pause"); exit(-1); } else { cout << "登录成功,用户id:" << user.id << ",您所在的关卡是:" << user.level_id << ",请开始您的表演!" << endl; } //把数据库中的地图数据转换到map中 ret = transform_map_dbtoarry(level, map); if (!ret) { cout << "转换地图失败,请重试!" << endl; ::system("pause"); exit(-1); } 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; } putimage(START_X + j * RATIO, START_Y + i * RATIO, &pic[map[i][j]]); } } do { if (_kbhit()) { char ch = _getch(); if (ch == KEY_UP) { gameCotrol(UP); } else if (ch == KEY_DOWN) { gameCotrol(DOWN); } else if (ch == KEY_LEFT) { gameCotrol(LEFT); } else if (ch == KEY_RIGHT) { gameCotrol(RIGHT); } else if (ch == KEY_QUIT) { quit = false; } if (isGameOver()) { if (level.next_level < 1) { gameOverScene(&bg_img); quit = true; break; } gameNextScene(&bg_img); if (update_user_level(user, level.next_level)) { user.level_id = level.next_level; putimage(0, 0, &bg_img); //显示图片 } break; } } Sleep(100); } while (!quit); } while (quit == false); ::system("pause"); closegraph(); return 0; }
database.cpp
#include <mysql.h> #include <stdio.h> #include "database.h" using namespace std; #define DB_NAME "box_man" #define DB_HOST "127.0.0.1" #define DB_POST 3306 #define DB_USER "root" #define DB_USER_PASSWD "rfly" //static bool connect_db(MYSQL& mysql); bool connect_db(MYSQL& mysql) { mysql_init(&mysql); mysql_options(&mysql,MYSQL_SET_CHARSET_NAME,"gbk"); if (mysql_real_connect(&mysql, DB_HOST, DB_USER, DB_USER_PASSWD, DB_NAME, DB_POST, 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_RES* res; //查询结果集 MYSQL_ROW row; //查询记录行 char a[256]; //用来保存字符串查询语句 int ret = false; //1.连接数据库 if (connect_db(mysql) == false) { cout << "数据库连接失败" << endl; return false; } snprintf(a, 256, "select id,level_id from users where username = '%s' and password = md5('%s');", user.username.c_str(), user.passwd.c_str()); ret = mysql_query(&mysql, a); //等于0,表示查询成功 if (ret) { printf("数据库查询出错,%s 错误原因:%s\n", a, mysql_error(&mysql)); mysql_close(&mysql); return false; } res = mysql_store_result(&mysql); row = mysql_fetch_row(res); if (row == NULL) { cout <<"查询到空数据,失败!" << endl; mysql_free_result(res); mysql_close(&mysql); return false; } user.id = atoi(row[0]); user.level_id = atoi(row[1]); cout << "用户名id:" << user.id << "\t" << "用户关卡:" << user.level_id; mysql_free_result(res); mysql_close(&mysql); return true; } bool fetch_level_info(levelinfo& level, int level_id) { MYSQL mysql; MYSQL_RES* res; //查询结果集 MYSQL_ROW row; //查询记录行 char sql[256]; bool ret = false; if (connect_db(mysql) == false) { cout << "数据库连接失败" << endl; return false; } 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); if (ret) { printf("数据库查询出错,%s 错误原因:%s\n", sql, mysql_error(&mysql)); mysql_close(&mysql); return false; } res = mysql_store_result(&mysql); row = mysql_fetch_row(res); if (row == NULL) { cout << "查询到空数据,失败!" << endl; mysql_free_result(res); mysql_close(&mysql); return false; } 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]); mysql_free_result(res); mysql_close(&mysql); return true; } bool transform_map_dbtoarry(levelinfo& level, int map[MAP_X][MAP_Y]) { //1.合法性检查 if (level.map_row > MAP_X || level.map_column > MAP_Y) { cout << "给定的地图太大!" << endl; return false; } if (level.map_data.length() < 1) { printf("地图数据有误,请重新设置!\n"); return false; } int start = 0, end = 0; int row = 0, column = 0; do { end = level.map_data.find('|', start); //find找到最后一行,没有'|'返回-1 if (end < 0) { end = level.map_data.length(); } if (start >= end) { break; } string line = level.map_data.substr(start, end - start); printf("get line:%s\n", line.c_str()); //去除逗号 char* next_token = NULL; char* item = strtok_s((char*)line.c_str(), ",", &next_token); column = 0; while (item && column < level.map_column) { printf("%s ", item); map[row][column] = atoi(item); column++; item = strtok_s(NULL, ",", &next_token); } if (column < level.map_column) { printf("地图解析错误!\n"); return false; } printf("\n"); row++; if (row >= level.map_row) { break; } start = end + 1; } while (1); if (row < level.map_row) { printf("地图行数少于设定,%d(need:%d),终止!\n", row, level.map_row); return false; } return true; } /****************************** * 功能:更新用户的关卡id * 输入:user -将下一关的id赋值给user * * next_level_id 下一关id *******************************/ bool update_user_level(userinfo& user, int next_level_id) { MYSQL mysql; MYSQL_RES* res; //查询结果集 MYSQL_ROW row; //记录结构体 char sql[256]; bool ret = false; //1.连接数据库 if (connect_db(mysql) == false) { return false; } //2.根据用户id更新下一关的level_id 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; } return true; }
database.h
#pragma once #include <string> #include "box_man.h" #include <iostream> using namespace std; typedef struct _userinfo { int id; string username; string passwd; int level_id; }userinfo; typedef struct _levelinfo { int id; //关卡的id string name; //关卡的名字 int map_row; //地图的总行数 int map_column; //地图的总列数 string map_data; //二维地图的数据 int next_level; //下一关卡的id }levelinfo;