近期尝试了一下俄罗斯方块游戏,基本功能和界面的设计已经初步完成。分界面和功能实现两个模块简要地介绍一下实现过程。
一 界面设计与实现
界面示意图如图1-1所示,主要实现了界面以任意比例拉伸或缩小,界面中控件的位置和大小相对位置保持不变,图1-2是界面最大化显示的效果。
图1-1设计效果图
图1-2界面设计最大化示意图
界面中的所有控件 包括Button RadioButton ListCtrl Combox 等控件均为动态生成,位置大小需要简单地数学计算获取。关于控件,已RadioButton为例进行说明它的创建和位置确定的实现过程,其他的控件参考附录中界面部分完整的实现代码以及注释。游戏区域界面的背景绘制的实现也将做一个简要的说明。
1 控件的创建以及位置的确定,响应消息等等
以RadioButton为例,首先在View类的头文件中声明这两个对象,
//游戏类型选项
CButton m_radioSingle;//单人游戏选项 CButton m_radioDouble;//双人游戏选项
添加标识选中哪一个的变量,含义参考注释
int m_iRadio;//默认为1 1单人游戏 2双人游戏
然后在OnCreate函数中创建这两个对象,
int CTerisView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) { return -1; } m_radioSingle.Create(_T("单人游戏"),WS_EX_TRANSPARENT|WS_CHILD|WS_VISIBLE|BS_AUTORADIOBUTTON, CRect(0,0,0,0), this, ID_RADIOBUTTON_SINGLE); m_radioSingle.SetCheck(ID_RADIOBUTTON_SINGLE);//设置默认项 m_radioDouble.Create(_T("双人游戏"),WS_EX_TRANSPARENT|WS_CHILD|WS_VISIBLE|BS_AUTORADIOBUTTON, CRect(0,0,0,0), this, ID_RADIOBUTTON_DOUBLE); return 0; }
这里只是创建了两个RadioButton对象,他们的位置并没有确定,下一步还需要确定他们在界面中的位置。这个位置要求满足界面大小改变时它们在界面中的相对位置保持不变。实现这一效果需要用到OnSize消息,详细信息可以参考前面的那一篇博文:
MFC控件和背景图片自适应窗口最大化和拉伸缩放的实现
在OnSize函数中添加下面的代码:
void CTerisView::OnSize(UINT nType, int cx, int cy) { //cx 指定工作区的新的宽度 //cy 指定工作区的新的高度 //计算控件的位置 m_radioSingle.MoveWindow(4*cx/5,91,100,30); m_radioDouble.MoveWindow(4*cx/5,140,100,30); CView::OnSize(nType, cx, cy); }
4*cx/5:cx 指定工作区的新的宽度,当窗口大小改变时可以实时获取到这个值,因为游戏区宽度设计的是占整个边框的2/3,因此确定这个按钮的左边界为整个界面宽度的4/5;
91:50(空白区高度)+30(标题区)+20(距离标题区的高度);
100:按钮的宽度;
30:按钮的高度。
这样,这两个按钮的在界面中的大小和相对位置以确定。后面是消息的响应。
在SDI中给按钮添加消息响应函数详细信息可以参考前面的博文:
SDI在视图类中添加按钮以及消息响应
具体地在附录中可以参考详细的代码实现和注释。
只是在消息响应函数中给前面声明的那个标识变量m_iRadio分别赋值,当为1的时候可以响应单击游戏,当为2时,响应双人游戏。
2 游戏区背景框的绘制
这一部分主要是在OnDraw函数中进行
//获取主窗口的大小 CRect rect; GetWindowRect(&rect); //游戏区域绘制 背景及其他 CPen pen_gamearea; CPen*myoldpen_gamearea; //创建蓝色的画笔 pen_gamearea.CreatePen(PS_SOLID,3,RGB(0,0,255)); myoldpen_gamearea=pDC->SelectObject(&pen_gamearea); pDC->MoveTo(0,rect.Height()-20);//到左下顶点 左侧边线和左边框重合 不绘制边线 pDC->LineTo(rect.Width()*2/3,rect.Height()-20); //底部边线 pDC->LineTo(rect.Width()*2/3,51); //右部边线 //方块游戏区域 CRect rect_gamearea(0,50,rect.Width()*2/3,rect.Height()-20); CBrush mybrush_gamearea; //创建画刷 绿色 mybrush_gamearea.CreateSolidBrush(RGB(0,222,0)); //绘制背景 pDC->FillRect(rect_gamearea,&mybrush_gamearea); pDC->SelectObject(myoldpen_gamearea); //底部的空白部分添加背景bottom //底部区域 //CRect的四个参数分别为 l t r b CRect rect_bottomarea(0,rect.Height()-18,rect.Width(),rect.Height()); CBrush mybrush_bottomarea; //创建画刷 mybrush_bottomarea.CreateSolidBrush(RGB(200,222,0)); //绘制背景 pDC->FillRect(rect_bottomarea,&mybrush_bottomarea); //游戏进行区域 //下一个方块区域 70=50+20 CRect rect_nextBoxarea(rect.Width()/3-60,70,rect.Width()/3+60,170);// CBrush mybrush_nextBoxarea; //创建画刷 mybrush_nextBoxarea.CreateSolidBrush(RGB(0,0,0)); //绘制背景 pDC->FillRect(rect_nextBoxarea,&mybrush_nextBoxarea); //绘制红色边线 //游戏区域绘制 背景及其他 CPen pen_nextbox; CPen*myoldpen_nextbox; //创建蓝色的画笔 pen_nextbox.CreatePen(PS_SOLID,2,RGB(200,0,0)); myoldpen_nextbox=pDC->SelectObject(&pen_nextbox); pDC->MoveTo(rect.Width()/3-60,70);//到左下顶点 左侧边线和左边框重合 不绘制边线 pDC->LineTo(rect.Width()/3-60,170); //zuo部边线 pDC->LineTo(rect.Width()/3+60,170); //di部边线 pDC->LineTo(rect.Width()/3+60,70); //右部边线 pDC->LineTo(rect.Width()/3-60,70); //上部边线 //游戏进行区 CRect rect_mainGameareaSingle(rect.Width()/6,200,rect.Width()/2,rect.Height()-21);// CBrush mybrush_mainGameareaSingle; //创建画刷 mybrush_mainGameareaSingle.CreateSolidBrush(RGB(0,0,0)); //绘制背景 pDC->FillRect(rect_mainGameareaSingle,&mybrush_mainGameareaSingle); //绘制红色边线 //游戏区域绘制 背景及其他 CPen pen_GameareaSingle; CPen*myoldpen_GameareaSingle; //创建蓝色的画笔 pen_GameareaSingle.CreatePen(PS_SOLID,2,RGB(200,0,0)); myoldpen_GameareaSingle=pDC->SelectObject(&pen_GameareaSingle); pDC->MoveTo(rect.Width()/6,200);//到左下顶点 左侧边线和左边框重合 不绘制边线 pDC->LineTo(rect.Width()/6,rect.Height()-21); //zuo部边线 pDC->LineTo(rect.Width()/2,rect.Height()-21); //di部边线 pDC->LineTo(rect.Width()/2,200); //右部边线 pDC->LineTo(rect.Width()/6,200); //shang部边线 //基本计分区域 120=20*6 CRect rect_Scorearea(rect.Width()/2+10,rect.Height()-21-125,rect.Width()/2+10+100/*rect.Width()/2+10+rect.Width()/8*/,rect.Height()-30); CBrush mybrush_Scorearea; //创建画刷 mybrush_Scorearea.CreateSolidBrush(RGB(255,182,193)); //绘制背景 pDC->FillRect(rect_Scorearea,&mybrush_Scorearea); //输出块数 //添加此行代码避免文字背景色破坏了图像 背景遮盖 pDC->SetBkMode(TRANSPARENT); //添加文字 pDC->TextOut(rect.Width()/2+10+30,rect.Height()-21-123,_T("块数")); CString str1=_T("10"); pDC->SetTextColor(RGB(255,0,0)); //设置数值字体颜色 pDC->TextOut(rect.Width()/2+10+30+5,rect.Height()-21-103,str1); pDC->SetTextColor(RGB(0,0,0)); //设置字体颜色黑色 pDC->TextOut(rect.Width()/2+10+30,rect.Height()-21-83,_T("分数")); CString str2=_T("20"); pDC->SetTextColor(RGB(0,255,0)); //设置数值字体颜色 pDC->TextOut(rect.Width()/2+10+30+5,rect.Height()-21-63,str2); pDC->SetTextColor(RGB(0,0,0)); //设置字体颜色黑色 pDC->TextOut(rect.Width()/2+10+30,rect.Height()-21-43,_T("行数")); CString str3=_T("30"); pDC->SetTextColor(RGB(0,0,255)); //设置数值字体颜色 pDC->TextOut(rect.Width()/2+10+30+5,rect.Height()-21-24,str3); pDC->SetTextColor(RGB(0,0,0)); //设置字体颜色黑色
注意:这里的块数、分数和行数是动态变化的,程序的实现部分将会将些值实时地传递给相应的变量并显示出来。
界面实现源码下载地址:http://download.csdn.net/detail/shufac/7353939