第三个游戏俄罗斯方块出来了
看了三天,昨天写了一天有问题没弄出来,今天下午重写一下午完成,当然还有之前看的基础才能一气呵成,所有的代码和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 编辑 ]










我还是个菜鸟
膜拜大神