.h部分
#include#include"resource.h" #include #define WINDOW_WIDTH 520 #define WINDOW_HEIGHT 700 //游戏区的行列 #define COL 10 #define ROW 22 //俄罗斯方块区域行列 #define COL_TETRIS 4 #define ROW_TETRIS 2 //定时器编号 #define TIMEDOWN 1 int m_arr[ROW][COL] = { 0 }; int m_arrTetris[ROW_TETRIS][COL_TETRIS] = { 0 }; int m_arrPreView[ROW_TETRIS][COL_TETRIS] = { 0 }; int m_Score = 0;//分数 //类型 int m_TetrisType = -1; int m_PreViewType = -1; //中心点 POINT m_TetrisPoint = { -1, -1 }; POINT m_PreViewPoint = { -1, -1 }; //判断定时器是否开启 BOOL BeginTimer = FALSE; //窗口处理函数 LRESULT CALLBACK WindProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); //绘图 void OnPaint(HDC hDC); //绘制俄罗斯方块 void DrawBlock(HDC hDC); //响应WM_CREATE消息 void OnCreate(); //创建预览俄罗斯方块 void CreatePreTetris(); //把俄罗斯方块复制到网格中 void CopyTetris(); //响应回车键 void OnReturn(HWND hWnd); //响应定时器 void OnTimer(HWND hWnd); //下落函数 void tetrisDown(); //判断是不是到了最底端 BOOL reachBottom(); //判断是不是在其他方块上 BOOL reachOtherTetris(); //停下来就改变状态1变2 void changeState(); //判断游戏是否结束 BOOL judgeOver(); //消除行 void removeRow(); //向左移 void leftShift(); //向右移 void rightShift(); //下键 void OnDown(HWND hWnd); //上键 void OnUp(); //长条变形处理 void StripChange(); //普通变形 void NormalChange(); //清除方格 void clearArr();
cpp部分
#include"tetris .h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int CmdShow)
{
//设计窗口类
wchar_t szClassName[50] = L"tetris";
wchar_t szWindowTitle[50] = L"俄罗斯方块";
WNDCLASS wc = { 0 };
wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
wc.hCursor = LoadCursor(NULL, IDC_CROSS);
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LOGO));
wc.hInstance = hInstance;
wc.lpfnWndProc = WindProc;//窗口处理函数
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = szClassName;
//注册窗口类
RegisterClass(&wc);
//创建窗口
HWND hWnd = CreateWindow(szClassName, szWindowTitle, WS_OVERLAPPEDWINDOW, 200, 100, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
//显示更新窗口
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG msg;
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
LRESULT CALLBACK WindProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hDC;
switch (uMsg)
{
case WM_CREATE:
OnCreate();
break;
case WM_KEYDOWN:
switch (wParam)
{
case VK_SPACE://空格
break;
case VK_RETURN://回车
if (FALSE == BeginTimer)
{
OnReturn(hWnd);
}
break;
case VK_UP://上
OnUp();
break;
case VK_DOWN://下
OnDown(hWnd);
break;
case VK_RIGHT://右
rightShift();
break;
case VK_LEFT://左
leftShift();
break;
}
break;
case WM_TIMER://响应定时器
{
int TimerID = (int)wParam;
if (TimerID == TIMEDOWN)
{
OnTimer(hWnd);
}
break;
}
case WM_PAINT:
{
hDC = BeginPaint(hWnd,&ps);
//所有绘图都在这里进行
OnPaint(hDC);
EndPaint(hWnd, &ps);
break;
}
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
void OnPaint(HDC hDC)
{
//双环冲绘图
//创建内存DC
HDC hMemDC = CreateCompatibleDC(hDC);
//创建一个兼容位图
HBITMAP hBackBmp = CreateCompatibleBitmap(hDC, WINDOW_WIDTH, WINDOW_HEIGHT);
SelectObject(hMemDC, hBackBmp);
//绘制
DrawBlock(hMemDC);
//贴图
BitBlt(hDC, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, hMemDC, 0, 0, SRCCOPY);
}
void DrawBlock(HDC hDC)
{
//绘制框的总大小
Rectangle(hDC, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
//绘制游戏区域
Rectangle(hDC, 0, 60, 300, 660);
//创建正在下落的画刷
HBRUSH hOneBrush = CreateSolidBrush(RGB(255, 255, 0));
HBRUSH hTowBrush = CreateSolidBrush(RGB(255, 0, 0));
//绘制俄罗斯方块
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (m_arr[i][j] == 1 && i>1)
{
SelectObject(hDC, hOneBrush);
Rectangle(hDC, j * 30, i * 30, (j + 1) * 30 - 1, (i + 1) * 30 - 1);
}
else if (m_arr[i][j] == 2 && i>1)
{
SelectObject(hDC, hTowBrush);
Rectangle(hDC, j * 30, i * 30, (j + 1) * 30 - 1, (i + 1) * 30 - 1);
}
}
}
//绘制预览图
HBRUSH hPreBrush = CreateSolidBrush(RGB(255, 0, 255));
SelectObject(hDC, hPreBrush);
for (int i = 0; i < ROW_TETRIS; i++)
{
for (int j = 0; j < COL_TETRIS; j++)
{
if (m_arrPreView[i][j] == 1)
{
Rectangle(hDC, j * 30 + 360, i * 30 + 50, (j + 1) * 30 + 360 - 1, (i + 1) * 30 + 50 - 1);
}
}
}
//分数
wchar_t szScore[20] = { 0 };
wsprintf(szScore, L"分数:%d", m_Score);
//设置字体
HFONT hFont = CreateFont(18, 0, 0, 0, FW_DONTCARE,
FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("楷体"));
SelectObject(hDC, hFont);
TextOut(hDC, 330, 140, szScore,wcslen(szScore));
//说明信息
TextOut(hDC, 330, 180, L"游戏说明", wcslen(L"游戏说明"));
TextOut(hDC, 330, 220, L"回车键:开始游戏", wcslen(L"回车键:开始游戏"));
TextOut(hDC, 330, 260, L"空格键:暂停/继续", wcslen(L"空格键:暂停/继续"));
TextOut(hDC, 330, 300, L"左键:向左移", wcslen(L"左键:向左移"));
TextOut(hDC, 330, 340, L"右键:向右移", wcslen(L"右键:向右移"));
TextOut(hDC, 330, 380, L"下键:加速下落", wcslen(L"下键:加速下落"));
TextOut(hDC, 330, 420, L"上键:变形", wcslen(L"上键:变形"));
}
//响应WM_CREATE消息
void OnCreate()
{
CreatePreTetris();
}
//创建俄罗斯方块预览
void CreatePreTetris()
{
srand((unsigned int)time(NULL));//随机数种子
//创建俄罗斯方块
int index = rand() % 7;
//int index = 1;
switch (index)
{
case 0:
m_arrPreView[0][0] = 1;m_arrPreView[0][1] = 1;m_arrPreView[0][2] = 1;m_arrPreView[0][3] = 1;
m_arrPreView[1][0] = 0;m_arrPreView[1][1] = 0;m_arrPreView[1][2] = 0;m_arrPreView[1][3] = 0;
m_PreViewPoint.x = 5;
m_PreViewPoint.y = 0;
break;
case 1:
m_arrPreView[0][0] = 1; m_arrPreView[0][1] = 0; m_arrPreView[0][2] = 0; m_arrPreView[0][3] = 0;
m_arrPreView[1][0] = 1; m_arrPreView[1][1] = 1; m_arrPreView[1][2] = 1; m_arrPreView[1][3] = 0;
m_PreViewPoint.x = 4;
m_PreViewPoint.y = 0;
break;
case 2:
m_arrPreView[0][0] = 1; m_arrPreView[0][1] = 1; m_arrPreView[0][2] = 0; m_arrPreView[0][3] = 0;
m_arrPreView[1][0] = 1; m_arrPreView[1][1] = 1; m_arrPreView[1][2] = 0; m_arrPreView[1][3] = 0;
m_PreViewPoint.x = 4;
m_PreViewPoint.y = 0;
break;
case 3:
m_arrPreView[0][0] = 1; m_arrPreView[0][1] = 1; m_arrPreView[0][2] = 0; m_arrPreView[0][3] = 0;
m_arrPreView[1][0] = 0; m_arrPreView[1][1] = 1; m_arrPreView[1][2] = 1; m_arrPreView[1][3] = 0;
m_PreViewPoint.x = 4;
m_PreViewPoint.y = 0;
break;
case 4:
m_arrPreView[0][0] = 0; m_arrPreView[0][1] = 1; m_arrPreView[0][2] = 1; m_arrPreView[0][3] = 0;
m_arrPreView[1][0] = 1; m_arrPreView[1][1] = 1; m_arrPreView[1][2] = 0; m_arrPreView[1][3] = 0;
m_PreViewPoint.x = 4;
m_PreViewPoint.y = 0;
break;
case 5:
m_arrPreView[0][0] = 0; m_arrPreView[0][1] = 0; m_arrPreView[0][2] = 1; m_arrPreView[0][3] = 0;
m_arrPreView[1][0] = 1; m_arrPreView[1][1] = 1; m_arrPreView[1][2] = 1; m_arrPreView[1][3] = 0;
m_PreViewPoint.x = 4;
m_PreViewPoint.y = 0;
break;
case 6:
m_arrPreView[0][0] = 0; m_arrPreView[0][1] = 1; m_arrPreView[0][2] = 0; m_arrPreView[0][3] = 0;
m_arrPreView[1][0] = 1; m_arrPreView[1][1] = 1; m_arrPreView[1][2] = 1; m_arrPreView[1][3] = 0;
m_PreViewPoint.x = 4;
m_PreViewPoint.y = 0;
break;
}
m_PreViewType = index;//把预览类型记录下来
}
//把俄罗斯方块复制到网格中
void CopyTetris()
{
for (int i = 0; i < ROW_TETRIS; i++)
{
for (int j = 0; j < COL_TETRIS; j++)
{
m_arr[i][j + 4] = m_arrTetris[i][j];
}
}
}
//响应回车键
void OnReturn(HWND hWnd)
{
MessageBox(hWnd, L"游戏开始", L"温馨提示", MB_OK);
//将预览图绘制到俄罗斯方块数组中,然后绘制到网格中
memcpy(m_arrTetris, m_arrPreView, sizeof(m_arrPreView));
//将类型赋值给俄罗斯方块
m_TetrisType = m_PreViewType;
m_TetrisPoint.x = m_PreViewPoint.x;
m_TetrisPoint.y = m_PreViewPoint.y;
CopyTetris();
//生成预览图
CreatePreTetris();
//开启定时器,设置定时器刷新页面
SetTimer(hWnd, TIMEDOWN, 500, NULL);
BeginTimer = TRUE;
}
//响应定时器
void OnTimer(HWND hWnd)
{
InvalidateRect(hWnd, NULL, TRUE);//刷新页面
//下落
if (reachBottom() ||reachOtherTetris())//判断是否到达最低端或者在其他方块上
{
changeState();//不下落了,改变状态
//消除行
removeRow();
//判断游戏是否结束
if (FALSE == judgeOver())
{
//将预览赋值给俄罗斯方块
memcpy(m_arrTetris, m_arrPreView, sizeof(m_arrPreView));
//将类型赋值给俄罗斯方块
m_TetrisType = m_PreViewType;
m_TetrisPoint.x = m_PreViewPoint.x;
m_TetrisPoint.y = m_PreViewPoint.y;
//将俄罗斯方块赋值给网格
CopyTetris();
//重新生成预览
CreatePreTetris();
}
else//游戏结束
{
KillTimer(hWnd, TIMEDOWN);//关闭定时器
if (IDYES == MessageBox(hWnd, L"游戏结束", L"温馨提示", MB_YESNO))
{
clearArr();
OnReturn(hWnd);
}
else//退出
{
DestroyWindow(hWnd);
}
}
}
else
{
tetrisDown();
m_TetrisPoint.y++;
}
}
//下落函数
void tetrisDown()
{
for (int i = ROW - 1; i >= 0; i--)
{
for (int j = 0; j < COL; j++)
{
if (m_arr[i][j] == 1)
{
m_arr[i + 1][j] = 1;
m_arr[i][j] = 0;
}
}
}
}
//判断是不是到了最底端
BOOL reachBottom()
{
for (int i = 0; i < COL; i++)
{
if (m_arr[ROW - 1][i] == 1)
{
return TRUE;
}
}
return FALSE;
}
//判断是不是在其他方块上
BOOL reachOtherTetris()
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (m_arr[i][j] == 1 && m_arr[i + 1][j] == 2)
{
return TRUE;
}
}
}
return FALSE;
}
//停下来就改变状态1变2
void changeState()
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (m_arr[i][j] == 1)
{
m_arr[i][j] = 2;
}
}
}
}
//判断游戏是否结束
BOOL judgeOver()
{
for (int i = 0; i < COL; i++)
{
if (m_arr[2][i] == 2)
{
return TRUE;
}
}
return FALSE;
}
//消除行
void removeRow()
{
//消除行
int row[ROW] = {-1};//记录一下哪一行
int num = -1;//记录有几行
for (int i = ROW - 1; i >= 0; i--)
{
if (m_arr[i][0] == 2 && m_arr[i][1] == 2 && m_arr[i][2] == 2 && m_arr[i][3] == 2
&& m_arr[i][4] == 2 && m_arr[i][5] == 2 && m_arr[i][6] == 2 && m_arr[i][7] == 2
&& m_arr[i][8] == 2 && m_arr[i][9] == 2)
{
num++;
row[num] = i;
for (int j = 0; j < COL; j++)
{
m_arr[i][j] = 0;
}
}
}
//整体下落
for (int k = 0; k < num + 1; k++)
{
for (int i = row[k]; i >= 2; i--)
{
for (int j = 0; j < COL; j++)
{
if (m_arr[i][j] == 2)
{
m_arr[i + 1][j] = 2;
m_arr[i][j] = 0;
}
}
}
}
//加分,一行10分
for (int i = 0; i < num + 1; i++)
{
m_Score += 10;
}
}
//向左移
void leftShift()
{
//限制边界
for (int i = 0; i < ROW; i++)
{
if (m_arr[i][0] == 1)
{
return;
}
}
//碰到已经落好的方块
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (m_arr[i][j] == 1 && m_arr[i][j - 1] == 2)
{
return;
}
}
}
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (m_arr[i][j] == 1)
{
m_arr[i][j - 1] = 1;
m_arr[i][j] = 0;
}
}
}
m_TetrisPoint.x--;
}
//向右移
void rightShift()
{
//限制边界
for (int i = 0; i < ROW; i++)
{
if (m_arr[i][COL - 1] == 1)
{
return;
}
}
//碰到已经落好的方块
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (m_arr[i][j] == 1 && m_arr[i][j + 1] == 2)
{
return;
}
}
}
for (int i = 0; i < ROW; i++)
{
for (int j = COL - 1; j >= 0; j--)
{
if (m_arr[i][j] == 1)
{
m_arr[i][j + 1] = 1;
m_arr[i][j] = 0;
}
}
}
m_TetrisPoint.x++;
}
//下键
void OnDown(HWND hWnd)
{
OnTimer(hWnd);
}
//上键
void OnUp()
{
switch (m_TetrisType)
{
case 0://长条的
StripChange();
break;
case 2://正方形的
break;
case 1:
case 3:
case 4:
case 5:
case 6:
NormalChange();
break;
}
}
//长条变形处理
void StripChange()
{
for (int i = 0; i < COL; i++)
{
if (m_arr[0][i] == 1 || m_arr[1][i] == 1)
{
return;
}
}
//先判断是横向还是竖向的
if (m_arr[m_TetrisPoint.y][m_TetrisPoint.x + 1] == 1)//横向
{
//判断是否可以旋转
if (m_arr[2][m_TetrisPoint.x] == 1 ||
m_arr[ROW - 1][m_TetrisPoint.x] == 1 ||
m_arr[ROW - 2][m_TetrisPoint.x] == 1
)
{
return;
}
if (m_arr[m_TetrisPoint.y - 1][m_TetrisPoint.x] == 2 ||
m_arr[m_TetrisPoint.y - 1][m_TetrisPoint.x - 1] == 2 ||
m_arr[m_TetrisPoint.y + 1][m_TetrisPoint.x] == 2 ||
m_arr[m_TetrisPoint.y + 2][m_TetrisPoint.x] == 2 ||
m_arr[m_TetrisPoint.y + 1][m_TetrisPoint.x + 1] == 2 ||
m_arr[m_TetrisPoint.y + 1][m_TetrisPoint.x + 2] == 2 ||
m_arr[m_TetrisPoint.y + 2][m_TetrisPoint.x + 1] == 2 ||
m_arr[m_TetrisPoint.y + 2][m_TetrisPoint.x + 2] == 2
)
{
return;
}
//原有的清零
m_arr[m_TetrisPoint.y][m_TetrisPoint.x - 1] = 0;
m_arr[m_TetrisPoint.y][m_TetrisPoint.x + 1] = 0;
m_arr[m_TetrisPoint.y][m_TetrisPoint.x + 2] = 0;
//旋转
m_arr[m_TetrisPoint.y - 1][m_TetrisPoint.x] = 1;
m_arr[m_TetrisPoint.y + 1][m_TetrisPoint.x] = 1;
m_arr[m_TetrisPoint.y + 2][m_TetrisPoint.x] = 1;
}
else if (m_arr[m_TetrisPoint.y + 1][m_TetrisPoint.x] == 1)//竖向
{
//判断是否可以旋转
if (m_arr[m_TetrisPoint.y][0] == 1 ||
m_arr[m_TetrisPoint.y][COL - 2] == 1 ||
m_arr[m_TetrisPoint.y][COL - 1] == 1)
{
return;
}
if (m_arr[m_TetrisPoint.y][m_TetrisPoint.x - 1] == 2 ||
m_arr[m_TetrisPoint.y][m_TetrisPoint.x + 1] == 2 ||
m_arr[m_TetrisPoint.y][m_TetrisPoint.x + 2] == 2 ||
m_arr[m_TetrisPoint.y - 1][m_TetrisPoint.x + 1] == 2 ||
m_arr[m_TetrisPoint.y + 1][m_TetrisPoint.x - 1] == 2 ||
m_arr[m_TetrisPoint.y + 2][m_TetrisPoint.x - 1] == 2 ||
m_arr[m_TetrisPoint.y - 1][m_TetrisPoint.x + 2] == 2
)
{
return;
}
//清零
m_arr[m_TetrisPoint.y - 1][m_TetrisPoint.x] = 0;
m_arr[m_TetrisPoint.y + 1][m_TetrisPoint.x] = 0;
m_arr[m_TetrisPoint.y + 2][m_TetrisPoint.x] = 0;
//旋转
m_arr[m_TetrisPoint.y][m_TetrisPoint.x + 1] = 1;
m_arr[m_TetrisPoint.y][m_TetrisPoint.x - 1] = 1;
m_arr[m_TetrisPoint.y][m_TetrisPoint.x + 2] = 1;
}
}
void NormalChange()
{
//将图形copy到copyBlock数组中
int copyBlock[3][3] = { 0 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
copyBlock[i][j] = m_arr[m_TetrisPoint.y + i][m_TetrisPoint.x + j];
}
}
//是否可以旋转
if (m_TetrisPoint.x < 0 || m_TetrisPoint.x == COL - 1 || m_TetrisPoint.x == COL - 2 ||
m_TetrisPoint.y == ROW - 1 || m_TetrisPoint.y == ROW - 2 ||
m_TetrisPoint.y == 0 || m_TetrisPoint.y == 1)
{
return;
}
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (copyBlock[i][j] == 2)
{
return;
}
}
}
//在copyBlock中旋转
for (int i = 0; i < 3; i++)
{
for (int j = i; j < 3; j++)
{
int temp = copyBlock[i][j];
copyBlock[i][j] = copyBlock[j][i];
copyBlock[j][i] = temp;
}
}
//在进行旋转
for (int i = 0; i < 3; i++)
{
int temp = copyBlock[i][0];
copyBlock[i][0] = copyBlock[i][2];
copyBlock[i][2] = temp;
}
//完成旋转复制到网格
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
m_arr[m_TetrisPoint.y + i][m_TetrisPoint.x + j] = copyBlock[i][j];
}
}
}
//清除方格
void clearArr()
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
m_arr[i][j] = 0;
}
}
}
总结一下吧,这个小项目不算难,是最基础的俄罗斯方块,在游戏结束时本人没做任何操作,喜欢的小伙伴可以去改进,还可以设置关卡哟,我在这里就不设置啦,在编写的时候本人认为有一个小难点,在不同的俄罗斯方块转动的时候比较考验逻辑性。我是把这7种不同的方块分成了三大部分。一个是正方形的,当然这个是最简单的,旋转都一样不用考虑,长方形的视作一部分,剩下的5个视作一部分,我是通过给他设置旋转点来进行旋转的。当然不同的小伙伴有不同的思路。这个代码有好多地方可以优化,我在这就不进行优化啦,希望我这次抛砖引玉可以给小伙伴们提供一些思路,欢迎评论,私信优化方案。



