俄罗斯方块MFC手把手教程

时间:2025-01-04 12:46:16

1、程序界面就是这样了,要多简单有多简单,两个按钮加两个picture control就完成了,通过上下左右四个键控制。 我个人比较熟悉MVC的结构,考虑到这是个小程序只有一个界面,又考虑到我是个C++菜鸟,为了避免比必要的风险,于是省去了控制器。用CBoxDlg当做view层,用Game做Model辅以Tool类,前后台划分以及各个类的详细情况如图所示。CBoxDlg作为唯一的窗口,主要完成图像显示以及用户操作的收集,本身并不负责逻辑的处理。通过DrawBigNet()和DrawSmallNet()两个方法分别将后台数据显示在两个图片控件中,后台提供的数据由0和1组成的矩阵,1代表有方块,0反之。至于所有的逻辑操作全部放在Game类中完成,Tool类代表积木,Game类比较庞大,不过public的东西却很少,这也是面向对象封装的意义之所在吧,每个类对外只是一个黑盒,认真完成自己的事情不去关心其他类的实现细节,将自己应该做的踏踏实实的做好,此所谓高内聚。Game内的方法大致分为四类:第一、get方法向外提供界面需要的数据;第二、类似Input方法用来获得界面传来的操作,第三、canXXX和isXXX方法判断某个操作是否可行或者判断当前状态;第四、MoveXXX等方法执行这个操作。前两种方法都是public的后两种都是private的,前两种调用后两种,复杂的功能被逐渐分拆成细小的碎块,每个只要几行至十几行代码就够了。长期以来,本人一直呼吁不要将一个函数的长度超过30行,将复杂的功能逐渐细化,即容易完成也提高了可维护性。

俄罗斯方块MFC手把手教程

2、码已经上传,列几个细节:void CBoxDlg::OnKeyDown(UINT nChar) { game->Input(nChar); Invalidate(true);// 重绘画面}

3、2)绘制主网格,void CBoxDlg::DrawBigNet() { CRect rect; CWnd *wnd = GetDlgItem(IDC_PIC_MAIN); CPaintDC dc(wnd); wnd->GetClientRect(&rect); if(game->GetBigNet()) for(int i=0;i<game->NET_HEIGHT;i++) for(int j=0;j<game->NET_WIDTH;j++) if(game->GetBigNet()[i*(game->NET_WIDTH)+j]==1) dc.Rectangle( j*rect.Width()/game->NET_WIDTH, i*rect.Height()/game->NET_HEIGHT, (j+1)*rect.Width()/game->NET_WIDTH, (i+1)*rect.Height()/game->NET_HEIGHT); wnd->RedrawWindow(); }

4、3)MFC的事件响应有点奇特,必须覆盖一下这个方法:BOOL CBoxDlg::PreTranslateMessage(MSG* pMsg) { if(pMsg->message==WM_KEYDOWN) OnKeyDown((UINT)pMsg->wParam); return false; }

5、4)这个方法我觉得比较巧妙,用假设移动统计移动前后方块数目的方法判断是否可以运动:bool Game::CanMoveDown() { int cnt1 = 4,cnt2=0,x = Loc_X,y = Loc_Y+1; int *tmp = (int *)malloc(sizeof(int)*Height*Width); // 复制一个副本,统计原有方块数+tool中的块数 for(int i=0;i<Height;i++) for(int j=0;j<Width;j++) { tmp[i*Width+j] = BigNet[i*Width+j]; cnt1 += tmp[i*Width+j]; } // 假设发生变换 for(int i=0;i<4;i++) for(int j=0;j<4;j++) { if( i+y>=0 && i+y<Height && j+x>=0 && j+x<Width && tool->GetData()[i*4+j]) tmp[(i+y)*Width+j+x] = 1; } // 统计变换后方块数 for(int i=0;i<Height;i++) for(int j=0;j<Width;j++) cnt2 += tmp[i*Width+j]; delete(tmp); return cnt2==cnt1; }

6、5)Remove 三兄弟,本来以为这里会有点烦,情况比较多无从下手,分解成三个函数以后还是很简单的:void Game::RemoveLines() { for(int i=Height-1;i>0;i--) while(CanRemoveLine(i)) RemoveLine(i); }void Game::RemoveLine(int index) { for(int i=index;i>0;i--) for(int j=0;j<Width;j++) BigNet[i*Width+j] = BigNet[(i-1)*Width+j]; for(int j=0;j<Width;j++) BigNet[j] = 0; }bool Game::CanRemoveLine(int index) { int count = 0; for(int i=0;i<Width;i++) if(BigNet[index*Width+i]==1) count ++; return count==Width; }

7、 数数看自己写的代码也有七八百行,历时三日,总算是完成,小有成就感。 最后吐槽一下C++,用了很久的C#再用C++还真不习惯。 连个像样的智能提示都没有,好不容易提示了个方法名却连个注释也看不到,你让我自己猜啊! 光是字符和字符串就有如此之多的类型,真心记不住,更别提互相之间的转化了,放弃ASCII能死吗? 编译器绝对是个弱智,你踩他一脚他告诉你头疼,类后面还要加;文件结束还得多加几个回车,不然还编译通不过。

© 手抄报圈