俄罗斯方块(MFC)

 

变量声明:

// elsblocksDlg.h : 头文件 // #define BLOCK_X 10 //总游戏区x格子 #define BLOCK_Y 16 //总游戏区y格子 #define CELL_LEN 30 //单个格子的宽度 #define SCELL_LEN 20 //小格子的宽度 #pragma once // CelsblocksDlg 对话框 class CelsblocksDlg : public CDialog { // 构造 public: CelsblocksDlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据 enum { IDD = IDD_ELSBLOCKS_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; int m_level; //等级 int m_Map[(BLOCK_X+1)][BLOCK_Y+1]; //地图 int m_Speed; //下落速度 bool m_Model[7][4][4]; //各个Block的模型 CPoint m_MoldeChange[4][4]; //Block改变的对应坐标 bool m_BlockNext[4][4]; //下一个Block bool m_BlockShow[4][4]; //当前游戏的Block CPoint m_CurrPosi; //当前的m_BlockShow的(0,0)在client的坐标 int m_Score; //得分 bool m_IsStart; //游戏是否开始 bool m_IsPause; //游戏是否暂停 //CRect m_ShowNext; //下一个Block显示区坐标 // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: void NextBlock(); //创建下一个Block void DrawNextBlock(); //画出下一个Block void DrawCell(CRect rect); //画单元格 //定时器 void GoDown(); //往下 void GoLeft(); //向左移动 void GoRight(); //向右移动 void ChangeBlock(bool Block[4][4],bool flag); //改变Block的姿势 void GameStart(); //开始游戏 bool CanMoveLR(int flag); //判断是否能左右移动 bool CanMoveDown(); //判断是否向下移动 void DeletLines(); //消行 void UpdateAll(); //刷新游戏区 void GameResule(); //游戏得分情况 BOOL CelsblocksDlg::PreTranslateMessage(MSG* pMsg); //按键响应 afx_msg void OnTimer(UINT_PTR nIDEvent); // afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg void OnAbout(); afx_msg void OnGameStart(); afx_msg void OnGamePause(); };

主要实现代码:

// elsblocksDlg.cpp : 实现文件 // #include "stdafx.h" #include "elsblocks.h" #include "elsblocksDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialog { public: CAboutDlg(); // 对话框数据 enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: DECLARE_MESSAGE_MAP() public: afx_msg void OnBnClickedOk(); }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) ON_BN_CLICKED(IDOK, &CAboutDlg::OnBnClickedOk) END_MESSAGE_MAP() // CelsblocksDlg 对话框 CelsblocksDlg::CelsblocksDlg(CWnd* pParent /*=NULL*/) : CDialog(CelsblocksDlg::IDD, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CelsblocksDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CelsblocksDlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP ON_WM_TIMER() // ON_WM_KEYDOWN() ON_COMMAND(ID_ABOUT, &CelsblocksDlg::OnAbout) ON_COMMAND(ID_GAME_START, &CelsblocksDlg::OnGameStart) ON_COMMAND(ID_GAME_PAUSE, &CelsblocksDlg::OnGamePause) END_MESSAGE_MAP() // CelsblocksDlg 消息处理程序 BOOL CelsblocksDlg::OnInitDialog() { CDialog::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 ShowWindow(SW_NORMAL); // TODO: 在此添加额外的初始化代码 SetWindowPos(GetDlgItem(IDD_ELSBLOCKS_DIALOG), 0, 0,BLOCK_X*CELL_LEN+4*CELL_LEN, BLOCK_Y*CELL_LEN+CELL_LEN+SCELL_LEN-5, SWP_NOZORDER | SWP_NOMOVE); / / /模子 / memset(m_Model,0,sizeof(m_Model)); /** /** /** /***** /***** m_Model[0][2][1]=1; m_Model[0][3][1]=1; m_Model[0][3][2]=1; m_Model[0][3][3]=1; /**** m_Model[1][3][0]=1; m_Model[1][3][1]=1; m_Model[1][3][2]=1; m_Model[1][3][3]=1; //**** //**** //**** m_Model[2][2][2]=1; m_Model[2][2][3]=1; m_Model[2][3][2]=1; m_Model[2][3][3]=1; //** //**** ** m_Model[3][2][1]=1; m_Model[3][2][2]=1; m_Model[3][3][2]=1; m_Model[3][3][3]=1; ** //**** //** m_Model[4][3][1]=1; m_Model[4][3][2]=1; m_Model[4][2][2]=1; m_Model[4][2][3]=1; //** ****** m_Model[5][1][2]=1; m_Model[5][2][1]=1; m_Model[5][2][2]=1; m_Model[5][2][3]=1; ** ** ** /***** /***** m_Model[6][3][1]=1; m_Model[6][3][2]=1; m_Model[6][3][3]=1; m_Model[6][2][3]=1; ///转换对应位置 m_MoldeChange[0][0]=CPoint(0,0); m_MoldeChange[0][1]=CPoint(1,0); m_MoldeChange[0][2]=CPoint(2,0); m_MoldeChange[0][3]=CPoint(3,0); m_MoldeChange[1][0]=CPoint(0,3); m_MoldeChange[1][1]=CPoint(1,3); m_MoldeChange[1][2]=CPoint(2,3); m_MoldeChange[1][3]=CPoint(3,3); m_MoldeChange[2][0]=CPoint(0,2); m_MoldeChange[2][1]=CPoint(1,2); m_MoldeChange[2][2]=CPoint(2,2); m_MoldeChange[2][3]=CPoint(3,2); m_MoldeChange[3][0]=CPoint(0,1); m_MoldeChange[3][1]=CPoint(1,1); m_MoldeChange[3][2]=CPoint(2,1); m_MoldeChange[3][3]=CPoint(3,1); memset(m_BlockShow,0,sizeof(m_BlockShow)); memset(m_BlockNext,0,sizeof(m_BlockNext)); memset(m_Map,0,sizeof(m_Map)); m_IsStart=false; m_level=1; m_Score=0; //MessageBox(LPCTSTR("sdfs"),0,0); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CelsblocksDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CelsblocksDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CPaintDC dc(this); // 用于绘制的设备上下文 //dc.TextOutW(BLOCK_X*CELL_LEN+CELL_LEN,CELL_LEN,CString("下一个")); //画出下一个Block DrawNextBlock(); UpdateAll(); GameResule(); CDialog::OnPaint(); } } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CelsblocksDlg::OnQueryDragIcon() { return static_cast(m_hIcon); } void CelsblocksDlg::NextBlock() { //把m_BlockNext复制给m_BlockShow for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { m_BlockShow[i][j]=m_BlockNext[i][j]; } } srand(time(NULL)); //time(NULL)返回自1/1/1970 0:00 记时 单位为秒 int index = rand()%7; //[0,nMax)为你想要的整数范围 for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { m_BlockNext[i][j]=m_Model[index][i][j]; } } //旋转得到的Block模型 srand(time(NULL)); //time(NULL)返回自1/1/1970 0:00 记时 单位为秒 for(int count = rand()%4;count>0;count--) { ChangeBlock(m_BlockNext,false); } //初始化坐标 m_CurrPosi.x=(BLOCK_X/2-2); m_CurrPosi.y=-2; //Invalidate(); //清理下一个的视窗 DrawNextBlock(); //memcpy(m_Model[index],Block,sizeof(Block)); } void CelsblocksDlg::DrawNextBlock() { for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { if(m_BlockNext[i][j]) { DrawCell(CRect(BLOCK_X*CELL_LEN+i*SCELL_LEN+SCELL_LEN/2,2*CELL_LEN+j*SCELL_LEN,BLOCK_X*CELL_LEN+i*SCELL_LEN+SCELL_LEN/2+SCELL_LEN,2*CELL_LEN+j*SCELL_LEN+SCELL_LEN)); //DrawBlock(CPoint(BLOCK_X,1),m_BlockNext); } else {//清理 InvalidateRect(CRect(BLOCK_X*CELL_LEN+i*SCELL_LEN+SCELL_LEN/2,2*CELL_LEN+j*SCELL_LEN,BLOCK_X*CELL_LEN+i*SCELL_LEN+SCELL_LEN+SCELL_LEN/2,2*CELL_LEN+j*SCELL_LEN+SCELL_LEN)); } } } } void CelsblocksDlg::DrawCell(CRect rect) { CDC *pDC=GetDC(); //CPen *pGreenPen=new CPen(PS_SOLID,2,RGB(0,255,0)); //CGdiObject* pOldBrush=pDC->SelectObject(pGreenPen); CBrush brush; brush.CreateSolidBrush(RGB(255,0,0)); CBrush *pOldBrush = pDC->SelectObject(&brush); pDC->Rectangle(&rect); pDC->SelectObject(pOldBrush); } void CelsblocksDlg::ChangeBlock(bool Block[4][4],bool flag) { //得到转换后的Block bool temp[4][4]; memset(temp,0,sizeof(temp)); for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { if(Block[i][j]) { temp[m_MoldeChange[i][j].x][m_MoldeChange[i][j].y]=1; } } } if(flag==true) {//当是在游戏区时 for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { if(temp[i][j]) { if(m_CurrPosi.x+i<0||m_CurrPosi.x+i>=BLOCK_X||m_CurrPosi.y+j>=BLOCK_Y) {//撞到边界 return; } else if(m_Map[m_CurrPosi.x+i][m_CurrPosi.y+j]!=0) {//撞到其他版块 return; } }//if }//for }//for UpdateAll(); } //可以转动 //复制装置后的Block memset(Block,0,sizeof(Block)); for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { Block[i][j]=temp[i][j]; } } } void CelsblocksDlg::GameStart() { memset(m_Map,0,sizeof(m_Map)); NextBlock(); NextBlock(); SetTimer(1,800-m_level*80,NULL); m_IsPause=false; //GameResule(); } //flag代表的是移动的方向 bool CelsblocksDlg::CanMoveLR(int flag) { for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { if(m_BlockShow[i][j]) { if(m_CurrPosi.x+i+flag<0||m_CurrPosi.x+i+flag>BLOCK_X-1) {//越出边界 return false; } else if(m_CurrPosi.x+i+flag>=0&&m_CurrPosi.x+i+flag=BLOCK_Y-1) {//到底部 return false; } else if(m_Map[m_CurrPosi.x+i][m_CurrPosi.y+j+1]!=0) {//向下撞到其他版块 return false; } } } } return true; } void CelsblocksDlg::GoDown() { if(CanMoveDown()) { m_CurrPosi.y++; UpdateAll(); //InvalidateRect(&CRect(0,0,BLOCK_X*CELL_LEN,BLOCK_Y*CELL_LEN)); //DrawBlock(m_CurrPosi,m_BlockShow); } else {//不能下移 for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { if(m_BlockShow[i][j]) { m_Map[m_CurrPosi.x+i][m_CurrPosi.y+j]=1; } } } DeletLines(); //是否游戏结束 for(int i=0;imessage) { case WM_KEYDOWN: switch (pMsg->wParam) { case VK_UP: ChangeBlock(m_BlockShow,true); //InvalidateRect(CRect(m_CurrPosi.x*CELL_LEN,m_CurrPosi.y*CELL_LEN,m_CurrPosi.x*CELL_LEN+4*CELL_LEN,m_CurrPosi.y*CELL_LEN+4*CELL_LEN)); break; case VK_LEFT: GoLeft(); break; case VK_RIGHT: GoRight(); break; case VK_DOWN: GoDown(); break; default: OnGamePause(); break; bHandleNow = TRUE; break; } if (bHandleNow) { OnKeyDown((UINT)pMsg->wParam, LOWORD(pMsg->lParam), HIWORD(pMsg->lParam)); } break; } } return bHandleNow; } void CelsblocksDlg::DeletLines() { int LinesNum=0; //消行数 for(int i=0;i=0;j--) {//行 for(int k=0;k0) { MessageBeep(MB_OK); m_Score+=LinesNum+LinesNum-1; //TCHAR score[10]; //wsprintf(score,"%d",m_Score); if(m_Score>=m_level*20) { KillTimer(1); m_level++; SetTimer(1,800-m_level*100,NULL); } } } //刷新游戏区 void CelsblocksDlg::UpdateAll() { CDC*pDC=GetDC(); CDC dcMem; //用于缓冲作图的内存DC CBitmap bmp; //内存中承载临时图象的位图 dcMem.CreateCompatibleDC(NULL); //依附窗口DC创建兼容内存DC (就是一个内存DC,所有图形先画在上面) bmp.CreateCompatibleBitmap(pDC,BLOCK_X*CELL_LEN,BLOCK_Y*CELL_LEN); //创建兼容位图 dcMem.SelectObject(&bmp); //将位图选择进内存DC dcMem.FillSolidRect(&CRect(0,0,BLOCK_X*CELL_LEN,BLOCK_Y*CELL_LEN),RGB(255, 255, 255)); //画背景 //画出落下的Block for(int i=0;i=i&&i=j&&jBitBlt(0,0,BLOCK_X*CELL_LEN,BLOCK_Y*CELL_LEN,&dcMem,0,0,SRCCOPY); //将内存DC上的图象拷贝到前台 ReleaseDC(pDC); } //关于链接到外网 void CAboutDlg::OnBnClickedOk() { // TODO: 在此添加控件通知处理程序代码 ShellExecute(NULL,CString("open"),CString("http://hi.csdn.net/space-8149237.html"),NULL,NULL,SW_SHOWNORMAL); this->SendMessage(WM_CLOSE); OnOK(); } void CelsblocksDlg::OnAbout() { // TODO: 在此添加命令处理程序代码 CAboutDlg aboutDlg; aboutDlg.DoModal(); } void CelsblocksDlg::GameResule() { //CClientDC dc(this); //CPaintDC dc(this); // 用于绘制的设备上下文 //LPTSTR scoreBuffer=new TCHAR[20]; //LPTSTR levelBuffer=new TCHAR[20]; wchar_t ScoreBuff[255]; wchar_t LevelBuff[255]; wsprintf(ScoreBuff,L"得分:%d",m_Score*100); //dc.TextOutW(BLOCK_X*CELL_LEN+CELL_LEN,10*CELL_LEN,ScoreBuff); wsprintf(LevelBuff,L"等级:%d",m_level); //dc.TextOutW(BLOCK_X*CELL_LEN+CELL_LEN,8*CELL_LEN,LevelBuff); SetDlgItemText(IDC_EDITLEVEL,LevelBuff); SetDlgItemText(IDC_EDITSCORE,ScoreBuff); } 菜单栏响应开始 void CelsblocksDlg::OnGameStart() { // TODO: 在此添加命令处理程序代码 m_IsStart=true; m_level=1; m_Score=0; GameStart(); UpdateAll(); } void CelsblocksDlg::OnGamePause() { // TODO: 在此添加命令处理程序代码 if(!m_IsStart)return; if(!m_IsPause) { KillTimer(1); m_IsPause=true; } else { SetTimer(1,800-m_level*80,NULL); m_IsPause=false; } }

这个程序的思路是这样的:

用以个矩阵来存储游戏区,游戏区中已经落下的标记为1其余清0

当然还有一个活动的 方块 ,这个方块我没有放到地图矩阵中来

在画图时,我开始尝试的是整体刷新( Invalidate

发现闪的厉害,我就改用局部刷新( InvalidateRect)。

我尝试了,当把方块画的小于格子,局部刷新闪屏好一些

但我在这个程序的实现过程中并没有全部用上面的方法,因为效果还是不好,总有闪屏

最重要的是很难控制,出现内存错误了!!!

建议在画动态图时还是使用双缓冲好

什么是双缓冲呢?

就是把地图先画成图,然后再把图插入到界面。

 

还有个是哪个变换的问题,我是使用了一个变换矩阵来转换坐标(如果能计算的方法也不错,本人愚笨,当时怎么算都没算出来,不过我还是找到了变换的公式了,呵呵)

 

在写键盘按键反应时,在DLG类中不能用OnKeyDown

而应用PreTranslateMessage来截取键盘消息

不过不知道何时出现一个奇怪现象,我本想用空格来暂停游戏的,结果一按就退出游戏了,不解。。。

你可能感兴趣的:(俄罗斯方块(MFC))