基于MFC实现的俄罗斯方块游戏

TrueLove

发布日期: 2019-06-06 11:04:40 浏览量: 1511
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

1.需求分析

需要实现的功能:

  • 使用键盘方向键实现方块的控制和移动

  • 程序能够对方块的位置进行判断,防止方块移动出界

  • 当满足消行条件时,能够及时消去满足条件的行数,其他保持不变

  • 实现加分和等级晋级,以及游戏难度增加等附加功能

2.设计分析

2.1 方块显示

可以采用二维数组来对方块进行控制变化和显示。1-代表有方块,0-无方块。如:

  1. Now[4][4]={
  2. 1110
  3. 0100
  4. 0000
  5. 0000
  6. };

2.2 背景数组

  1. ArryBK[18][12]={0,0,…,0};

屏幕绘制采用双缓冲,现在内存绘制,最后一次性送到显存;

2.3 数据结构设计

定义结构体

  1. struct shape
  2. {
  3. int ary[4][4]; //方块数组
  4. CPoint pt; //起点(方块左上角),X-所在行,Y-所在列
  5. };

定义类

  1. class CRussia
  2. {
  3. public:
  4. //构造函数,用来初始化数据成员
  5. CRussia();
  6. virtual ~CRussia();
  7. //重新开始游戏
  8. void GameAgain();
  9. //判断游戏是否结束
  10. bool IsOver();
  11. //将当前形状数组附加到背景数组
  12. void Attch();
  13. //获得当前速度,用开控制游戏难度
  14. int GetSpeed()const;
  15. //在内存dc中画出当前图形
  16. void DrawNow(CDC *);
  17. //在内存dc中画出右边分数,等级,和下一图形
  18. void DrawScores(CDC *);
  19. //在内存dc画出背景
  20. void DrawRussia(CDC *);
  21. //消行
  22. void DeleteLines();
  23. //当用户按下方向键上时调用,用来变换矩阵
  24. void InvertShape();
  25. //用来产生随机矩阵图形
  26. void RandShape();
  27. //用来判断方向,当用户按下方向键,左(2),右(3),下(1)时调用
  28. void Judge(int i=1);//默认判断方向为下
  29. private:
  30. bool m_bOver; //判断是否结束
  31. int m_scores; //玩家得分
  32. int m_speed; //时间间隔,用来设置定时器控制难度
  33. int m_level; //玩家级数
  34. shape Now; //当前矩阵图形
  35. shape Will; //下一矩阵图形
  36. int Russia[18][12]; //背景矩阵
  37. CBitmap m_fangkuai; //方块位图
  38. };

3.设计实现

游戏数据初始化

方块初始起点设置成第0行,第6列。

  1. CRussia::CRussia()
  2. {
  3. m_level=1;
  4. m_scores=0;
  5. m_speed=600; //600ms间隔
  6. m_bOver=false;
  7. m_fangkuai.LoadBitmap(IDB_FANGKUAI);
  8. int i,j;
  9. for(i=0;i<18;i++)
  10. for(j=0;j<12;j++)
  11. {
  12. Russia[i][j]=0; //背景数组清空
  13. }
  14. for (i=0;i<4;i++)
  15. for(j=0;j<4;j++)
  16. {
  17. Now.ary[i][j]=0; //当前矩阵数组清空
  18. Will.ary[i][j]=0; //下一矩阵数组清空
  19. }
  20. //当前矩阵图形初始化
  21. Now.ary[0][0]=1;
  22. Now.ary[0][1]=1;
  23. Now.ary[1][0]=1;
  24. Now.ary[2][0]=1;
  25. Now.pt.x=0;
  26. Now.pt.y=6;
  27. //下一矩阵图形初始化
  28. Will.ary[0][0]=1;
  29. Will.ary[0][1]=1;
  30. Will.ary[1][0]=1;
  31. Will.ary[2][0]=1;
  32. Will.pt.x=6;
  33. Will.pt.y=0;
  34. }

方块,背景及玩家信息屏幕绘制实现

当前方块图形绘制,由于Now.pt.x表示所在行数,Now.pt.y表示所在列数。正好与默认坐标映射模式相反,所以在BitBlt()函数的前2个参数的X,Y坐标时,调换了行和列的坐标值。

  1. void CRussia::DrawNow(CDC *pDc)
  2. {
  3. // 创建内存dc
  4. CDC memDc;
  5. memDc.CreateCompatibleDC(pDc);
  6. int nDC=memDc.SaveDC();
  7. //初始化内存dc
  8. memDc.SelectObject(m_fangkuai);
  9. for (int i=0;i<4;i++)
  10. for(int j=0;j<4;j++)
  11. {
  12. if (1==Now.ary[i][j])
  13. {
  14. //将方块图案绘制到缓冲内存dc中
  15. pDc->BitBlt(30*(j+Now.pt.y),30*(i+Now.pt.x),30,30,&memDc,0,0,SRCCOPY);
  16. }
  17. }
  18. memDc.RestoreDC(nDC);
  19. }

背景数组绘制。

  1. void CRussia::DrawRussia(CDC *pDc)
  2. {
  3. CDC memDc;
  4. memDc.CreateCompatibleDC(pDc);
  5. int nDC=memDc.SaveDC();
  6. memDc.SelectObject(m_fangkuai);
  7. for (int i=0;i<18;i++)
  8. for(int j=0;j<12;j++)
  9. {
  10. if (1==Russia[i][j])
  11. {
  12. pDc->BitBlt(30*j,30*i,30,30,&memDc,0,0,SRCCOPY);
  13. }
  14. }
  15. memDc.RestoreDC(nDC);
  16. }

游戏附加信息绘制。

  1. void CRussia::DrawScores(CDC *pDC)
  2. {
  3. CDC memDc;
  4. memDc.CreateCompatibleDC(pDC);
  5. int nDC=memDc.SaveDC();
  6. memDc.SelectObject(m_fangkuai);
  7. for (int i=0;i<4;i++)
  8. for(int j=0;j<4;j++)
  9. {
  10. if (1==Will.ary[i][j])
  11. {
  12. pDC->BitBlt(30*(j+13),30*(i+8),30,30,&memDc,0,0,SRCCOPY);
  13. }
  14. }
  15. memDc.RestoreDC(nDC);
  16. int nOldDC=pDC->SaveDC();
  17. CFont font;
  18. font.CreatePointFont(300,"华文楷体");
  19. pDC->SelectObject(&font);
  20. CString str;
  21. pDC->SetTextColor(RGB(20,255,0));
  22. pDC->SetBkColor(RGB(255,255,0));
  23. str.Format("%d",m_level);
  24. pDC->TextOut(430,120,str);
  25. str.Format("%d",m_scores);
  26. pDC->TextOut(430,2,str);
  27. pDC->RestoreDC(nOldDC);
  28. }

键盘方向控制实现

默认方向向下,当方块向下移动时,遍历方块数组依次判断每个值为1的方块的正下方方块是否有方块阻挡,以及判断是否碰到底界,如果遇到阻挡或碰到底,则将当前方块数组附加到背景数组,并返回结束本次移动,否则Now.pt.x++方块起点的X值加1。

  1. void CRussia::Judge(int i)
  2. {
  3. //1-下; 2-左 3-右
  4. int j=0,k=0;
  5. switch(i)
  6. {
  7. case 1:
  8. for ( j=0;j<4;j++)
  9. for( k=0;k<4;k++)
  10. {
  11. if (1==Now.ary[j][k])
  12. {
  13. //判断是否遇阻,以及是否遇到底界
  14. if (1==Russia[Now.pt.x+j+1][Now.pt.y+k]||17==Now.pt.x+j)
  15. {
  16. if (0==Now.pt.x)
  17. {
  18. m_bOver=true;
  19. MessageBeep(1);
  20. }
  21. Attch();
  22. return;
  23. }
  24. }
  25. }
  26. Now.pt.x++;
  27. break;
  28. case 2:
  29. for (j=0;j<4;j++)
  30. for(k=0;k<4;k++)
  31. {
  32. if (1==Now.ary[j][k])
  33. {
  34. //判断是否遇阻,以及是否遇到左边界
  35. if (1==Russia[Now.pt.x+j][Now.pt.y+k-1]||0==Now.pt.y)
  36. {
  37. MessageBeep(1);
  38. return;
  39. }
  40. }
  41. }
  42. Now.pt.y--;
  43. break;
  44. case 3:
  45. for (j=0;j<4;j++)
  46. for(k=0;k<4;k++)
  47. {
  48. if (1==Now.ary[j][k])
  49. {
  50. //判断是否遇阻,以及是否遇到右边界
  51. if (1==Russia[Now.pt.x+j][Now.pt.y+k+1]||11==Now.pt.y+k)
  52. {
  53. MessageBeep(1);
  54. return;
  55. }
  56. }
  57. }
  58. Now.pt.y++;
  59. break;
  60. default:
  61. break;
  62. }
  63. }

Attch()函数的实现,遍历当前方块数组,值为1的坐标赋值给背景数组对应的点。并判断是否可以消行。

  1. void CRussia::Attch()
  2. {
  3. for (int i=0;i<4;i++)
  4. for(int j=0;j<4;j++)
  5. {
  6. if (1==Now.ary[i][j])
  7. {
  8. Russia[Now.pt.x+i][Now.pt.y+j]=1;
  9. }
  10. }
  11. DeleteLines();
  12. RandShape();
  13. }

DeleteLines()的实现,遍历背景数组,从第一行开始判断。并用nRet记录可消行的行数值。如果nRet不为0,则消行;

  1. void CRussia::DeleteLines()
  2. {
  3. static int nflag=1;
  4. int nRet=0;
  5. bool bRet=true;
  6. for (int i=0;i<18;i++)
  7. {
  8. for (int j=0;j<12;j++)
  9. {
  10. if(0==Russia[i][j])
  11. {
  12. bRet=false;
  13. break;
  14. }
  15. }
  16. //判断是否有一整行全部为1,即达到消行状态
  17. if (bRet)
  18. {
  19. nRet++;
  20. }
  21. bRet=true;
  22. }
  23. //可消行数为1行
  24. if (1==nRet)
  25. {
  26. m_scores+=1;
  27. }
  28. //可消行数为2行
  29. if (2==nRet)
  30. {
  31. m_scores+=3;
  32. }
  33. //可消行数为3行
  34. if (3==nRet)
  35. {
  36. m_scores+=5;
  37. }
  38. //可消行数为4行
  39. if (4==nRet)
  40. {
  41. m_scores+=10;
  42. }
  43. if (m_scores>50*nflag) //加分,晋级,及难度控制
  44. {
  45. m_level+=1;
  46. m_speed-=50;
  47. nflag++;
  48. }
  49. if (nRet!=0)
  50. {
  51. bool bFlag=true;
  52. for (int i=0;i<18;i++)
  53. {
  54. for (int j=0;j<12;j++)
  55. {
  56. if(0==Russia[i][j])
  57. {
  58. bFlag=false;
  59. break;
  60. }
  61. }
  62. //为了记录所在行数值i,采用一行一行消去
  63. if (bFlag)
  64. {
  65. //可消行,i值记录了可消的行所在的行数
  66. for(int m=0;m<i;m++)
  67. for (int k=0;k<12;k++)
  68. {
  69. //第i行以上整体下移一行
  70. Russia[i-m][k]=Russia[i-m-1][k];
  71. }
  72. }
  73. bFlag=true;
  74. }
  75. }
  76. }

方向上键控制,用来转换方块矩阵图形InvertShape()。

  1. void CRussia::InvertShape()
  2. {
  3. int i=0,j=0,MaxV=0;
  4. for ( i=0;i<4;i++)
  5. for( j=0;j<4;j++)
  6. {
  7. Now.ary[i][j]=0; //清空
  8. }
  9. static int k=2;
  10. switch(k)
  11. {
  12. case 0:
  13. Now.ary[0][0]=1;
  14. Now.ary[0][1]=1;
  15. Now.ary[1][0]=1;
  16. Now.ary[1][1]=1;
  17. break;
  18. case 1:
  19. Now.ary[0][0]=1;
  20. Now.ary[0][1]=1;
  21. Now.ary[1][0]=1;
  22. Now.ary[2][0]=1;
  23. break;
  24. case 2:
  25. Now.ary[0][0]=1;
  26. Now.ary[0][1]=1;
  27. Now.ary[1][1]=1;
  28. Now.ary[2][1]=1;
  29. break;
  30. case 3:
  31. Now.ary[0][0]=1;
  32. Now.ary[1][0]=1;
  33. Now.ary[1][1]=1;
  34. Now.ary[1][2]=1;
  35. break;
  36. case 4:
  37. Now.ary[0][2]=1;
  38. Now.ary[1][0]=1;
  39. Now.ary[1][1]=1;
  40. Now.ary[1][2]=1;
  41. break;
  42. case 5:
  43. Now.ary[0][0]=1;
  44. Now.ary[0][1]=1;
  45. Now.ary[0][2]=1;
  46. Now.ary[1][0]=1;
  47. break;
  48. case 6:
  49. Now.ary[0][0]=1;
  50. Now.ary[0][1]=1;
  51. Now.ary[0][2]=1;
  52. Now.ary[1][2]=1;
  53. break;
  54. case 7:
  55. Now.ary[0][1]=1;
  56. Now.ary[1][0]=1;
  57. Now.ary[1][1]=1;
  58. Now.ary[2][0]=1;
  59. break;
  60. case 8:
  61. Now.ary[0][1]=1;
  62. Now.ary[0][2]=1;
  63. Now.ary[1][0]=1;
  64. Now.ary[1][1]=1;
  65. break;
  66. case 9:
  67. Now.ary[0][1]=1;
  68. Now.ary[1][0]=1;
  69. Now.ary[1][1]=1;
  70. Now.ary[1][2]=1;
  71. break;
  72. case 10:
  73. Now.ary[0][0]=1;
  74. Now.ary[0][1]=1;
  75. Now.ary[0][2]=1;
  76. Now.ary[1][1]=1;
  77. break;
  78. case 11:
  79. Now.ary[0][0]=1;
  80. Now.ary[1][0]=1;
  81. Now.ary[2][0]=1;
  82. Now.ary[3][0]=1;
  83. break;
  84. case 12:
  85. Now.ary[0][0]=1;
  86. Now.ary[0][1]=1;
  87. Now.ary[0][2]=1;
  88. Now.ary[0][3]=1;
  89. break;
  90. }
  91. k=++k%13;
  92. //判断是否出右边界,出界则向左移动方块起点
  93. for ( i=0;i<4;i++)
  94. for( j=0;j<4;j++)
  95. {
  96. if (1==Now.ary[i][j])
  97. {
  98. if (12==Now.pt.y+j)
  99. Now.pt.y-=1;
  100. if (13==Now.pt.y+j)
  101. Now.pt.y-=2;
  102. if (14==Now.pt.y+j)
  103. Now.pt.y-=3;
  104. }
  105. }
  106. }

4.游戏测试

经多次测试,游戏可以按照设计文档的功能需求运行;

上传的附件 cloud_download 基于MFC实现的俄罗斯方块游戏.7z ( 288.32kb, 25次下载 )
error_outline 下载需要12点积分

发送私信

放空的心,是最好的礼物;独走的路,是最美的风景

9
文章数
14
评论数
最近文章
eject