第三个游戏俄罗斯方块出来了
看了三天,昨天写了一天有问题没弄出来,今天下午重写一下午完成,当然还有之前看的基础才能一气呵成,所有的代码和exe还有工程文件都在压缩包里,有兴趣看看把,这是连接地址L:http://pan.baidu.com/share/link?shareid=4198653437&uk=2988506976
程序代码:
/************************************* ** 程序名称:俄罗斯方块 ** ** 编译环境:vs2012 ** ** 编辑作者:往事随风<1034882113> ** ** 最后修改:2013-07-24 ** ** 项目类型:win32控制台程序 ** **************************************/ #include<graphics.h> #include<conio.h> // _kbhit() _getch() #include<time.h> /****************** 宏定义区 **********************/ #define GAME_ROW 20 // 游戏区行数 #define GAME_COL 10 // 游戏区列数 #define BLOCK_SIZE 20 // 每个游戏区单位像素大小 #define MENU_LEN 400 // 菜单界面长度 #define MENU_WIDE 440 // 菜单界面宽度 /****************** 数据类型定义区 ******************/ enum CMD { CMD_UP, // 上方向键 CMD_LEFT,CMD_RIGHT,CMD_DOWN, // 左右下方向将 CMD_SINK, // 沉底(空格) CMD_QUIT // 退出 }; enum DRAW { SHOW, // 显示 HIDE, // 隐藏 FIX // 固定 }; struct block { WORD dir[4]; // 16位短整形 存储四个变形 COLORREF color; }Block[7] = { {0x0F00, 0x4444, 0x0F00, 0x4444, RED}, // I {0x0660, 0x0660, 0x0660, 0x0660, BLUE}, // 口 {0x4460, 0x02E0, 0x0622, 0x0740, MAGENTA}, // L {0x2260, 0x0E20, 0x0644, 0x0470, YELLOW}, // 反L {0x0C60, 0x2640, 0x0C60, 0x2640, CYAN}, // Z {0x0360, 0x4620, 0x0360, 0x4620, GREEN}, // 反Z {0x4E00, 0x4C40, 0x0E40, 0x4640, BROWN}}; // T; struct blockinfo { byte id; // 确定基本形状 char x,y; // 在游戏区的坐标 byte dir:2; // 四个变形 }Cur_Block,Next_Block; byte Block_Map[GAME_ROW][GAME_COL]; /****************** 全局变量区 **********************/ static int Sc_length = BLOCK_SIZE*(GAME_COL+8)+10; // 屏幕总长 // 窗口边框占了10像素 static int Sc_height = BLOCK_SIZE*GAME_ROW+10; // 屏幕总高 IMAGE img_border[2]; // 屏幕中的边框 (0:左边框右边框 1:右边横边框) IMAGE img_bk; // 屏幕中的背景图片 IMAGE img_block[2]; // 方格图片 IMAGE img_nextbk; // 预览方块背景 IMAGE img_menu; IMAGE img_textbk; DWORD old_time; // int score = 0; // 分数 DWORD speed = 500; // 速度 /****************** 函数声明区 **********************/ void print_tips(); void gameOption(); void gameDeclare(void); void getMouse(POINT *point); void print_menu(void); void init_img(); void init_window(); void quit_game(); void game_over(); void new_game(); void new_block(); void draw_block(blockinfo _block,DRAW draw = SHOW); bool check_border(blockinfo _block); CMD get_cmd(); void DispatchCmd(CMD _cmd); void click_left(); void click_right(); void click_up(); void click_down(); void click_space(); void game_begin(); void game_score(int count); void print_score(int score); /****************** 函数定义区 **********************/ /* 打印分数 */ void print_score(int score) { char cscore[5] = "0"; char cspeed[4] = "0"; int i = 0; _itoa(score,cscore,10); _ltoa(speed,cspeed,10); // 显示分数 putimage(BLOCK_SIZE*(GAME_COL+1),BLOCK_SIZE*7,&img_textbk); settextcolor(MAGENTA); setbkmode(TRANSPARENT); // 设置文字背景透明 outtextxy(BLOCK_SIZE*(GAME_COL+2),BLOCK_SIZE*10,_T("分数:")); outtextxy(BLOCK_SIZE*(GAME_COL+2),BLOCK_SIZE*14,_T("速度:")); while (cscore[i]) outtextxy(BLOCK_SIZE*(GAME_COL+3)+8*i,BLOCK_SIZE*12,cscore[i++]); i = 0; while (cspeed[i]) outtextxy(BLOCK_SIZE*(GAME_COL+3)+8*i,BLOCK_SIZE*16,cspeed[i++]); } /* 计算游戏分数并设置游戏速度 */ void game_score(int count) { switch (count) { case 1: score += 10; break; case 2: score += 20; break; case 3: score += 40; break; case 4: score += 80; break; } if (score >= 1000) speed = 200; if (speed >= 5000) speed = 100; print_score(score); } /* 开始游戏 */ void game_begin() { CMD c; init_window(); while (true) { c = get_cmd(); DispatchCmd(c); if (c == CMD_QUIT) { HWND wnd = GetHWnd(); if (MessageBox(wnd, _T("您要退出游戏吗?"), _T("提醒"), MB_OKCANCEL | MB_ICONQUESTION) == IDOK) quit_game(); } } } /* 空格键沉底 */ void click_space() { int x =0,y = 0,i = 0; // 连续下移方块 draw_block(Cur_Block,HIDE); blockinfo tmp = Cur_Block; // tmp.y++; while (check_border(tmp)) { Cur_Block.y++; tmp.y++; } draw_block(Cur_Block,FIX); // 固定在游戏区 WORD b = Block[Cur_Block.id].dir[Cur_Block.dir]; // 确定形状 for (i = 0; i < 16; i++) { if (b & 0x8000) { x = Cur_Block.x + i%4; y = Cur_Block.y + i/4; if (y < 0) { game_over(); return; } else Block_Map[y][x] = 1; /*******************/ } b <<= 1; } // 判断是否可以消行并标记 int row[4] = {0}; bool bRow = false; for (y = Cur_Block.y; y <= max(Cur_Block.y + 3,GAME_ROW); y++) // 每一行 { i = 0; for (x = 0; x < GAME_COL; x++) // 每一列 if (Block_Map[y][x]) i++; if (i == GAME_COL) { bRow = true; row[y - Cur_Block.y] = 1; // 从上往下消行 } } // 取消标记 if (bRow) { Sleep(200); IMAGE img; // 截图 int count = 0; for (i = 0; i < 4; i++) { if (row[i]) { count++; // 判断消得行数 for (y = Cur_Block.y+i-1; y > 0; y--) for (x = 0; x < GAME_COL; x++) { Block_Map[y+1][x] = Block_Map[y][x]; // Block_Map[y][x] = 0; } getimage(&img,0,0,BLOCK_SIZE*GAME_COL,BLOCK_SIZE*(Cur_Block.y+i)); // putimage(0,BLOCK_SIZE,&img); } } game_score(count); } new_block(); } /* 上方向键 */ void click_up() { blockinfo tmp = Cur_Block; // int dx = 0; tmp.dir++; if (check_border(tmp)) { dx = 0; goto rota; } tmp.x = Cur_Block.x + 1; if (check_border(tmp)) { dx = 1; goto rota; } tmp.x = Cur_Block.x - 1; if (check_border(tmp)) { dx = -1; goto rota; } tmp.x = Cur_Block.x + 2; if (check_border(tmp)) { dx = 2; goto rota; } tmp.x = Cur_Block.x - 2; if (check_border(tmp)) { dx = -2; goto rota; } return; rota: draw_block(Cur_Block,HIDE); Cur_Block.dir++; Cur_Block.x += dx; draw_block(Cur_Block); } /* 下方向键 */ void click_down() { blockinfo tmp = Cur_Block; // tmp.y++; if (check_border(tmp)) { draw_block(Cur_Block,HIDE); Cur_Block.y++; draw_block(Cur_Block); } else click_space(); } /* 右方向键 */ void click_right() { blockinfo tmp = Cur_Block; // tmp.x++; if (check_border(tmp)) { draw_block(Cur_Block,HIDE); Cur_Block.x++; draw_block(Cur_Block); } } /* 左方向键 */ void click_left() { blockinfo tmp = Cur_Block; // tmp.x--; if (check_border(tmp)) { draw_block(Cur_Block,HIDE); Cur_Block.x--; draw_block(Cur_Block); } } // 分发控制命令 void DispatchCmd(CMD _cmd) { switch(_cmd) { case CMD_UP: click_up(); break; case CMD_LEFT: click_left(); break; case CMD_RIGHT: click_right(); break; case CMD_DOWN: click_down(); break; case CMD_SINK: click_space(); break; case CMD_QUIT: break; } } /* 获取控制命令 */ CMD get_cmd() { // 获取控制值 while(true) { // 如果超时,自动下落一格 DWORD new_time = GetTickCount(); if (new_time - old_time >= speed) { old_time = new_time; return CMD_DOWN; } // 如果有按键,返回按键对应的功能 if (_kbhit()) { switch(_getch()) { case 'w': case 'W': return CMD_UP; case 'a': case 'A': return CMD_LEFT; case 'd': case 'D': return CMD_RIGHT; case 's': case 'S': return CMD_DOWN; case 27: return CMD_QUIT; case ' ': return CMD_SINK; case 0: case 0xE0: /* 这个是标识一次按键将会返回两个16位整数的特殊值。一旦你扫描键盘按键, 返回值(比如_getch()的返回值)是0xE0的话,那么预示着后面还有一个整数等 待返回,你需要再调用一次_getch()获得那个返回值,前后两个返回值合并构成 一个32位的整数值,才是那个按键的完整代码。通常是按下控制键时会出现这个 现象,比如Ctrl+键、PgUp/PgDn等,都会这样。也就是说,键盘按键,有些键是 返回16位整数的,有些是返回32位整数的,后者的高位必定是0xE0。这跟汉字的 编码与ASCII编码有区别,是同一个道理。在C语言编程中,如果使用_getch()函 数接收键盘按键,那么就要分析其返回值是否0xE0,如果是,则必须再调用一 次_getch(),否则缓冲区中会残留数据,也影响程序的正常运作 */ switch(_getch()) { case 72: return CMD_UP; case 75: return CMD_LEFT; case 77: return CMD_RIGHT; case 80: return CMD_DOWN; } } } // 延时 (降低 CPU 占用率) Sleep(20); } } /* 判断越界 */ bool check_border(blockinfo _block) { WORD tmp = Block[_block.id].dir[_block.dir]; // 确定具体形状 int x = 0, y = 0; for (int i = 0; i < 16; i++) { if (tmp & 0x8000) { x = _block.x + i%4; y = _block.y + i/4; if (x < 0 || x >= GAME_COL || y >= GAME_ROW) return false; if (y >= 0 && Block_Map[y][x]) // return false; } tmp <<= 1; } return true; } /* 画出一个方块 */ void draw_block(blockinfo _block,DRAW draw) { WORD tmp = Block[_block.id].dir[_block.dir]; // 确定具体形状 int x = 0,y = 0; // 记录临时坐标 for (int i = 0; i < 16; i++) { if (tmp & 0x8000) // 此行都为0没有方块 { x = _block.x + i%4; y = _block.y + i/4; if (y >= 0) { if (draw == HIDE) // 擦除 putimage(x*BLOCK_SIZE,y*BLOCK_SIZE,&img_block[1]); else putimage(x*BLOCK_SIZE,y*BLOCK_SIZE,&img_block[0]); } } tmp <<= 1; } } /* 产生新的方块 */ void new_block() { // 产生当前屏幕中要出现的方块 Cur_Block.id = Next_Block.id; Cur_Block.dir = Next_Block.dir; Cur_Block.x = (GAME_COL-4)/2; Cur_Block.y = -3; // 产生新的预览方块 Next_Block.id = rand()%7; Next_Block.dir = rand()%4; // 下移方块直到显示局部 WORD tmp = Block[Cur_Block.id].dir[Cur_Block.dir]; while ((tmp & 0xF) == 0) { Cur_Block.y++; tmp >>= 4; } // 画出屏幕中的方块 draw_block(Cur_Block); // 画出新的预览方块 putimage((GAME_COL+1)*BLOCK_SIZE,0,&img_nextbk); draw_block(Next_Block); // 设置计时器判断方块下落 old_time = GetTickCount(); } /* 新的游戏 */ void new_game() { // 初始化游戏 putimage(0,0,&img_bk); // 清空游戏屏幕 ZeroMemory(Block_Map,GAME_ROW*GAME_COL); // 重置地图标志 // 产生新的预览方块 Next_Block.id = rand()%7; Next_Block.dir = rand()%4; Next_Block.x = GAME_COL+2; Next_Block.y = 1; // 画出新的预览方格 draw_block(Next_Block); // 产生新的方块 new_block(); } /* 游戏失败 */ void game_over() { HWND wnd = GetHWnd(); if (MessageBox(wnd, _T("游戏结束。\n您想重新来一局吗?"), _T("游戏结束"), MB_YESNO | MB_ICONQUESTION) == IDYES) new_game(); else quit_game(); } /* 退出游戏 */ void quit_game() { closegraph(); exit(0); } /* 初始化游戏窗口数据 */ void init_window() { initgraph(Sc_length,Sc_height); setbkcolor(DARKGRAY); cleardevice(); srand((unsigned)time(NULL)); putimage(BLOCK_SIZE,0,&img_bk); // 输出背景图片 putimage(0,0,&img_border[0]); // 输出最左边边界 putimage(BLOCK_SIZE*(GAME_COL+1),0,&img_border[0]); // 输出右边边界 putimage(BLOCK_SIZE*(GAME_COL+2),BLOCK_SIZE*6,&img_border[1]); // 输出右边横边框 setorigin(BLOCK_SIZE,0); // 设置坐标原点 print_score(score); new_game(); } /* c初始化图片资源 */ void init_img() { loadimage(&img_block[0],_T("./res/tetris0.bmp"),BLOCK_SIZE,BLOCK_SIZE,true); loadimage(&img_block[1],_T("./res/tetris1.jpg"),BLOCK_SIZE,BLOCK_SIZE,true); loadimage(&img_bk,_T("./res/bk.jpg"),BLOCK_SIZE*GAME_COL,BLOCK_SIZE*GAME_ROW,true); loadimage(&img_border[0],_T("./res/border.bmp"),BLOCK_SIZE,BLOCK_SIZE*GAME_ROW,true); loadimage(&img_border[1],_T("./res/dborder.bmp"),BLOCK_SIZE*6,BLOCK_SIZE,true); loadimage(&img_nextbk,_T("./res/nextbk.jpg"),BLOCK_SIZE*6,BLOCK_SIZE*6,true); loadimage(&img_menu,_T("./res/menu.jpg"),MENU_LEN,MENU_WIDE,true); loadimage(&img_textbk,_T("./res/textbk.jpg"),BLOCK_SIZE*6,BLOCK_SIZE*13,true); } /* 输出提示信息 */ void print_tips() { outtextxy(150,400,_T("按任意键返回")); _getch(); } /* 游戏版本信息 */ void gameOption() { int x = 140,y = 50; putimage(0,0,&img_menu); setcolor(BLUE); // 设置字体颜色 outtextxy(x,y,_T("俄罗斯方块")); outtextxy(x,y+80,_T("版本:1.0")); outtextxy(x-60,y+120,_T("由往事随风<1034882113>制作")); print_tips(); } /* 游戏说明 */ void gameDeclare(void) { int x = 140,y = 80; putimage(0,0,&img_menu); outtextxy(170,40,_T("游戏说明")); outtextxy(x,y,_T("↑:变换形状")); outtextxy(x,y+40,_T("↓:加速向下移动")); outtextxy(x,y+80,_T("←:向左移动")); outtextxy(x,y+120,_T("→:向右移动")); print_tips(); } /* 得到鼠标坐标 */ void getMouse(POINT *point) { HWND hwnd = GetHWnd(); // 获取绘图窗口句柄 MOUSEMSG msg; FlushMouseMsgBuffer(); while (true) // 等待鼠标点击 { msg = GetMouseMsg(); if (msg.uMsg ==WM_LBUTTONDOWN) { GetCursorPos(point); // 获取鼠标指针位置(屏幕坐标) ScreenToClient(hwnd,point); // 将鼠标指针位置转换为窗口坐标 break; } } } /* 输出游戏菜单 */ void print_menu(void) { int x = MENU_LEN / 3,y = MENU_WIDE / 5; initgraph(MENU_LEN,MENU_WIDE); putimage(0,0,&img_menu); setfillcolor(RED); fillrectangle(x,y,x * 2,y * 4); setbkmode(TRANSPARENT); // 设置文字背景透明 outtextxy(x+60,y + 40,_T("菜单")); // x为133,y为88 outtextxy(x+30,y+80,_T("开始新游戏")); // 一个字占的长宽分别为15 outtextxy(x+40,y+120,_T("游戏说明")); outtextxy(x+40,y+160,_T("关于游戏")); outtextxy(x+40,y+200,_T("退出游戏")); } /****************** 主函数区 **********************/ void main() { POINT point; // 存储坐标位置 init_img(); srand((unsigned)time(NULL)); // 产生随机种子 while (true) { print_menu(); getMouse(&point); if (point.x > 163 && point.x < 238) // 菜单范围 { if (point.y >168 && point.y < 182) // 开始游戏 { closegraph(); game_begin(); } else if (point.y > 208 && point.y < 222) // 游戏说明 gameDeclare(); else if (point.y >248 && point.y < 262) // 关于游戏 gameOption(); else if (point.y > 288 && point.y < 305) // 退出游戏 { HWND wnd = GetHWnd(); if (MessageBox(wnd, _T("您要退出游戏吗?"), _T("提醒"), MB_OKCANCEL | MB_ICONQUESTION) == IDOK) quit_game(); } } else ; /*********键盘操作*******/ } }
[ 本帖最后由 Fisher~ 于 2013-7-24 02:23 编辑 ]