// ToyBricks.cpp : Defines the entry point for the application. // #include "stdafx.h" /*******************************************************/ /* 文 件 名: ToyBricks.c * 完成时间: 2001年9月 * 完成地点: 华东地质学院综合楼507机房 * 开发模式: SDK程序 * 开发环境: MS VC++ 6.0 * 作 者: 毛建忠 * 工作单位: 中国建设银行三水市(广东省)支行 * E - Mail: Mao_jzh@163.com */ /* 备注: * 这是一个简单的俄罗斯方块游戏程序,写程序时没有注释, * 这是我写程序时的一个坏习惯。 正在装载数据…… 毕业有半年了,也就半年没有写 * 过程序,周末闲着没事,捡起那些旧程序看看,顺便加上一些注 * 释。很怀念在学校写程序的那段日子,很怀念507。 */ /*******************************************************/ #include <windows.h> #include <time.h> #include <stdlib.h> #define CELL 15 // 【方格】的边长(pix) #define W 22 // 游戏区宽(22个【方格】边长) #define H 30 // 游戏区高(30个【方格】边长) #define MS_NEWBLOCK WM_USER+1 // 消息ID,产生新的【方块】 #define MS_DRAW WM_USER+2 LRESULT CALLBACK WndProc ( HWND, UINT, WPARAM, LPARAM);//窗口过程处理 int WINAPI WinMain ( HINSTANCE hInstance, //当前实例句柄 HINSTANCE hPrevInstance, //前一实例句柄 PSTR szCmdLine, //指向程序命令行参数的指针 int iCmdShow) //应用程序开始执行窗口时显示方式用int类型标志 { static char AppName[]="ToyBrick";//定义一个静态字符数组保存字符串"ToyBrick"(机应用程序名) HWND hwnd; //定义一个窗口句柄 MSG msg; //定义一消息结构体变量 WNDCLASSEX wndclass; //定义一窗口类结构变量,包含窗口类全部信息 int iScreenWide; //定义屏幕显示宽度 wndclass.cbSize = sizeof(wndclass);//窗口类对象大小 wndclass.style = CS_HREDRAW|CS_VREDRAW;//窗口类对象风格 wndclass.lpfnWndProc = WndProc;//窗口处理函数为WndProc wndclass.cbClsExtra = 0; //窗口类无扩展 wndclass.cbWndExtra = 0;//窗口类实例没有扩展 wndclass.hInstance = hInstance;//当前实例句柄 wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);//窗口最小化图标为默认图标 wndclass.hCursor = LoadCursor (NULL,IDC_ARROW);//窗口当前光标为箭头光标 wndclass.hbrBackground = (HBRUSH)GetStockObject (BLACK_BRUSH);//获得当前背景设置为黑色 wndclass.lpszMenuName = NULL;//窗体菜单名为空 wndclass.lpszClassName = AppName;//应用程序名 wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);//为应用程序加载图标 if(!RegisterClassEx (&wndclass)) return FALSE; //注册一个窗口类 // 获取显示器分辨率的X值iScreenWide,将程序窗口置于屏幕中央 iScreenWide=GetSystemMetrics (SM_CXFULLSCREEN); //创建窗口 hwnd =CreateWindow (AppName, "俄罗斯方块", WS_MINIMIZEBOX|WS_SYSMENU , iScreenWide/2-W*CELL/2, CELL, (W+1)*CELL-8, H*CELL, NULL, NULL, hInstance, NULL ); if(!hwnd) return FALSE; //没有获得窗口句柄,返回假 ShowWindow (hwnd,iCmdShow); //显示窗口 UpdateWindow (hwnd); //更新窗口 MessageBox(hwnd,"开始游戏","开始",MB_OK);//显示Message SendMessage(hwnd,MS_NEWBLOCK,0,0);//向当前窗口发送消息 SetTimer (hwnd, 1, 550,NULL);//设置下落时间 while (GetMessage (&msg, NULL, 0, 0))//进入消息循环 { TranslateMessage (&msg); DispatchMessage (&msg); } return msg.wParam; } // 函数DrawRact: 画【正方形】 // 参数: 设备环境句柄和【正方形】的四角坐标 void DrawRect (HDC hdc, int l, int t, int r, int b) { MoveToEx (hdc, l, t, NULL); LineTo (hdc, r, t); LineTo (hdc, r, b); LineTo (hdc, l, b); LineTo (hdc, l,t); } // 函数DrawCell: 画【方格】 // 参数: 设备环境句柄和【方格】的四角坐标 // 每个方格由内外两个【正方形】(DrawCell)画成,使其有立体感 void DrawCell (HDC hdc, int l, int t, int r, int b) { DrawRect (hdc,l+1, t+1, r-1, b-1); DrawRect (hdc,l+3, t+3, r-3, b-3); } // 函数DrawBlock: 画【方块】 // 参数: 设备环境句柄和【方块】中四个【方格】在游戏区域中的位置 // 每个【方块】由四个【方格】组成7种不同的形状 void DrawBlock (HDC hdc, int block[4][2]) { int i; HPEN hpen; hpen =CreatePen (PS_SOLID,1,RGB(255,255,255)); SelectObject (hdc,hpen); for(i=0; i<4; i++) DrawCell (hdc, (block[i][0]-1)*CELL, (block[i][1]-1)*CELL, //.... block[i][0]*CELL, block[i][1]*CELL); DeleteObject (hpen); } // 函数Cover: 清除原来位置的【方块】 // 参数: 设备环境句柄和待清除的【方块】 // 清除【方块】即在该【方块】的每个【方块】处画一个正方形的黑块 void Cover (HDC hdc, int org[4][2]) { int i; SelectObject (hdc, (HBRUSH)GetStockObject (BLACK_BRUSH)); for(i=0; i<4; i++) Rectangle ( hdc, (org[i][0]-1)*CELL, (org[i][1]-1)*CELL, //..... org[i][0]*CELL, org[i][1]*CELL); } // 窗口过程函数WndProc LRESULT CALLBACK WndProc ( HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam ) { int i,j,k,lines,r; static int top, sel, flag; static int cells[W+2][H]; // 控制游戏的【方格矩阵】 static int org[4][2], block[4][2]; // 【方块】 HDC hdc; HPEN hpen; PAINTSTRUCT ps; switch (iMsg) { case WM_CREATE: top=H-1; // 将第一列和最后一列【方格】置1,控制【方块】不超出游戏区域 for(i=0; i<H; i++) { cells[0][i]=1; cells[W+1][i]=1; } // 将最底下一行【方格】置1,控制【方块】不超出游戏区域 for(i=0; i<W+2; i++) cells[i][H-1]=1; // 其他【方格】置0,游戏方块只能在这里移动 for(i=1; i<=W; i++) for(j=0; j<H-1; j++) cells[i][j]=0; return 0; case MS_NEWBLOCK: flag=0; // flag表示【方块】旋转了几次 for(i=top; i<H-1; i++) { lines =0; // 循环语句检查是否有某一行全部被【方格】都填满 for(j=1; j<=W+1; j++) if(! cells[j][i]) { lines=1; break; } // 若该行被填满,则将上一行的填充状态复制到该行,依此类推 // 即从该行开始,所有的【方格】都下移一行 if(!lines) { for(j=1;j<W+1; j++) for(k=i; k>=top; k--) cells[j][k]=cells[j][k-1]; top++; InvalidateRect (hwnd, NULL, TRUE); } } // 产生随机数0~7,分别代表【方块】的7种形状 srand( (unsigned)time( NULL ) ); sel =rand()%7; //【方块】形状初始化 //【方块】的形状由其每个【方格】的位置决定 // 游戏区宽W=22,block[?][0]=9/10/11/12,block[?][1]=0/1/2 // 这样【方块】初始位置在游戏区的最顶部的中央 switch(sel) { case 0: // ▓▓ // ▓▓ org[0][0]=block[0][0] =10; org[0][1]=block[0][1] =0; org[1][0]=block[1][0] =11; org[1][1]=block[1][1] =0; org[2][0]=block[2][0] =10; org[2][1]=block[2][1] =1; org[3][0]=block[3][0] =11; org[3][1]=block[3][1] =1; break; case 1: // ▓▓▓▓ org[0][0]=block[0][0] =9; org[0][1]=block[0][1] =0; org[1][0]=block[1][0] =10; org[1][1]=block[1][1] =0; org[2][0]=block[2][0] =11; org[2][1]=block[2][1] =0; org[3][0]=block[3][0] =12; org[3][1]=block[3][1] =0; break; case 2: //▓ //▓▓ // ▓ org[0][0]=block[0][0] =10; org[0][1]=block[0][1] =0; org[1][0]=block[1][0] =10; org[1][1]=block[1][1] =1; org[2][0]=block[2][0] =11; org[2][1]=block[2][1] =1; org[3][0]=block[3][0] =11; org[3][1]=block[3][1] =2; break; case 3: // ▓ //▓▓ //▓ org[0][0]=block[0][0] =11; org[0][1]=block[0][1] =0; org[1][0]=block[1][0] =11; org[1][1]=block[1][1] =1; org[2][0]=block[2][0] =10; org[2][1]=block[2][1] =1; org[3][0]=block[3][0] =10; org[3][1]=block[3][1] =2; break; case 4: //▓ //▓ //▓▓ org[0][0]=block[0][0] =10; org[0][1]=block[0][1] =0; org[1][0]=block[1][0] =10; org[1][1]=block[1][1] =1; org[2][0]=block[2][0] =10; org[2][1]=block[2][1] =2; org[3][0]=block[3][0] =11; org[3][1]=block[3][1] =2; break; case 5: // ▓ // ▓ //▓▓ org[0][0]=block[0][0] =10; org[0][1]=block[0][1] =0; org[1][0]=block[1][0] =10; org[1][1]=block[1][1] =1; org[2][0]=block[2][0] =10; org[2][1]=block[2][1] =2; org[3][0]=block[3][0] =9; org[3][1]=block[3][1] =2; break; case 6: // ▓ //▓▓▓ org[0][0]=block[0][0] =10; org[0][1]=block[0][1] =0; org[1][0]=block[1][0] =9; org[1][1]=block[1][1] =1; org[2][0]=block[2][0] =10; org[2][1]=block[2][1] =1; org[3][0]=block[3][0] =11; org[3][1]=block[3][1] =1; break; default: SendMessage (hwnd, MS_NEWBLOCK, 0, 0); break; } return 0; case WM_TIMER: // 每个时间节拍【方块】自动下移一行 for(i=0; i<4; i++) block[i][1]++; // 检查【方块】下移是否被档住,即判断下移后新位置是否有【方格】 for(i=0; i<4; i++) if(cells[ block[i][0] ][ block[i][1] ]) { for(i=0; i<4; i++) cells[ org[i][0] ][ org[i][1] ]=1; if(top>org[0][1]-2) top=org[0][1]-2; if (top<1) { KillTimer (hwnd, 1); MessageBox (hwnd, "游戏结束,即将退出 !", "退出", MB_OK); PostQuitMessage (0); } SendMessage (hwnd, MS_NEWBLOCK, 0, 0); return 0; } SendMessage (hwnd, MS_DRAW, 0, 0); return 0; // 响应键盘控制 case WM_KEYDOWN: r=0; switch((int)wParam) { case VK_LEFT: for(i=0; i<4; i++) block[i][0]--; break; case VK_RIGHT: for(i=0; i<4; i++) block[i][0]++; break; case VK_DOWN: for(i=0; i<4; i++) block[i][1]++; break; // 按[向上键],【方块】顺时针旋转 //【方块】的旋转不是真正的旋转,而是根据不同的【方块】形状和 // 该【方块】旋转过的次数来移动其中的一个或几个【方格】,从而 // 达到旋转的效果 这样做很复杂,算法不够理想,但是能够保持【方 // 块】旋转时的重心比较稳定。 case VK_UP: r=1; flag++; //【方块】旋转加1 switch(sel) // sel代表当前【方块】的形状 { case 0: break; case 1: flag =flag%2; for(i=0; i<4; i++) { block[i][(flag+1)%2] =org[2][(flag+1)%2]; block[i][flag] =org[2][flag]-2+i; } break; case 2: flag =flag%2; if(flag) { block[0][1] +=2; block[3][0] -=2; } else { block[0][1] -=2; block[3][0] +=2; } break; case 3: flag =flag%2; if(flag) { block[0][1] +=2; block[3][0] +=2; } else { block[0][1] -=2; block[3][0] -=2; } break; case 4: flag=flag%4; switch(flag) { case 0: block[2][0] +=2; block[3][0] +=2; block[2][1] +=1; block[3][1] +=1; break; case 1: block[2][0] +=1; block[3][0] +=1; block[2][1] -=2; block[3][1] -=2; break; case 2: block[2][0] -=2; block[3][0] -=2; block[2][1] -=1; block[3][1] -=1; break; case 3: block[2][0] -=1; block[3][0] -=1; block[2][1] +=2; block[3][1] +=2; break; } break; case 5: flag=flag%4; switch(flag) { case 0: block[2][0] +=1; block[3][0] +=1; block[2][1] +=2; block[3][1] +=2; break; case 1: block[2][0] +=2; block[3][0] +=2; block[2][1] -=1; block[3][1] -=1; break; case 2: block[2][0] -=1; block[3][0] -=1; block[2][1] -=2; block[3][1] -=2; break; case 3: block[2][0] -=2; block[3][0] -=2; block[2][1] +=1; block[3][1] +=1; break; } break; case 6: flag =flag%4; switch(flag) { case 0: block[0][0]++; block[0][1]--; block[1][0]--; block[1][1]--; block[3][0]++; block[3][1]++; break; case 1: block[1][0]++; block[1][1]++; break; case 2: block[0][0]--; block[0][1]++; break; case 3: block[3][0]--; block[3][1]--; break; } break; } break; } // 判断【方块】旋转后新位置是否有【方格】,若有,则旋转取消 for(i=0; i<4; i++) if(cells[ block[i][0] ][ block[i][1] ]) { if(r) flag +=3; for(i=0; i<4; i++) for(j=0; j<2; j++) block[i][j]=org[i][j]; return 0; } SendMessage(hwnd, MS_DRAW, 0, 0);; return 0; // 清除当前【方块】,并在新的位置重新绘制【方块】 case MS_DRAW: hdc =GetDC (hwnd); Cover (hdc, org); for(i=0; i<4; i++) for(j=0; j<2; j++) org[i][j]=block[i][j]; DrawBlock (hdc,block); ReleaseDC (hwnd, hdc); return 0; // 按照【方格矩阵】重绘游戏区域的【方格】 case WM_PAINT: hdc =BeginPaint (hwnd, &ps); hpen =CreatePen (PS_SOLID,1,RGB(255,255,255)); SelectObject (hdc,hpen); for (i=top; i<H-1; i++) for(j=1; j<=W; j++) if( cells[j][i] ) DrawCell (hdc, (j-1)*CELL, (i-1)*CELL, j*CELL, i*CELL); DeleteObject (hpen); EndPaint (hwnd, &ps); return 0; case WM_DESTROY: KillTimer (hwnd, 1); PostQuitMessage (0); return 0; } return DefWindowProc (hwnd, iMsg, wParam, lParam); } /************************************************************/ /* 结束 * 注释时间: 2003/01/04 SAT * 于 广东省三水市建行宿舍 */ /************************************************************/ ------------------------------------------------------------------------------------- // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) #define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #include <windows.h> // TODO: reference additional headers your program requires here //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the divvious line. #endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) ---------------------------------------------------------------------------------------- // stdafx.cpp : source file that includes just the standard includes // ToyBricks.pch will be the div-compiled header // stdafx.obj will contain the div-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H // and not in this file --------------------------------------------------------------------------------