首页 > 代码库 > 一个c语言的俄罗斯方块 参考
一个c语言的俄罗斯方块 参考
本文来自开源中国 http://www.oschina.net/code/snippet_188854_13088
#include <windows.h> #include <stdio.h> #include <time.h> #define CELL 20 #define ROWS 25 #define COLS 15 //升级所需分数值 #define SCORE_LEVEL_INC 80 #define ID_TIMER 1 /////////////////全局变量///////////////////////////// HWND hwnd; //保存窗口句柄 int score=0; //分数 int level=0; //级数 int interval_unit=25; //随级数递增的时间间隔增量 int interval_base=300; //时间间隔基量 int old_interval; //保存当前的时间间隔,用于加速操作 int cur_left,cur_top; //记录方块当前的位置 int width_block,height_block; //方块的宽带和高度 bool isPause=false; //暂停标识 UINT timer_id=0; //保存计时器ID static byte *block=NULL; //方块,方块为随机大小,采用动态分配内存方式,所以这里是指针变量 byte g_panel[ROWS][COLS]={0}; //////////////////////////////////////////////////// LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); void DrawPanel(HDC hdc); //绘制表格 void RefreshPanel(HDC hdc); //刷新面板 void DoDownShift(HDC hdc); //下移 void DoLeftShift(HDC hdc); //左移 void DoRightShift(HDC hdc); //右移 void DoAccelerate(HDC hdc); //加速 void DoRedirection(HDC hdc); //改变方向 void ClearRow(HDC hdc); //消行 bool ExportBlock(); //输出方块, //该函数会直接修改全局变量block,width_block,height_block, //cur_left和cur_top bool IsTouchBottom(HDC hdc); //判断是否到达底部 int main(){ HINSTANCE hInstance=GetModuleHandle(NULL); TCHAR szAppName[]=TEXT("teris"); MSG msg; WNDCLASS wc; wc.style=CS_HREDRAW|CS_VREDRAW; wc.lpfnWndProc=WndProc; wc.cbClsExtra=0; wc.cbWndExtra=0; wc.hInstance=hInstance; wc.hIcon=LoadIcon(NULL,IDI_APPLICATION); wc.hCursor=LoadCursor(NULL,IDC_ARROW); wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName=NULL; wc.lpszClassName=szAppName; if(!RegisterClass(&wc)){ printf("RegisterClass occur errors!"); return 0; } hwnd=CreateWindow(szAppName,TEXT("Teris Demo"), WS_OVERLAPPEDWINDOW, 0,0,0,0, NULL, NULL, hInstance, NULL); ShowWindow(hwnd,SW_SHOW); UpdateWindow(hwnd); while(GetMessage(&msg,NULL,0,0)){ TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } void DrawPanel(HDC hdc){ //绘制游戏面板 int x,y; RECT rect; for(y=0;y<ROWS;y++){ for(x=0;x<COLS;x++){ //计算方块的边框范围 rect.top=y*CELL+1; rect.bottom=(y+1)*CELL-1; rect.left=x*CELL+1; rect.right=(x+1)*CELL-1; FrameRect(hdc,&rect,(HBRUSH)GetStockObject(BLACK_BRUSH)); } } } void DoDownShift(HDC hdc){ //下移 if(NULL==block)return; //判断是否到达底部 if(IsTouchBottom(hdc)){ //到底部 //消行处理 ClearRow(hdc); ExportBlock(); //输出下一个方块 } cur_top++; RefreshPanel(hdc); } void DoLeftShift(HDC hdc){ //左移 int x,y; if(NULL==block)return; if(0==cur_left)return; if(cur_top<0)return; //方块没有完整显示前,不能左移 for(y=0;y<height_block;y++){ for(x=0;x<width_block;x++){ //从左边开始扫描,获取该行最左边的实心方格块 if(*(block+y*width_block+x)){ //判断当前方格在面板上面左边一个方格是否为实心,是就代表不能再左移 if(g_panel[cur_top+y][cur_left+x-1]) return; break; //只判断最左边的一个实心方格,之后直接扫描下一行 } } } cur_left--; RefreshPanel(hdc); } void DoRightShift(HDC hdc){ //右移 int x,y; if(NULL==block)return; if(COLS-width_block==cur_left)return; if(cur_top<0)return; //方块完整显示前不能右移 for(y=0;y<height_block;y++){ for(x=width_block-1;x>=0;x--){ //从右边开始扫描,获取该行最右边的实心方格块 if(*(block+y*width_block+x)){ //判断当前方格在面板上右边一个方格是否为实心,是就代表不能再右移 if(g_panel[cur_top+y][cur_left+x+1])return; break; //只判断最右边的一个实心方格 } } } cur_left++; RefreshPanel(hdc); } void DoRedirection(HDC hdc){ //改变方向 int i,j; byte * temp=NULL; if(NULL==block)return; if(cur_top<0)return; //方块完整显示前不能转向 temp=(byte *)malloc(sizeof(byte)*width_block*height_block); for(i=0;i<width_block;i++){ for(j=0;j<height_block;j++){ //temp[i][j]=block[height_block-j-1][i]; *(temp+i*height_block+j)=*(block+(height_block-j-1)*width_block+i); } } //给方块重新定位 int incHeight=width_block-height_block; int incWidth=height_block-width_block; int temp_cur_top=cur_top-incHeight/2; int temp_cur_left=cur_left-incWidth/2; //system("cls"); //printf("temp_top=%d, temp_left=%d",temp_cur_top,temp_cur_left); //判断当前空间是否足够让方块改变方向 int max_len=max(width_block,height_block); //防止下标访问越界 if(temp_cur_top+max_len-1>=ROWS||temp_cur_left<0||temp_cur_left+max_len-1>=COLS){ free(temp); //退出前必须先释放内存 return; } for(i=0;i<max_len;i++){ for(j=0;j<max_len;j++){ //转向所需的空间内有已被占用的实心方格存在,即转向失败 if(g_panel[temp_cur_top+i][temp_cur_left+j]){ free(temp); //退出前必须先释放内存 return; } } } //把临时变量的值赋给block,只能赋值,而不能赋指针值 for(i=0;i<height_block;i++){ for(j=0;j<width_block;j++){ //block[i][j]=temp[i][j]; *(block+i*width_block+j)=*(temp+i*width_block+j); } } //全局变量重新被赋值 cur_top=temp_cur_top; cur_left=temp_cur_left; //交换 i=width_block; width_block=height_block; height_block=i; free(temp); //释放为临时变量分配的内存 RefreshPanel(hdc); } void DoAccelerate(HDC hdc){ //加速 if(NULL==block)return; if(IsTouchBottom(hdc)){ //消行处理 ClearRow(hdc); ExportBlock(); } cur_top++; RefreshPanel(hdc); } bool IsTouchBottom(HDC hdc){ int x,y; int i,j; if(NULL==block) return false; if(ROWS==cur_top+height_block){ //固定方块 for(i=0;i<height_block;i++){ for(j=0;j<width_block;j++){ if(*(block+i*width_block+j)) g_panel[cur_top+i][cur_left+j]=1; } } return true; } for(y=height_block-1;y>=0;y--){ //从底行开始扫描 //判断第一个实心方块在面板上邻接的下方方格是否为实心,是就代表已经到达底部 for(x=0;x<width_block;x++){ if(*(block+y*width_block+x)){ if(cur_top+y+1<0)return false; if(g_panel[cur_top+y+1][cur_left+x]){ //判断是否gameover if(cur_top<=0){ if(timer_id){ KillTimer(hwnd,ID_TIMER); timer_id=0; } MessageBox(hwnd,TEXT("游戏结束"),TEXT("MSG"),MB_OK|MB_ICONEXCLAMATION); SendMessage(hwnd,WM_CLOSE,0,0); } // //固定方块 for(i=0;i<height_block;i++){ for(j=0;j<width_block;j++){ if(*(block+i*width_block+j)) g_panel[cur_top+i][cur_left+j]=1; } } return true; } } } } return false; } void ClearRow(HDC hdc){ //消行 int i,j,k; int count=0; //消行次数 bool isFilled; //消行处理 for(i=ROWS-1;i>=0;i--){ isFilled=true; for(j=0;j<COLS;j++){ if(!g_panel[i][j]){ isFilled=false; break; } } if(isFilled){ for(j=0;j<COLS;j++){ g_panel[i][j]=0; } //所有方块往下移 for(k=i-1;k>=0;k--){ for(j=0;j<COLS;j++){ g_panel[k+1][j]=g_panel[k][j]; } } i=i+1; count++; } } //最高级别为9级,所以分数极限为(9+1)*SCORE_LEVEL_INC-1 if(score>=10*SCORE_LEVEL_INC-1)return; //加分规则:消除行数,1行加10分,2行加15分,3行加20分,4行加30分 switch(count){ case 1: score+=10; break; case 2: score+=15; break; case 3: score+=20; break; case 4: score+=30; break; } int temp_level=score/SCORE_LEVEL_INC; if(temp_level>level){ level=temp_level; //撤销当前计时器,然后重设 if(timer_id)KillTimer(hwnd,ID_TIMER); timer_id=SetTimer(hwnd,ID_TIMER,interval_base-level*interval_unit,NULL); } system("cls"); printf("score: %d, level: %d ",score,level); } void RefreshPanel(HDC hdc){ //刷新面板 int x,y; RECT rect; HBRUSH h_bSolid=(HBRUSH)GetStockObject(GRAY_BRUSH), h_bEmpty=(HBRUSH)GetStockObject(WHITE_BRUSH); if(NULL==block)return; //先刷屏 for(y=0;y<ROWS;y++){ for(x=0;x<COLS;x++){ //为避免刷掉方块的边框,rect范围必须比边框范围小1 rect.top=y*CELL+2; rect.bottom=(y+1)*CELL-2; rect.left=x*CELL+2; rect.right=(x+1)*CELL-2; if(g_panel[y][x]) FillRect(hdc,&rect,h_bSolid); else FillRect(hdc,&rect,h_bEmpty); } } //再定位方块 for(y=0;y<height_block;y++){ for(x=0;x<width_block;x++){ if(*(block+y*width_block+x)){ //实心 rect.top=(y+cur_top)*CELL+2; rect.bottom=(y+cur_top+1)*CELL-2; rect.left=(x+cur_left)*CELL+2; rect.right=(x+cur_left+1)*CELL-2; FillRect(hdc,&rect,h_bSolid); } } } } bool ExportBlock(){ //输出方块 int sel; if(block){ free(block); //释放之前分配的内存 block=NULL; } sel=rand()%7; switch(sel){ case 0: //水平条 width_block=4; height_block=1; block=(byte *)malloc(sizeof(byte)*width_block*height_block); *(block+0)=1; //可以理解为*(block+0*width_block+0)=1,即第一行的第一个方格,下面同理 *(block+1)=1; //*(block+0*width_block+1)=1 *(block+2)=1; //*(block+0*width_block+2)=1 *(block+3)=1; //*(block+0*width_block+3)=1 cur_top=0-height_block; cur_left=(COLS-width_block)/2; break; case 1: //三角 width_block=3; height_block=2; block=(byte *)malloc(sizeof(byte)*width_block*height_block); *(block+0)=0; //可以理解为*(block+0*width_block+0)=0,即第一行的第一个方格,下面同理 *(block+1)=1; //*(block+0*width_block+1)=1 *(block+2)=0; //*(block+0*width_block+2)=0 *(block+3)=1; //*(block+1*width_block+0)=1,第二行开始 *(block+4)=1; //*(block+1*width_block+1)=1 *(block+5)=1; //*(block+1*width_block+2)=1 cur_top=0-height_block; cur_left=(COLS-width_block)/2; break; case 2: //左横折 width_block=3; height_block=2; block=(byte *)malloc(sizeof(byte)*width_block*height_block); *(block+0)=1; //可以理解为*(block+0*width_block+0)=1,下面同理 *(block+1)=0; //*(block+0*width_block+1)=0 *(block+2)=0; //*(block+0*width_block+2)=0 *(block+3)=1; //*(block+1*width_block+0)=1 *(block+4)=1; //*(block+1*width_block+1)=1 *(block+5)=1; //*(block+1*width_block+2)=1 cur_top=0-height_block; cur_left=(COLS-width_block)/2; break; case 3: //右横折 width_block=3; height_block=2; block=(byte *)malloc(sizeof(byte)*width_block*height_block); *(block+0)=0; //可以理解为*(block+0*width_block+0)=0,下面同理 *(block+1)=0; //*(block+0*width_block+1)=0 *(block+2)=1; //*(block+0*width_block+2)=1 *(block+3)=1; //*(block+1*width_block+0)=1 *(block+4)=1; //*(block+1*width_block+1)=1 *(block+5)=1; //*(block+1*width_block+2)=1 cur_top=0-height_block; cur_left=(COLS-width_block)/2; break; case 4: //左闪电 width_block=3; height_block=2; block=(byte *)malloc(sizeof(byte)*width_block*height_block); *(block+0)=1; //可以理解为*(block+0*width_block+0)=1,下面同理 *(block+1)=1; //*(block+0*width_block+1)=1 *(block+2)=0; //*(block+0*width_block+2)=0 *(block+3)=0; //*(block+1*width_block+0)=0 *(block+4)=1; //*(block+1*width_block+1)=1 *(block+5)=1; //*(block+1*width_block+2)=1 cur_top=0-height_block; cur_left=(COLS-width_block)/2; break; case 5: //右闪电 width_block=3; height_block=2; block=(byte *)malloc(sizeof(byte)*width_block*height_block); *(block+0)=0; //可以理解为*(block+0*width_block+0)=0,下面同理 *(block+1)=1; //*(block+0*width_block+1)=1 *(block+2)=1; //*(block+0*width_block+2)=1 *(block+3)=1; //*(block+1*width_block+0)=1 *(block+4)=1; //*(block+1*width_block+1)=1 *(block+5)=0; //*(block+1*width_block+2)=0 cur_top=0-height_block; cur_left=(COLS-width_block)/2; break; case 6: //石头 width_block=2; height_block=2; block=(byte *)malloc(sizeof(byte)*width_block*height_block); *(block+0)=1; //可以理解为*(block+0*width_block+0)=1,下面同理 *(block+1)=1; //*(block+0*width_block+1)=1 *(block+2)=1; //*(block+1*width_block+0)=1 *(block+3)=1; //*(block+1*width_block+1)=1 cur_top=0-height_block; cur_left=(COLS-width_block)/2; break; } return block!=NULL; } LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){ HDC hdc; PAINTSTRUCT ps; //TCHAR szBuffer[1024]; switch(message){ case WM_CREATE: MoveWindow(hwnd,400,10,CELL*COLS+8,CELL*ROWS+32,FALSE); //补齐宽度和高度 srand(time(NULL)); ExportBlock(); timer_id=SetTimer(hwnd,ID_TIMER,interval_base-level*interval_unit,NULL); return 0; case WM_TIMER: hdc=GetDC(hwnd); DoDownShift(hdc); ReleaseDC(hwnd,hdc); return 0; case WM_KEYDOWN: hdc=GetDC(hwnd); switch(wParam){ case VK_LEFT: //左移 if(!isPause)DoLeftShift(hdc); break; case VK_RIGHT: //右移 if(!isPause)DoRightShift(hdc); break; case VK_UP: //转向 if(!isPause)DoRedirection(hdc); break; case VK_DOWN: //加速 if(!isPause)DoAccelerate(hdc); break; case VK_SPACE: //暂停 isPause=!isPause; if(isPause){ if(timer_id)KillTimer(hwnd,ID_TIMER); timer_id=0; }else{ timer_id=SetTimer(hwnd,ID_TIMER,interval_base-level*interval_unit,FALSE); } break; } ReleaseDC(hwnd,hdc); return 0; case WM_PAINT: hdc=BeginPaint(hwnd,&ps); DrawPanel(hdc); //绘制面板 RefreshPanel(hdc); //刷新 EndPaint(hwnd,&ps); return 0; case WM_DESTROY: if(block)free(block); if(timer_id)KillTimer(hwnd,ID_TIMER); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd,message,wParam,lParam); }
可以扩展的地方很多,例如暂停,分数,等级,提示下一个图形等。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。