项目地址:
https://yunpan.cn/cYkeA6gGbFrXD (提取码:a2a3)
一、定义了一些结构体和常量
// 每个俄罗斯方块(block)由4个正方形(pston)组成,一个正方形(pston)的定义如下: typedef struct pston { int row; int col; } ty_pston; // 定义一个俄罗斯方块(block),包含了4个正方形pst,及颜色属性color,距离左l与顶t的位置 typedef struct block { ty_pston pst[4]; COLORREF color; int t; int l; } ty_block; // 游戏区域(gamePst)的坐标(ps)是否有方块(bSet) typedef struct gamePst { BOOL bSet; COLORREF color; ty_pston ps; } ty_gamepst; // 随机颜色 #define COLOR_RAND RGB(rand()%256, rand() % 256, rand() % 256) // 俄罗斯方块(block)的种类(pst_kind)有7种,每种有4个正方形(pston) const ty_pston pst_kind[7][4] = { { {0,-1}, {0,0}, {0,1}, {0,2} }, { { 0,-1},{ 0,0 },{ 0,1 },{ 1,1 } }, { { 0,-1 },{ 0,0 },{ 0,1 },{ -1,1 } }, { { 0,-1 },{ 0,0 },{ -1,0 },{ -1,1 } }, { { 0,-1 },{ 0,0 },{ 1,0 },{ 1,1 } }, { { 0,-1 },{ 0,0 },{ 1,0 },{ 1,-1 } }, { { 0,-1 },{ 0,0 },{ 0,1 },{ 1,0 } }, }; // 俄罗斯方块的移动方向(direction) enum direction {LEFT = -1,DOWN = 0,RIGHT = 1}; // 每个正方形(pston)的宽度 const int PST_WIDTH = 24; // 游戏区域相对于左边与上边的距离 const int GAME_SIDE_LEFT = 10; const int GAME_SIDE_TOP = 10;
#pragma once class CStone { public: CStone(); ~CStone(); private: ty_block m_block; int curBlockType; ty_gamepst m_gPst[18][10]; int score; public: ty_block m_preBlock; public: void drawBlock(); void makeBlock(ty_block& block); void alterBlock(); void MoveTo(int to); void reMakeBlock(); BOOL canMove(ty_block block); CRect getPstRect(pston pst); CRect getGamePstRect(pston pst); CRect getPrePstRect(pston pst); BOOL IsTop(); };------------
#include "stdafx.h" #include "Stone.h" #include <vector> #include <algorithm> #include <time.h> #include "TetrisDlg.h" CStone::CStone() { // 初始化时生成一个游戏区的俄罗斯方块 makeBlock(m_block); // 把游戏区域18*10=180个正方形初始化状态与位置 for (int i = 0; i < 18; ++i) for (int j = 0; j < 10; ++j) m_gPst[i][j] = { FALSE ,RGB(255,255,255),{ i,j } }; // 初始化分数 score = 0; } CStone::~CStone() { } // 生成一个俄罗斯方块 void CStone::makeBlock(ty_block& block) { srand((unsigned)time(0)); curBlockType = rand() % 7; for (int i = 0; i < 4; ++i) block.pst[i] = pst_kind[curBlockType][i]; block.color = COLOR_RAND; block.l = 4; block.t = 0; } // 绘制界面 void CStone::drawBlock() { // 获取客户区的绘图设备 CClientDC* p_dc = (CClientDC*)AfxGetMainWnd()->GetDC(); // 获取兼容的绘图设备 CDC mdc; mdc.CreateCompatibleDC(p_dc); // 获取兼容的位图 CBitmap bmp_cpb; bmp_cpb.CreateCompatibleBitmap(p_dc, 10 * PST_WIDTH, 18 * PST_WIDTH); // 载入位图 mdc.SelectObject(&bmp_cpb); // 填充背景色为白色 mdc.FillSolidRect(CRect(0,0,10* PST_WIDTH,18* PST_WIDTH), RGB(255,255,255)); // 绘制游戏区的俄罗斯方块 for (int a = 0; a < 4;++a) { CBrush * bs_block = new CBrush(m_block.color); mdc.FillRect(getPstRect(m_block.pst[a]), bs_block); bs_block->DeleteObject(); bs_block = nullptr; } // 绘制游戏区的底部正方形 for (int i = 0; i < 18;++i) { for (int j = 0; j < 10;++j) { if (m_gPst[i][j].bSet) { CBrush * bs_gPst = new CBrush(m_gPst[i][j].color); mdc.FillRect(getGamePstRect(m_gPst[i][j].ps), bs_gPst); bs_gPst->DeleteObject(); bs_gPst = nullptr; } } } // 将缓冲区的位图显示在游戏区 p_dc->BitBlt(GAME_SIDE_LEFT, GAME_SIDE_TOP, 10 * PST_WIDTH, 18 * PST_WIDTH, &mdc, 0, 0, SRCCOPY); mdc.DeleteDC(); // 获取兼容的绘图设备 CDC premdc; premdc.CreateCompatibleDC(p_dc); // 绘制预览区的俄罗斯方块 CBitmap bmp_precpb; bmp_precpb.CreateCompatibleBitmap(p_dc, 6 * PST_WIDTH, 18 * PST_WIDTH); // 载入位图 premdc.SelectObject(&bmp_precpb); // 填充背景色为白色 premdc.FillSolidRect(CRect(0, 0, 6 * PST_WIDTH, 18 * PST_WIDTH), RGB(255, 255, 255)); for (int b = 0; b < 4; ++b) { CBrush * bs_preblock = new CBrush(m_preBlock.color); premdc.FillRect(getPrePstRect(m_preBlock.pst[b]), bs_preblock); bs_preblock->DeleteObject(); bs_preblock = nullptr; } // 绘制游戏得分情况 CString curScore; curScore.Format(_T("score:%d"), score); premdc.TextOut(2*PST_WIDTH, 300, curScore); // 将缓冲区的位图显示在预览区 p_dc->BitBlt((10+1)*PST_WIDTH +GAME_SIDE_LEFT, GAME_SIDE_TOP, (10+1+6) * PST_WIDTH, 18 * PST_WIDTH, &premdc, 0, 0, SRCCOPY); premdc.DeleteDC(); p_dc->DeleteDC(); } // 获取俄罗斯方块中的正方形区域 CRect CStone::getPstRect(pston pst) { if ((pst.row + m_block.t) < 0 || (pst.col+ m_block.l) < 0) { return CRect(0,0,0,0); } return CRect( (pst.col+m_block.l)*PST_WIDTH, (pst.row+m_block.t)*PST_WIDTH, (pst.col + m_block.l+1) *PST_WIDTH, (pst.row + m_block.t+1) *PST_WIDTH ); } // 获取游戏区域中的每个正方形的区域 CRect CStone::getGamePstRect(pston pst) { return CRect( (pst.col)*PST_WIDTH, (pst.row)*PST_WIDTH, (pst.col + 1) *PST_WIDTH, (pst.row + 1) *PST_WIDTH ); } // 获取预览区的俄罗斯方块的区域 CRect CStone::getPrePstRect(pston pst) { return CRect( (pst.col+m_preBlock.l-1)*PST_WIDTH, (pst.row + m_preBlock.t+3)*PST_WIDTH, (pst.col + m_preBlock.l)*PST_WIDTH, (pst.row + m_preBlock.t +4)*PST_WIDTH ); } // 变形 void CStone::alterBlock() { ty_block tempcopy = m_block; auto swapBlock = [](ty_pston & ps) { int temp = ps.row; ps.row = ps.col*(-1); ps.col = temp; }; for (ty_pston &ps : tempcopy.pst) { swapBlock(ps); } if (canMove(tempcopy)) { m_block = tempcopy; } } // 移动 void CStone::MoveTo(int to) { if (to == DOWN) { ++m_block.t; if (!canMove(m_block)) { --m_block.t; reMakeBlock(); } } else { m_block.l += to; if (!canMove(m_block)) { m_block.l -= to; } } } // 判断能否移动 BOOL CStone::canMove(ty_block block) { std::vector<int> vcol,vrow; for (ty_pston e : block.pst) { if (m_gPst[e.row + block.t][e.col + block.l].bSet) return FALSE; vcol.push_back(e.col); vrow.push_back(e.row); } int colMax = *std::max_element(vcol.begin(), vcol.end()); int colMin = *std::min_element(vcol.begin(), vcol.end()); int rowMax = *std::max_element(vrow.begin(),vrow.end()); return (colMin + block.l) >= 0 && (colMax + block.l) < 10 && (rowMax + block.t) < 18; } // 重新生成俄罗斯方块 void CStone::reMakeBlock() { for (ty_pston e : m_block.pst) { m_gPst[e.row + m_block.t][e.col + m_block.l].bSet = TRUE; m_gPst[e.row + m_block.t][e.col + m_block.l].color = m_block.color; } auto isfull = [=](int row) { for (int j=0; j < 10; ++j) if (!m_gPst[row][j].bSet) return FALSE; return TRUE; }; for (int i=0; i < 18; ) { if (isfull(i)) { for (int k = i; k > 0;--k) { for (int t = 0; t < 10;++t) { m_gPst[k][t].bSet = m_gPst[k - 1][t].bSet; m_gPst[k][t].color = m_gPst[k - 1][t].color; } } score += 100; continue; } ++i; } m_block = m_preBlock; makeBlock(m_preBlock); IsTop(); } BOOL CStone::IsTop() { ++m_block.t; if (!canMove(m_block)) { KillTimer(AfxGetMainWnd()->m_hWnd, 1002); ((CTetrisDlg*)AfxGetMainWnd())->m_status = FALSE; AfxMessageBox(_T("游戏已结束")); } --m_block.t; return 0; }
三、对话框
void CTetrisDlg::OnPaint() { // 游戏区域 CRect rc(9, 9, 251, 443); // 预览区 CRect preRc(273, 9, 420, 445); CClientDC* p_dc = (CClientDC*)GetDC(); CPen * pen = new CPen(PS_SOLID, 1, RGB(255, 0, 0)); CPen * oldpen = p_dc->SelectObject(pen); p_dc->Rectangle(rc); p_dc->Rectangle(preRc); p_dc->SelectObject(oldpen); pen->DeleteObject(); pen = nullptr; p_dc->Detach(); }
void CTetrisDlg::OnBnClickedOk() { if (!m_status) { m_status = TRUE; m_stone->makeBlock(m_stone->m_preBlock); m_stone->drawBlock(); SetTimer(1002, 300, 0); } else { m_status = FALSE; KillTimer(1002); } } BOOL CTetrisDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 if (pMsg->message == WM_KEYDOWN) { if (!m_status) { return TRUE; } switch (pMsg->wParam) { case VK_LEFT: m_stone->MoveTo(LEFT); break; case VK_RIGHT: m_stone->MoveTo(RIGHT); break; case VK_DOWN: m_stone->MoveTo(DOWN); break; case VK_UP: m_stone->alterBlock(); break; } m_stone->drawBlock(); return TRUE; } return CDialog::PreTranslateMessage(pMsg); } void CTetrisDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 switch (nIDEvent) { case 1002: { m_stone->MoveTo(DOWN); break; } default: break; } m_stone->drawBlock(); CDialog::OnTimer(nIDEvent); }