社区
GAME,图形处理/多媒体
帖子详情
StretchBlt,BitBlt函数好象不可以绘制JPEG格式文件,那么要在指定的DC和RECT上按伸展、居中、平铺方式画图怎么办?
yczyk
2003-11-13 10:37:25
StretchBlt,BitBlt函数好象只可以绘BMP格式的文件,可现在我需要按上面说的三种方式绘JPEG文件啊,请大家帮忙!
...全文
42
8
打赏
收藏
StretchBlt,BitBlt函数好象不可以绘制JPEG格式文件,那么要在指定的DC和RECT上按伸展、居中、平铺方式画图怎么办?
StretchBlt,BitBlt函数好象只可以绘BMP格式的文件,可现在我需要按上面说的三种方式绘JPEG文件啊,请大家帮忙!
复制链接
扫一扫
分享
转发到动态
举报
写回复
配置赞助广告
用AI写文章
8 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
SydPink
2004-02-11
打赏
举报
回复
我还没工作呢。。图形图象方面菜着呢。。。知道 MoveTo,LIneTo.哈哈。
zilchcsdn
2004-02-10
打赏
举报
回复
SydPink(呜噜~呜噜) 一直在做图像方面的工作阿:)
SydPink
2004-01-23
打赏
举报
回复
俺处理图象都是变成BITMAP来进行中间操作.然后是什么格式再变成什么格式保存回去的.BITMAP处理起来多简单啊..直接处理JPEG.俺就不会了.不懂它的数据格式.
XPathHowToQueryLove
2004-01-08
打赏
举报
回复
主 题: ADO主从表删除问题,请教各位。。。
作 者: yczyk (有鬼:泪眼问花花不语,乱红飞过千秋去)
等 级:
信 誉 值: 98
所属论坛: Delphi 基础类
问题点数: 50
回复次数: 4
发表时间: 2003-10-13 11:02:50
-------------------------------------------
原来兄弟你是DELPHI区的呀,哈哈。
你在ASP.NET的那些帖子什么时候结呀~~~在技术区的那么多帖子什么时候结呀~~·
我昏。
---------------------------------------------
主 题: StretchBlt,BitBlt函数好象不可以绘制JPEG格式文件,那么要在指定的DC和RECT上按伸展、居中、平铺方式画图怎么办?
作 者: yczyk (有鬼:泪眼问花花不语,乱红飞过千秋去)
等 级:
信 誉 值: 98
所属论坛: Delphi 图形处理/多媒体
问题点数: 50
回复次数: 3
发表时间: 2003-11-13 10:30:48
回复人: yczyk(有鬼:泪眼问花花不语,乱红飞过千秋去) ( ) 信誉:98 2003-11-13 13:18:00 得分:0
NND,没有人回答啊?自己顶吧! //如此回答真是经典也~~
-------------------------------------------
嘿嘿,偶是个小斑竹,所以,找人比较容易。
说来,偶也是无聊ING。
XPathHowToQueryLove
2004-01-08
打赏
举报
回复
主 题: ADO主从表删除问题,请教各位。。。
作 者: yczyk (有鬼:泪眼问花花不语,乱红飞过千秋去)
等 级:
信 誉 值: 98
所属论坛: Delphi 基础类
问题点数: 50
回复次数: 4
发表时间: 2003-10-13 11:02:50
CareYouOnly
2003-11-14
打赏
举报
回复
同意!
fj218
2003-11-13
打赏
举报
回复
可以呀。你在单元文件里uses jpeg单元,就可以使用TJpegImage类,就可以读入Jpeg图像了。然后把Tjpegimage赋值到Tbitmap,不就可以使用StretchBlt,BitBlt函数了。
yczyk
2003-11-13
打赏
举报
回复
NND,没有人回答啊?自己顶吧!
C#中使用双缓冲以及
Bit
Blt
提高GDI+绘图效率的示例程序
利用GDI+在Form中
绘制
60*60个圆点,显示帧速,使用三种不同方法,分别是: 1.直接
绘制
2.使用双缓冲 3.使用
Bit
Blt
函数
对比三种
函数
的帧速,以及提速效果 开发环境为vs2008
VB
St
re
tch
Blt
函数
实现图片旋转、放大、缩小
VB
St
re
tch
Blt
函数
实现图片旋转、放大、缩小
2、4、8、16、24、32位图解析显示
在VC中,位图显示一般有现成的
方式
,如使用picture控件、Get
DC
()->
St
re
tch
Blt
、::
Bit
Blt
等,但这些
方式
都是高层的封装,让你不清楚一副位图是如何解析并显示到
DC
上的。实际应用中,比如图像处理,视频显示等,需要操作到位图中的像素,这需要弄明白位图文件如何组成,网上有太多的位图文件格式说明,下面借助实例和SetPixel
函数
完成解析与显示。 文档说明:http://blog.csdn.net/dijk
st
ar/article/details/20854709
VC之美化界面篇本文专题讨论VC中的界面美化,适用于具有中等VC水平的读者。读者最好具有以下VC基础:
VC之美化界面篇 作者:白乔 链接:http://vcer.net/1046595482643.html 本文专题讨论VC中的界面美化,适用于具有中等VC水平的读者。读者最好具有以下VC基础: 1. 大致了解MFC框架的基本运作原理; 2. 熟悉Windows消息机制,熟悉MFC的消息映射和反射机制; 3. 熟悉OOP理论和技术; 本文根据笔者多年的开发经验,并结合简单的例子一一展开,希望对读者有所帮助。 1 美化界面之开题篇 相信使用过《金山毒霸》、《瑞星杀毒》软件的读者应该还记得它们的精美界面: 图1 瑞星杀毒软件的精美界面 程序的功能如何如何强大是一回事,它的用户界面则是另一回事。千万不要忽视程序的用户界面,因为它是给用户最初最直接的印象,丑陋的界面、不友好的风格肯定会影响用户对软件程序的使用。 “受之以鱼,不若授之以渔”,本教程并不会向你推荐《瑞星杀毒软件》精美界面的具体实现,而只是向你推荐一些常用的美化方法。 2 美化界面之基础篇 美化界面需要先熟悉Windows下的绘图操作,并明白Windows的幕后绘图操作,才能有的放矢,知道哪些可以使用,知道哪些可以避免…… 2.1 Windows下的绘图操作 熟悉DOS的读者可能就知道:DOS下面的图形操作很方便,进入图形模式,整个屏幕就是你的了,你希望在哪画个点,那个地方就会出现一个点,红的、或者黄的,随你的便。你也可以花点时间画个按钮,画个你自己的菜单,等等…… Windows本身就是图形界面,所以Windows下面的绘图操作功能更丰富、简单。要了解Windows下的绘图操作,要实现Windows界面的美化,就必须了解MFC封装的设备环境类和图形对象类。 2.1.1 设备环境类 Windows下的绘图操作说到底就是
DC
操作。
DC
(Device Context设备环境)对象是一个抽象的作图环境,可能是对应屏幕,也可能是对应打印机或其它。这个环境是设备无关的,所以你在对不同的设备输出时只需要使用不同的设备环境就行了,而作图
方式
可以完全不变。这也就是Windows的设备无关性。 MFC的C
DC
类封装了Windows API 中大部分的
画图
函数
。C
DC
的常见操作
函数
包括: Drawing-Attribute Functions:绘图属性操作,如:设置透明模式 Mapping Functions:映射操作 Coordinate Functions:坐标操作 Clipping Functions:剪切操作 Line-Output Functions:画线操作 Simple Drawing Functions:简单绘图操作,如:
绘制
矩形框 Ellipse and Polygon Functions:椭圆/多边形操作 Text Functions:文字输出操作 Printer Escape Functions:打印操作 Scrolling Functions:滚动操作 *
Bit
map Functions:位图操作 *Region Functions:区域操作 *Font Functions:字体操作 *Color and Color Palette Functions:颜色/调色板操作 其中,标注*项会用到相应的图形对象类,参见2.1.2内容。 2.1.2 图形对象类 设备环境不足以包含绘图功能所需的所有绘图特征,除了设备环境外, Windows还有其他一些图形对象用来储存绘图特征。这些附加的功能包括从画线的宽度和颜色到画文本时所用的字体。图形对象类封装了所有六个图形对象。 下面的表格列出了MFC的图形对象类: MFC类 图形对象句柄 图形对象目的 C
Bit
map H
BIT
MAP 内存中的位图 CBrush HBRUSH 画刷特性—填充某个图形时所使用的颜色和模式 CFont HFONT 字体特性—写文本时所使用的字体 CPalette HPALETTE 调色板颜色 CPen HPEN 画笔特性—画轮廓时所使用的线的粗细 CRgn HRGN 区域特性—包括定义它的点 表1 图形对象类和它们封装的句柄 使用C
DC
和图形对象类,在Windows里绘图还算是很简单的。观察以下的画面: 图2 使用C
DC
绘制
出的按钮 该画面通过以下代码自行
绘制
的假按钮: BOOL CUi1View::PreCreateWindow(CREATE
ST
RUCT& cs) { //设置背景色 //CBrush CUi1View::m_Back m_Back.CreateSolidBrush(::GetSysColor(COLOR_3DFACE)); cs.lpszClass = AfxRegi
st
erWn
dC
lass(0, 0, m_Back, NULL); return CView::PreCreateWindow(cs); } int CUi1View::OnCreate(LPCREATE
ST
RUCT lpCreate
St
ruct) { if (CView::OnCreate(lpCreate
St
ruct) == -1) return -1; //创建字体 //CFont CUi1View::m_Font m_Font.CreatePointFont(120, "Impact"); return 0; } void CUi1View::OnDraw(C
DC
* p
DC
) { //
绘制
按钮框架 p
DC
->DrawFrameControl(C
Rect
(100, 100, 220, 160), DFC_BUTTON, DFCS_BUTTONPUSH); //输出文字 p
DC
->SetBkMode(TRANSPARENT); p
DC
->TextOut(120, 120, "Hello, CFan!"); } 呵呵,不好意思,这并不是真的Windows按钮,它只是一个假的空框子,当用户在按钮上点击鼠标时,放心,什么事情都不会发生。 2.2 Windows的幕后绘图操作 在Window中,如果所有的界面操作都由用户代码来实现,那将是一个很浩大的工程。笔者曾经在DOS设计过窗口图形界面,代码上千行,但实现的界面还是很古板、难看,除了我那个对编程一窍不通的女友,没有一个人欣赏它L;而且,更要命的是,操作系统,包括别的应用程序并不认识你的界面元素,这才是真正悲哀的。认识这些界面的只有你的程序,图2中的按钮永远只是一个无用的框子。 有了Windows,一切都好办了,Windows将诸如按钮、菜单、工具栏等等这些通用界面的
绘制
及动作都交给了系统,程序员就不用花心思再画那些按钮了,可以将更多的精力放在程序的功能实现方面。 所有的标准界面元素都被Windows封装好了。Windows知道怎么画你的菜单以及你的标注着“Hello, Cfan!”的按钮。当CFan某个快乐的小编(譬如:小飞)点击这个按钮的时候,Windows也明白按钮按下去的时候该有的模样,甚至,当这个友好的按钮获取焦点时,Windows也会不失时机地为它准备一个虚框…… 有利必有弊。你的不满这时候产生了:你既想使用Windows的True Button,可也嫌它的界面不够好看,譬如,你喜欢用蓝色的粗体表达你对CFan的无限情怀(正如图2那样)——人心不足,有办法吗?有的。 3 美化界面之实现篇 Windows还是给程序员留下了很多后门,通过一些途径还是可以美化界面的。本章节我们系统学习一下Windows界面美化的实现。 3.1 美化界面的途径 如何以合法的手段来达到美化界面的效果?一般美化界面的方法包括: 1. 使用MFC类的既有
函数
,设定界面属性; 2. 利用Windows的消息机制,截获有用的Windows的消息。通过MFC的消息映射(Message Mapping)和反射(Message Reflecting)机制,在Windows准备或者正在
绘制
该元素时,偷偷修改它的状态和行为,譬如:让按钮的边框为红色; 3. 利用MFC类的虚
函数
机制,重载有用的虚
函数
。在MFC框架调用该
函数
的时候,重新定义它的状态和行为; 一般来说,应用程序可以通过以下两种途径来实现以上的方法: 1. 在父窗口里,截获自身的或者由子元素(包括控件和菜单等元素)传递的关于界面
绘制
的消息; 2. 子类化子元素,或者为子元素准备一个新的类(一般来说该类必须继承于MFC封装的某个标准类,如:CButton)。在该子元素里,截获自身的或者从父窗口反射过来的关于界面
绘制
的消息。譬如:用户可以创建一个CXPButton类来实现具有XP风格的按钮,CXPButton继承于CButton。 对于应用程序,使用CXPButton类的途径相对于对话框窗口和普通窗口分成两种: ① 对话框窗口中,直接将原先绑定按钮的CButton类替换成CXPButton类,或者在绑定变量时直接
指定
Control类型为CXPButton,如图3所示: 图3 为按钮
指定
CXPButton类型 ②在普通窗口中,直接创建一个CXPButton类对象,然后在OnCreate()中调用CXPButton的Create方法; 以下的章节将综合地使用以上的方法,请读者朋友留心观察。 3.2 使用MFC类的既有
函数
在界面美化的专题中,MFC也并非一无是处。MFC类对于界面美化也做了部分的努力,以下是一些可以使用的,参数说明略去。 CWinApp::SetDialogBkColor void SetDialogBkColor( COLORREF clrCtlBk = RGB(192, 192, 192), COLORREF clrCtlText = RGB(0, 0, 0) );
指定
对话框的背景色和文本颜色。 CLi
st
Ctrl::SetBkColor CReBarCtrl::SetBkColor C
St
atusBarCtrl::SetBkColor CTreeCtrl::SetBkColor COLORREF SetBkColor( COLORREF clr ); 设定背景色。 CLi
st
Ctrl::SetTextColor CReBarCtrl::SetTextColor CTreeCtrl::SetTextColor COLORREF SetTextColor( COLORREF clr ); 设定文本颜色。 CLi
st
Ctrl::SetBkImage BOOL SetBkImage( LVBKIMAGE* plvbkImage ); BOOL SetBkImage( H
BIT
MAP hbm, BOOL fTile = TRUE, int xOffsetPercent = 0, int yOffsetPercent = 0); BOOL SetBkImage( LPT
ST
R pszUrl, BOOL fTile = TRUE, int xOffsetPercent = 0, int yOffsetPercent = 0 ); 设定列表控件的背景图片。 CComboBoxEx::SetExtended
St
yle CLi
st
Ctrl::SetExtended
St
yle CTabCtrl::SetExtended
St
yle CToolBarCtrl::SetExtended
St
yle DWORD SetExtended
St
yle( DWORD dwExMask, DWORD dwEx
St
yles ); 设置控件的扩展属性,例如:设置列表控件属性带有表格线。 图4是个简单应用MFC类的既有
函数
来改善Windows界面的例子: 图4 使用MFC类的既有
函数
美化界面 相关实现代码如下: BOOL CUi2App::InitIn
st
ance() { //… //设置对话框背景色和字体颜色 SetDialogBkColor(RGB(128, 192, 255), RGB(0, 0, 255)); //… } BOOL CUi2Dlg::OnInitDialog() { //… //设置列表控件属性带有表格线 DWORD New
St
yle = m_Li
st
.GetExtended
St
yle(); New
St
yle |= LVS_EX_GRIDLINES; m_Li
st
.SetExtended
St
yle(New
St
yle); //设置列表控件字体颜色为红色 m_Li
st
.SetTextColor(RGB(255, 0, 0)); //填充数据 m_Li
st
.InsertColumn(0, "QQ", LVCFMT_LEFT, 100); m_Li
st
.InsertColumn(1, "昵称", LVCFMT_LEFT, 100); m_Li
st
.InsertItem(0, "5854165"); m_Li
st
.SetItemText(0, 1, "白乔"); m_Li
st
.InsertItem(1, "6823864"); m_Li
st
.SetItemText(1, 1, "Satan"); //… } 嗯,这样的界面还算不错吧? 3.3 使用Windows的消息机制 使用MFC类的既有
函数
来美化界面,其功能是有限的。既然Windows是通过消息机制进行通讯的,那么我们就可以通过截获一些有用的消息来美化我们的界面,以下是一些有用的Windows消息: WM_PAINT WM_ERASEBKGND WM_CTLCOLOR* WM_DRAWITEM* WM_MEASUREITEM* NM_CU
ST
OMDRAW* 注意,标注*的消息是子元素发送给父窗口的通知消息,其它的为窗口或者子元素自身的消息。 3.3.1 WM_PAINT WM_PAINT消息相信大家都很熟悉,一个窗口要重绘了,就会有一个WM_PAINT消息发送给窗口。 可以响应窗口的WM_PAINT,以更改它们的模样。WM_PAINT的映射
函数
原型如下: afx_msg void OnPaint(); 控件也是窗口,所以控件也有WM_PAINT消息,通过消息映射我们完全可以定义控件的界面。如图5所示: 图5 利用WM_ PAINT消息美化界面 实现代码也很简单: void CLazy
St
atic::OnPaint() { CPaint
DC
dc
(this); // device context for painting //什么都不输出,仅仅画一个矩形框 C
Rect
rc; GetClient
Rect
(&rc);
dc
.
Rect
angle(rc); } 哈哈,简单吧?不过WM_PAINT确实绝了点,它要求应用程序完成元素界面的所有
绘制
过程,想象一下如何画出一个完整的列表控件?太烦了吧。一般来说,很少有人喜欢使用WM_PAINT,还有其它更细致的消息。 3.3.2 WM_ERASEBKGND Windows在向窗口发送WM_PAINT消息之前,总会发送一个WM_ERASEBKGND消息通知该窗口擦除背景,默认情况下,Windows将以窗口的背景色清除该窗口。 可以响应窗口(包括子元素)的WM_ERASEBKGND,以更改它们的背景。WM_ERASEBKGND的映射
函数
原型如下: afx_msg BOOL OnEraseBkgnd( C
DC
* p
DC
); 返回值:
指定
背景是否已清除,如果为FALSE,系统将自动清除 参数: p
DC
指定
了
绘制
操作所使用的设备环境。 图6是个简单的例子,通过OnEraseBkgnd为对话框加载了一副位图背景: 图6 利用WM_ ERASEBKGND消息美化界面 实现代码也很简单: BOOL CUi4Dlg::OnInitDialog() { //… //加载位图 //C
Bit
map m_Back; m_Back.Load
Bit
map(IDB_BACK); //… } BOOL CUi4Dlg::OnEraseBkgnd(C
DC
* p
DC
) { C
DC
dc
;
dc
.CreateCompatible
DC
(p
DC
);
dc
.SelectObject(&m_Back); //获取
BIT
MAP对象
BIT
MAP hb; m_Back.Get
Bit
map(&hb); //获取窗口大小 C
Rect
rt; GetClient
Rect
(&rt); //显示位图 p
DC
->
St
re
tch
Blt
(0, 0, rt.Width(), rt.Height(), &
dc
, 0, 0, hb.bmWidth, hb.bmHeight, SRCCOPY); return TRUE; } HBRUSH CUi4Dlg::OnCtlColor(C
DC
* p
DC
, CWnd* pWnd, UINT nCtlColor) { //设置透明背景模式 p
DC
->SetBkMode(TRANSPARENT); //设置背景刷子为空 return (HBRUSH)::Get
St
ockObject(HOLLOW_BRUSH); } 同时别忘了响应OnCtlColor,否则窗口里面的控件就不透明了。OnCtlColor的内容。 3.3.3 WM_CTLCOLOR 在控件显示之前,每一个控件都会向父对话框发送一个WM_CTLCOLOR消息要求获取
绘制
所需要的颜色。WM_CTLCOLOR消息缺省处理
函数
CWnd::OnCtlColor返回一个HBRUSH类型的句柄,这样,就可以设置前景和背景文本颜色,并为控件或者对话框的非文本区域选定一个刷子。 WM_CTLCOLOR的映射
函数
原型如下: afx_msg HBRUSH OnCtlColor( C
DC
* p
DC
, CWnd* pWnd, UINT nCtlColor ); 返回值: 用以
指定
背景的刷子 参数: p
DC
指定
了
绘制
操作所使用的设备环境。 pWnd 控件指针 nCtlColor
指定
控件类型,其取值如表2所示: 类型值 含义 CTLCOLOR_BTN 按钮控件 CTLCOLOR_DLG 对话框 CTLCOLOR_EDIT 编辑控件 CTLCOLOR_LI
ST
BOX 列表框 CTLCOLOR_MSGBOX 消息框 CTLCOLOR_SCROLLBAR 滚动条 CTLCOLOR_
ST
ATIC 静态控件 表2 nCtlColor的类型值与含义 作为一个简单的例子,观察以下的代码: BOOL CUi5Dlg::OnInitDialog() { //… //创建字体 //CFont CUi1View::m_Font1, CUi1View::m_Font2 m_Font1.CreatePointFont(120, "Impact"); m_Font3.CreatePointFont(120, "Arial"); return TRUE; // return TRUE unless you set the focus to a control } HBRUSH CUi5Dlg::OnCtlColor(C
DC
* p
DC
, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(p
DC
, pWnd, nCtlColor); if(nCtlColor == CTLCOLOR_
ST
ATIC) { //区分静态控件 switch(pWnd->GetDlgCtrlID()) { case I
DC
_
ST
ATIC1: { p
DC
->SelectObject(&m_Font1); p
DC
->SetTextColor(RGB(0, 0, 255)); break; } case I
DC
_
ST
ATIC2: { p
DC
->SelectObject(&m_Font2); p
DC
->SetTextColor(RGB(255, 0, 0)); break; } } } return hbr; } 生成的界面如下: 图7 利用WM_CTLCOLOR消息美化界面 3.3.4 WM_DRAWITEM OnCtlColor只能修改元素的颜色,但不能修改元素的界面框架,WM_DRAWITEM则可以。 当一个具有Owner draw风格的元素(包括按钮、组合框、列表框和菜单等)需要显示外观时,该元素会发送一条WM_DRAWITEM消息至它的隶属窗口(Owner)。 WM_DRAWITEM的映射
函数
原型如下: afx_msg void OnDrawItem( int nI
DC
tl, LPDRAWITEM
ST
RUCT lpDrawItem
St
ruct ); 参数: nI
DC
tl 该控件的ID,如果该元素为菜单,则nI
DC
tl为0 lpDrawItem
St
ruct 指向DRAWITEM
ST
RUCT结构对象的指针,DRAWITEM
ST
RUCT的结构定义如下: typedef
st
ruct tagDRAWITEM
ST
RUCT { UINT CtlType; UINT CtlID; UINT itemID; UINT itemAction; UINT item
St
ate; HWND hwndItem; H
DC
h
DC
;
RECT
rcItem; DWORD itemData; }DRAWITEM
ST
RUCT; CtlType
指定
了控件的类型,其取值如表3所示: 类型值 含义 ODT_BUTTON 按钮控件 ODT_COMBOBOX 组合框控件 ODT_LI
ST
BOX 列表框控件 ODT_LI
ST
VIEW 列表视图 ODT_MENU 菜单项 ODT_
ST
ATIC 静态文本控件 ODT_TAB Tab控件 表3 CtlType的类型值与含义 CtlID
指定
自绘控件的ID值,该成员不适用于菜单项 itemID表示菜单项ID,也可以表示列表框或者组合框中某项的索引值。对于一个空的列表框或组合框,该成员的值为?C1。这时应用程序只
绘制
焦点矩形(该矩形的坐标由rcItem 成员给出)虽然此时控件中没有需要显示的项,但是
绘制
焦点矩形还是很有必要的,因为这样做能够提示用户该控件是否具有输入焦点。当然也可以设置itemAction 成员为合适值,使得无需
绘制
焦点。 itemAction
指定
绘制
行为,其取值为表4中所示值的一个或者多个的联合: 类型值 含义 ODA_DRAWENTIRE 当整个控件都需要被
绘制
时,设置该值。 ODA_FOCUS 如果控件需要在获得或失去焦点时被
绘制
,则设置该值。此时应该检查item
St
ate成员,以确定控件是否具有输入焦点。 ODA_SELECT 如果控件需要在选中状态改变时被
绘制
,则设置该值。此时应该检查item
St
ate 成员,以确定控件是否处于选中状态。 表4 itemAction的类型值与含义 item
St
ate
指定
了当前
绘制
项的状态。例如,如果菜单项应该被灰色显示,则可以
指定
ODS_GRAYED状态标志。其取值为表5中所示值的一个或者多个的联合: 类型值 含义 ODS_CHECKED 标记状态,仅适用于菜单项。 ODS_DEFAULT 默认状态。 ODS_DISABLED 禁止状态。 ODS_FOCUS 焦点状态。 ODS_GRAYED 灰化状态,仅适用于菜单项。 ODS_SELECTED 选中状态。 ODS_HOTLIGHT 仅适用于Windows 98/Me/Windows 2000/XP,热点状态:如果鼠标指针位于控件之上,则设置该值,这时控件会显示高亮颜色。 ODS_INACTIVE 仅适用于Windows 98/Me/Windows 2000/XP,非激活状态。 ODS_NOACCEL 仅适用于Windows 2000/XP,控件是否有快速键。 ODS_COMBOBOXEDIT 在自绘组合框控件中只
绘制
选择区域。 ODS_NOFOCUS
RECT
仅适用于Windows 2000/XP,不
绘制
捕获焦点的效果。 表5 item
St
ate的类型值与含义 hwndItem
指定
了组合框、列表框和按钮等自绘控件的窗口句柄;如果自绘的对象为菜单项,则表示包含该菜单项的菜单句柄。 h
DC
指定
了
绘制
操作所使用的设备环境。 rcItem
指定
了将被
绘制
的矩形区域。这个矩形区域就是上面h
DC
的作用范围。系统会自动裁剪组合框、列表框或按钮等控件的自
绘制
区域以外的部分。也就是说rcItem中的坐标点(0,0)指的就是控件的左上角。但是系统不裁剪菜单项,所以在
绘制
菜单项的时候,必须先通过一定的换算得到该菜单项的位置,以保证
绘制
操作在我们希望的区域中进行。 itemData 对于菜单项,该成员的取值为由CMenu::AppendMenu、CMenu::InsertMenu、CMenu::ModifyMenu等
函数
传递给菜单的值。 对于列表框或这组合框,该成员的取值为由ComboBox::Add
St
ring、CComboBox::Insert
St
ring、CLi
st
Box::Add
St
ring或者CLi
st
Box::Insert
St
ring等
函数
传递给控件的值。 如果ctlType 的取值是ODT_BUTTON或者ODT_
ST
ATIC,itemData的取值为0。 图5是个相应的例子,它修改了按钮的界面: 图8 利用WM_DRAWITEM消息美化界面 实现代码如下: BOOL CUi6Dlg::OnInitDialog() { //… //创建字体 //CFont CUi1View::m_Font m_Font.CreatePointFont(120, "Impact"); //… } void CUi6Dlg::OnDrawItem(int nI
DC
tl, LPDRAWITEM
ST
RUCT lpDrawItem
St
ruct) { if(nI
DC
tl == I
DC
_HELLO_CFAN) { //
绘制
按钮框架 UINT u
St
yle = DFCS_BUTTONPUSH; //是否按下去了? if (lpDrawItem
St
ruct->item
St
ate & ODS_SELECTED) u
St
yle |= DFCS_PUSHED; C
DC
dc
;
dc
.Attach(lpDrawItem
St
ruct->h
DC
);
dc
.DrawFrameControl(&lpDrawItem
St
ruct->rcItem, DFC_BUTTON, u
St
yle); //输出文字
dc
.SelectObject(&m_Font);
dc
.SetTextColor(RGB(0, 0, 255));
dc
.SetBkMode(TRANSPARENT); C
St
ring
sT
ext; m_HelloCFan.GetWindowText(
sT
ext);
dc
.TextOut(lpDrawItem
St
ruct->rcItem.left + 20, lpDrawItem
St
ruct->rcItem.top + 20,
sT
ext); //是否得到焦点 if(lpDrawItem
St
ruct->item
St
ate & ODS_FOCUS) { //画虚框 C
Rect
rtFocus = lpDrawItem
St
ruct->rcItem; rtFocus.Deflate
Rect
(3, 3);
dc
.DrawFocus
Rect
(&rtFocus); } return; } CDialog::OnDrawItem(nI
DC
tl, lpDrawItem
St
ruct); } 别忘了标记Owner draw属性: 图9
指定
按钮的Owner draw属性 值得一提的是,CWnd内部截获了WM_DRAWITEM、WM_MEASUREITEM等消息,并映射成子元素的相应虚
函数
的调用,如CButton::DrawItem()。所以,以上例子也可以通过派生出一个CButton的派生类,并重载该类的DrawItem()
函数
来实现。使用虚
函数
机制实现界面美化参见3.4章节。 3.3.5 WM_MEASUREITEM 仅仅WM_DRAWITEM还是不够的,对于一些特殊的控件,如Li
st
Box,系统在发送WM_DRAWITEM消息前,还发送WM_MEASUREITEM消息,需要你设置Li
st
Box中每个项目的高度。 WM_DRAWITEM的映射
函数
原型如下: afx_msg void OnMeasureItem( int nI
DC
tl, LPMEASUREITEM
ST
RUCT lpMeasureItem
St
ruct ); nI
DC
tl 该控件的ID,如果该元素为菜单,则nI
DC
tl为0 lpMeasureItem
St
ruct指向MEASUREITEM
ST
RUCT结构对象的指针,MEASUREITEM
ST
RUCT的结构定义如下: typedef
st
ruct tagMEASUREITEM
ST
RUCT { UINT CtlType; UINT CtlID; UINT itemID; UINT itemWidth; UINT itemHeight; DWORD itemData } MEASUREITEM
ST
RUCT; CtlType
指定
了控件的类型,其取值如表6所示: 类型值 含义 ODT_COMBOBOX 组合框控件 ODT_LI
ST
BOX 列表框控件 ODT_MENU 菜单项 表6 CtlType的类型值与含义 CtlID
指定
自绘控件的ID值,该成员不适用于菜单项 itemID表示菜单项ID,也可以表示可变高度的列表框或组合框中某项的索引值。该成员不适用于固定高度的列表框或组合框。 itemWidth
指定
菜单项的宽度 itemHeight
指定
菜单项或者列表框中某项的的高度,最大值为255 itemData 对于菜单项,该成员的取值为由CMenu::AppendMenu、CMenu::InsertMenu、CMenu::ModifyMenu等
函数
传递给菜单的值。 对于列表框或这组合框,该成员的取值为由ComboBox::Add
St
ring、CComboBox::Insert
St
ring、CLi
st
Box::Add
St
ring或者CLi
st
Box::Insert
St
ring等
函数
传递给控件的值。 图示出了OnMeasureItem的效果: 图10 利用WM_MEASUREITEM消息美化界面 相应的OnMeasureItem()实现如下: void CUi7Dlg::OnMeasureItem(int nI
DC
tl, LPMEASUREITEM
ST
RUCT lpMeasureItem
St
ruct) { if(nI
DC
tl == I
DC
_COLOR_PICKER) { //设定高度为 lpMeasureItem
St
ruct->itemHeight = 30; return; } CDialog::OnMeasureItem(nI
DC
tl, lpMeasureItem
St
ruct); } 同样别忘了
指定
列表框的Owner draw属性: 图11
指定
下拉框的Owner draw属性 3.3.6 NM_CU
ST
OMDRAW 大家也许熟悉WM_NOTIFY,控件通过WM_NOTIFY向父窗口发送消息。在WM_NOTIFY消息体中,部分控件会发送NM_CU
ST
OMDRAW告诉父窗口自己需要绘图。 可以反射NM_CU
ST
OMDRAW消息,如: ON_NOTIFY_REFLECT(NM_CU
ST
OMDRAW, OnCu
st
omDraw) afx_msg void OnCu
st
omDraw(NMHDR *pNMHDR, LRESULT *pResult); 参数: pNMHDR 说到底只是一个指针,大多数情况下它指向一个NMHDR结构对象,NMHDR结构如下: typedef
st
ruct tagNMHDR { HWND hwndFrom; UINT idFrom; UINT code; } NMHDR; 其中: hwndFrom 发送方控件的窗口句柄 idFrom 发送方控件的ID code 通知代码 对于某些控件来说,pNMHDR则会解释成其它内容更丰富的结构对象的指针,如:对于列表控件来说,pNMHDR常常指向一个NMCU
ST
OMDRAW对象,NMCU
ST
OMDRAW结构如下: typedef
st
ruct tagNMCU
ST
OMDRAWINFO { NMHDR hdr; DWORD dwDraw
St
age; H
DC
h
dc
;
RECT
rc; DWORD dwItemSpec; UINT uItem
St
ate; LPARAM lItemlParam; } NMCU
ST
OMDRAW, FAR * LPNMCU
ST
OMDRAW; hdr NMHDR对象 dwDraw
St
age 当前
绘制
状态,其取值如表7所示: 类型值 含义 CDDS_PO
ST
ERASE 擦除循环结束 CDDS_PO
ST
PAINT
绘制
循环结束 CDDS_PREERASE 准备开始擦除循环 CDDS_PREPAINT 准备开始
绘制
循环 CDDS_ITEM
指定
dwItemSpec, uItem
St
ate, lItemlParam参数有效 CDDS_ITEMPO
ST
ERASE 列表项擦除结束 CDDS_ITEMPO
ST
PAINT 列表项
绘制
结束 CDDS_ITEMPREERASE 准备开始列表项擦除 CDDS_ITEMPREPAINT 准备开始列表项
绘制
CDDS_SU
BIT
EM
指定
列表子项 表7 dwDraw
St
age的类型值与含义 h
dc
指定
了
绘制
操作所使用的设备环境。 rc
指定
了将被
绘制
的矩形区域。 dwItemSpec 列表项的索引 uItem
St
ate 当前列表项的状态,其取值如表8所示: 类型值 含义 CDIS_CHECKED 标记状态。 CDIS_DEFAULT 默认状态。 CDIS_DISABLED 禁止状态。 CDIS_FOCUS 焦点状态。 CDIS_GRAYED 灰化状态。 CDIS_SELECTED 选中状态。 CDIS_HOTLIGHT 热点状态。 CDIS_INDETERMINATE 不定状态。 CDIS_MARKED 标注状态。 表8 uItem
St
ate的类型值与含义 lItemlParam 当前列表项的绑定数据 pResult 指向状态值的指针,
指定
系统后续操作,依赖于dwDraw
St
age: 当dwDraw
St
age为CDDS_PREPAINT,pResult含义如表9所示: 类型值 含义 CDRF_DODEFAULT 默认操作,即系统在列表项
绘制
循环过程不再发送NM_CU
ST
OMDRAW。 CDRF_NOTIFYITEMDRAW
指定
列表项
绘制
前后发送消息。 CDRF_NOTIFYPO
ST
ERASE 列表项擦除结束时发送消息。 CDRF_NOTIFYPO
ST
PAINT 列表项
绘制
结束时发送消息。 表9 pResult的类型值与含义(一) 当dwDraw
St
age为CDDS_ITEMPREPAINT,pResult含义如表10所示: 类型值 含义 CDRF_NEWFONT
指定
后续操作采用应用中
指定
的新字体。 CDRF_NOTIFYSU
BIT
EMDRAW 列表子项
绘制
时发送消息。 CDRF_SKIPDEFAULT 系统不必再
绘制
该子项。 表10 pResult的类型值与含义(二) 以下是一个利用NM_CU
ST
OMDRAW消息
绘制
出的多色列表框的例子: 图12 利用NM_CU
ST
OMDRAW消息美化界面 对应代码如下: void CCoolLi
st
::OnCu
st
omDraw(NMHDR *pNMHDR, LRESULT *pResult) { //类型安全转换 NMLVCU
ST
OMDRAW* pLVCD = reinterpret_ca
st
STOMDRAW*>(pNMHDR); *pResult = 0; //
指定
列表项
绘制
前后发送消息 if(CDDS_PREPAINT == pLVCD->nmcd.dwDraw
St
age) { *pResult = CDRF_NOTIFYITEMDRAW; } else if(CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDraw
St
age) { //奇数行 if(pLVCD->nmcd.dwItemSpec % 2) pLVCD->clrTextBk = RGB(255, 255, 128); //偶数行 else pLVCD->clrTextBk = RGB(128, 255, 255); //继续 *pResult = CDRF_DODEFAULT; } } 注意到上例采取了3.1所推荐的第2种实现方法,派生了一个新类CCoolLi
st
。 3.4 使用MFC类的虚
函数
机制 修改Windows界面,除了从Windows消息机制下功夫,也可以从MFC类下功夫,这应该得益于类的虚
函数
机制。为了防止诸如“面向对象技术”等术语在此泛滥,以下仅举一段代码作为例子: void CView::OnPaint() { //
st
andard paint routine CPaint
DC
dc
(this); OnPrepare
DC
(&
dc
); OnDraw(&
dc
); } 这是MFC中viewcore.cpp中的源代码,很多读者总不明白OnDraw()和OnPaint()之间的关系,从以上的代码中很容易看出,CView的WM_PAINT消息响应
函数
OnPaint()会自动调用CView::OnDraw()。而作为开发者的用户,可以通过简单的OnDraw()的重载实现对WM_PAINT的处理。所以说,对MFC类的虚
函数
的重载是对消息机制的扩展。 以下列出了与界面美化相关的虚
函数
,参数说明略去: CButton::DrawItem CCheckLi
st
Box::DrawItem CComboBox::DrawItem CHeaderCtrl::DrawItem CLi
st
Box::DrawItem CMenu::DrawItem C
St
atusBar::DrawItem C
St
atusBarCtrl::DrawItem CTabCtrl::DrawItem virtual void DrawItem( LPDRAWITEM
ST
RUCT lpDrawItem
St
ruct ); Owner draw元素自绘
函数
很显然,位图菜单都是通过这个DrawItem画出来的。限于篇幅,在此不再附以例程。
C++ MFC实现飞机大战游戏
目 录 1. 概述 3 1.1 实训项目简介 3 1.2 实训功能说明 3 1.2.1 基本功能 3 1.2.2 附加功能 3 2. 相关技术 4 2.1 Windows定时器技术 4 2.2 透明贴图实现技术 4 2.3 CObLi
st
链表 5 2.4获取矩形区域 6 2.5使用AfxMessageBox显示游戏过程中的提示信息 6 2.6内存释放 6 2.7 CImageLi
st
处理爆炸效果 6 2.8对话框的应用 6 3. 总体设计与详细设计 7 3.1 系统模块划分 7 3.2 主要功能模块 8 3.2.1 系统对象类图 8 3.2.2 系统主程序活动图 9 3.2.3 系统部分流程图 9 4. 编码实现 12 4.1
绘制
游戏背景位图程序 12 4.2 飞机大战游戏对象的
绘制
程序 13 4.3 飞机大战游戏对象战机位置的动态控制 15 4.4 飞机大战游戏对象之间的碰撞实现 17 4.5 游戏界面输出当前信息 19 5. 项目程序测试 20 5.1战机移动及子弹发射模块测试 20 5.2 敌机及炸弹模块测试 20 5.3 爆炸模块测试 20 6. 实训中遇到的主要问题及解决方法 21 7. 实训体会 21 1. 概述 1.1 实训项目简介 本次实训项目是做一个飞机大战的游戏,应用MFC编程,完成一个界面简洁流畅、游戏
方式
简单,玩起来易于上手的桌面游戏。该飞机大战项目运用的主要技术即是MFC编程中的一些
函数
、链表思想以及贴图技术。 1.2 实训功能说明 1.2.1 基本功能 (1)设置一个战机具有一定的速度,通过键盘,方向键可控制战机的位置,空格键发射子弹。 (2)界面中敌机出现的位置,以及敌机炸弹的发射均为随机的,敌机与敌机炸弹均具有一定的速度,且随着关卡难度的增大,数量和速度均增加。 (3)对于随机产生的敌机和敌机炸弹,若超过矩形区域,则释放该对象。 (4)添加爆炸效果,包括战机子弹打中敌机爆炸、敌机炸弹打中战机爆炸、战机与敌机相撞爆炸以及战机子弹与敌机炸弹相撞爆炸四种爆炸效果。且爆炸发生后敌机、子弹、炸弹均消失,战机生命值减一。 1.2.2 附加功能 (1) 为游戏界面添加了背景图片,并在战机发射子弹、战机击中敌机、敌机击中战机、以及战机敌机相撞时均添加了背景音效。 (2)为游戏设置了不同的关卡,每个关卡难度不同,敌机与敌机炸弹的速度随着关卡增大而加快,进入第二关以后敌机从上下方均会随机出现,且随机发射炸弹。 (3)第一关卡敌机从上方飞出,速度一定,战机每打掉一直敌机则增加一分,每积十分,则为战机增加一个生命值,当战机得分超过50分则可进入下一关;进入第二、三关时敌机速度加快,分别从上下两方飞出,此时战机每得分20、30分,才会增加一个生命值,得分超过100、150分则进入下一关、通关。 (4) 在游戏界面输出当前游戏进行信息,包括当前得分、当前关卡以及击中敌机数量。 (5)增加了鼠标控制战机位置这一效果,战绩的位置随着鼠标的移动而移动,并且点击鼠标左键可使得战机发射子弹。 (6)实现了暂停游戏的功能,玩家可通过键盘上的‘Z’键,对游戏进行暂停。 (7)通过对话框的弹出可提示玩家是否查看游戏说明、是否进入下一关、是否重新开始等消息,使得玩家可自己选择。 2. 相关技术 2.1 Windows定时器技术 Windows定时器是一种输入设备,它周期性地在每经过一个
指定
的时间间隔后就通知应用程序一次。程序将时间间隔告诉Windows,然后Windows给您的程序发送周期性发生的WM_TIMER消息以表示时间到了。本程序中使用多个定时器,分别控制不同的功能。在MFC的API
函数
中使用SetTimer()
函数
设置定时器,设置系统间隔时间,在OnTimer()
函数
中实现响应定时器的程序。 2.2 透明贴图实现技术
绘制
透明位图的关键就是创建一个“掩码”位图(mask
bit
map),这个“掩码”位图是一个单色位图,它是位图中图像的一个单色剪影。 在详细介绍实现过程之前先介绍下所使用的
画图
函数
以及
函数
参数所代表的功能;整个
绘制
过程需要使用到
Bit
Blt
()
函数
。整个功能的实现过程如下: (1) 创建一张大小与需要
绘制
图像相同的位图作为“掩码”位图; (2) 将新创建的“掩码”位图存储至掩码位图的设备描述表中; (3) 把位图设备描述表的背景设置成“透明色”,不需要显示的颜色; (4) 复制粘贴位图到“掩码”位图的设备描述表中,这个时候“掩码”位图设备描述表中存放的位图与位图设备描述表中的位图一样; (5) 把需要透明
绘制
的位图与对话框绘图相应区域的背景进行逻辑异或操作
绘制
到对话框上; (6) 把“掩码”位图与这个时候对话框相应区域的背景进行逻辑与的操作; (7) 重复步骤5的操作,把需要透明
绘制
的位图与对话框绘图相应区域的背景进行逻辑异或操作
绘制
到对话框上; (8) 最后把系统的画笔还给系统,删除使用过的GDIObject,释放非空的指针,最后把新建的设备描述表也删除。 2.3 CObLi
st
链表 MFC类库中提供了丰富的CObLi
st
类的成员
函数
,此程序主要用到的成员
函数
如下:(1) 构造
函数
,为CObject指针构造一个空的列表。 (2) GetHead(),访问链表首部,返回列表中的首元素(列表不能为空)。(3) AddTail(),在列表尾增加一个元素或另一个列表的所有元素。 (4) RemoveAll(),删除列表中所有的元素。 (5) GetNext(),返回列表中尾元素的位置。 (6) GetHeadPosition(),返回列表中首元素的位置。 (7) RemoveAt(),从列表中删除
指定
位置的元素。 (8) GetCount(),返回列表中的元素数。 在CPlaneGameView.h文件中声明各游戏对象与游戏对象链表: (1)//创建各游戏对象 CMyPlane *myplane; CEnemy *enemy; CBomb *bomb; CBall *ball; CExplosion *explosion; (2)//创建存储游戏对象的对象链表 CObLi
st
Li
st
Enemy; CObLi
st
Li
st
Me; CObLi
st
Li
st
Bomb; CObLi
st
Li
st
Ball; CObLi
st
Li
st
Explosion; 2.4获取矩形区域 首先,使用C
Rect
定义一个对象,然后使用GetClient
Rect
(&对象名)
函数
,获取界面的矩形区域
rect
.Width() 为矩形区域的宽度,
rect
.Height()为矩形区域的高度。 使用Intersect
Rect
(&,&))
函数
来判断两个源矩形是否有重合的部分。如果有不为空,则返回非零值;否则,返回0。 2.5使用AfxMessageBox显示游戏过程中的提示信息 AfxMessageBox()是模态对话框,你不进行确认时程序是否往下运行时,它会阻塞你当前的线程,除非你程序是多线程的程序,否则只有等待模态对话框被确认。 在MFC中,afxmessagebox是全局的对话框最安全,也最方便。 2.6内存释放 在VC/MFC用C
DC
绘图时,频繁的刷新,屏幕会出现闪烁的现象,CPU时间占用率相当高,绘图效率极低,很容易出现程序崩溃。及时的释放程序所占用的内存资源是非常重要的。 在程序中使用到的链表、刷子等占用内存资源的对象都要及时的删除。Delete Brush, Li
st
.removeall()等。 2.7 CImageLi
st
处理爆炸效果 爆炸效果是连续的显示一系列的图片。如果把每一张图片都显示出来的话,占用的时间是非常多的,必然后导致程序的可行性下降。CImageLi
st
是一个“图象列表”是相同大小图象的集合,每个图象都可由其基于零的索引来参考。可以用来存放爆炸效果的一张图片,使用Draw()
函数
来
绘制
在某拖拉操作中正被拖动的图象,即可连续
绘制
出多张图片做成的爆炸效果。 2.8对话框的应用 在设置游戏难度、炸弹的速度等,使用对话框进行设置非常方便,又体现出界面的友好。 对话框的应用过程如下: (1). 资源视图下,添加Dialog对话框。然后添加使用到的控件,并修改控件的ID以便于后面的使用。 (2). 为对话框添加类,在对话框模式下,点击项目,添加类。 (3). 在类视图中,为对话框类添加成员变量(控件变量)。设置变量的名称、类型、最值等信息。 (4). 在资源视图菜单中,选择相应的菜单项,右击添加时间监听程序,设置
函数
处理程序名称。 (5). 在处理程序
函数
中添加相应的信息。 3. 总体设计与详细设计 3.1 系统模块划分 该飞机大战游戏程序分为游戏背景位图
绘制
模块、各游戏对象
绘制
模块、游戏对象之间的碰撞模块、爆炸效果产生模块、游戏界面输出玩家得分关卡信息模块。 其中在游戏对象
绘制
模块中,战机是唯一对象,在游戏开始时产生该对象,赋予其固定的生命值,当其与敌机对象、敌机炸弹碰撞时使其生命值减一,直至生命值为零,便删除战机对象。敌机对象与敌机炸弹对象的
绘制
中采用定时器技术,定时产生。爆炸对象初始化为空,当游戏过程中即时发生碰撞时,在碰撞位置产生爆炸对象,添加到爆炸链表中。 3.2 主要功能模块 3.2.1 系统对象类图 CGameObject是各个游戏对象的抽象父类,继承自CObject类,其他的类:战机类、敌机类、爆炸类、子弹类、炸弹类、文字类都继承了此类。 每个游戏对象类中既继承了来自父类CGameObject的属性,又有自己的特有属性和方法。 3.2.2 系统主程序活动图 3.2.3 系统部分流程图 (1) 该飞机大战游戏执行流程图: (2) 利用定时器定时产生敌机并
绘制
敌机流程图 4. 编码实现 4.1
绘制
游戏背景位图程序 C
DC
*p
DC
=Get
DC
(); //获得矩形区域对象 C
Rect
rect
; GetClient
Rect
(&
rect
;); //设备环境对象类----C
DC
类。 C
DC
c
dc
; //内存中承载临时图像的位图 C
Bit
map
bit
map1; //该
函数
创建一个与
指定
设备兼容的内存设备上下文环境(
DC
) c
dc
.CreateCompatible
DC
(p
DC
); //该
函数
创建与
指定
的设备环境相关的设备兼容的位图。
bit
map1.CreateCompatible
Bit
map(p
DC
,
rect
.Width(),
rect
.Height()); //该
函数
选择一对象到
指定
的设备上下文环境中,该新对象替换先前的相同类型的对象。 C
Bit
map *pOld
Bit
=c
dc
.SelectObject(&
bit
map1;); //用固定的固体色填充文本矩形框 c
dc
.FillSolid
Rect
(
rect
,RGB(51,255,255)); //添加背景图片 C
Bit
map
bit
map_BackGround;
bit
map_BackGround.Load
Bit
map(IDB_BACKGROUND);
BIT
MAP bimap2;//位图图像
bit
map_BackGround.Get
Bit
map(&bimap2;); C
DC
c
dc
_BackGround;//定义一个兼容的
DC
c
dc
_BackGround.CreateCompatible
DC
(&c
dc
;);//创建
DC
C
Bit
map*Old=c
dc
_BackGround.SelectObject(&
bit
map;_BackGround); c
dc
.
St
re
tch
Blt
(0,0,
rect
.Width(),
rect
.Height(),&c
dc
;_BackGround,0,0,bimap2.bmWidth,bimap2.bmHeight,SRCCOPY); 4.2 飞机大战游戏对象的
绘制
程序 //画战机对象(唯一) if(myplane!= NULL) { myplane->Draw(&c
dc
;,TRUE); } //设置定时器,随机添加敌机,敌机随机发射炸弹,此时敌机速度与数量和关卡有关 SetTimer(2,300,NULL);//敌机产生的定时器 SetTimer(3,500,NULL);//敌机炸弹产生的定时器 if(myplane!=NULL&& is_Pause == 0) { switch(nIDEvent) { case 2://设置定时器产生敌机 { if(pass_Num == 1)//第一关 { int motion =1;//设置敌机的方向,从上方飞出 CEnemy *enemy=new CEnemy(motion); Li
st
Enemy.AddTail(enemy);//随机产生敌机 }//if else if(pass_Num >= 2)//第一关以后的关卡 { int motion1 = 1; //设置敌机的方向,从上方飞出 CEnemy *enemy1=new CEnemy(motion1); enemy1->SetSpeed_en((rand()%5 +1)* pass_Num); Li
st
Enemy.AddTail(enemy1);//随机产生敌机 int motion2 = -1;//设置敌机的方向,从下方飞出 CEnemy *enemy2=new CEnemy(motion2); enemy2->SetSpeed_en((rand()%5 +1)* pass_Num); Li
st
Enemy.AddTail(enemy2);//随机产生敌机 }//else if }//case break; }//switch //判断产生的敌机是否出界,若已经出界,则删除该敌机 POSITION posEn=NULL,posEn_t=NULL; posEn=Li
st
Enemy.GetHeadPosition(); int motion = 1; while(posEn!=NULL) { posEn_t=posEn; CEnemy *enemy= (CEnemy *)Li
st
Enemy.GetNext(posEn); //判断敌机是否出界 if(enemy->GetPoint().xGetPoint().x>
rect
.right ||enemy->GetPoint().yGetPoint().y>
rect
.bottom) { Li
st
Enemy.RemoveAt(posEn_t); delete enemy; }//if else { enemy->Draw(&c
dc
;,TRUE); switch(nIDEvent) { case 3://设置定时器产生敌机炸弹 { CBall*ball=newCBall(enemy->GetPoint().x+17, enemy->GetPoint().y+30,enemy->GetMotion()); Li
st
Ball.AddTail(ball); }//case break; }//switch }//else }//while //判断产生的敌机炸弹是否出界,若已经出界,则删除该敌机炸弹 POSITION posball=NULL,posball_t=NULL; posball= Li
st
Ball.GetHeadPosition(); while(posball!=NULL) { posball_t=posball; ball= (CBall *) Li
st
Ball.GetNext(posball); if( ball->GetPoint().xGetPoint().x>
rect
.right || ball->GetPoint().yGetPoint().y>
rect
.bottom) { Li
st
Ball.RemoveAt(posball_t); delete ball; }//if else { ball->Draw(&c
dc
;,1); }//else }//while }//if 4.3 飞机大战游戏对象战机位置的动态控制 if(myplane!= NULL) { myplane->Draw(&c
dc
;,TRUE); } //获得键盘消息,战机位置响应,战机速度speed为30 if((GetKey
St
ate(VK_UP) <0 || GetKey
St
ate('W') GetPoint().ySetPoint( myplane->GetPoint().x,
rect
.bottom); else myplane->SetPoint(myplane->GetPoint().x,( myplane->GetPoint().y - speed) ); }//if if((GetKey
St
ate(VK_DOWN) <0|| GetKey
St
ate('S') < 0)&& is_Pause== 0)//下方向键{}//if if((GetKey
St
ate(VK_LEFT) <0|| GetKey
St
ate('A') < 0)&& is_Pause== 0)//左方向键{}//if if((GetKey
St
ate(VK_RIGHT) <0|| GetKey
St
ate('D') < 0)&& is_Pause== 0)//右方向键{}//if if((GetKey
St
ate(VK_SPACE)GetPoint().x, myplane->GetPoint().y,1); Li
st
Bomb.AddTail(BombOne); CBomb*BombTwo=newCBomb(myplane->GetPoint().x+35, myplane->GetPoint().y,1); Li
st
Bomb.AddTail(BombTwo); PlaySound((LPCT
ST
R)IDR_WAVE2,AfxGetIn
st
anceHandle(),SND_RESOURCE |SND_ASYNC); }//if if(GetKey
St
ate('Z')SetPoint(point.x,point.y); } //鼠标控制战机,发射战机子弹 void CPlaneGameView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CView::OnLButtonDown(nFlags, point); if( is_Pause == 0) { CBomb *BombOne=new CBomb( myplane->GetPoint().x, myplane->GetPoint().y,1); PlaySound((LPCT
ST
R)IDR_WAVE2, AfxGetIn
st
anceHandle(), SND_RESOURCE |SND_ASYNC); Li
st
Bomb.AddTail(BombOne); CBomb *BombTwo=new CBomb( myplane->GetPoint().x+35, myplane->GetPoint().y,1); Li
st
Bomb.AddTail(BombTwo); } } 4.4 飞机大战游戏对象之间的碰撞实现 本飞机大战游戏中的碰撞考虑了飞机子弹打中敌机、敌机炸弹打中战机、战机与敌机相撞、敌机炸弹与战机子弹相撞四种情况,根据游戏对象的矩形区域是否有交叉,而确认两者是否相撞,而产生爆炸对象,添加到爆炸链表中。以战机与敌机相撞为例: if(myplane != NULL&& is_Pause== 0) { POSITION enemyPos,enemyTemp; for(enemyPos= Li
st
Enemy.GetHeadPosition();(enemyTemp=enemyPos)!=NULL;) { enemy =(CEnemy *) Li
st
Enemy.GetNext(enemyPos); //获得敌机的矩形区域 C
Rect
enemy
Rect
= enemy->Get
Rect
(); //获得战机的矩形区域 C
Rect
myPlane
Rect
= myplane->Get
Rect
(); //判断两个矩形区域是否有交接 C
Rect
temp
Rect
; if(temp
Rect
.Intersect
Rect
(&enemy
Rect
;,myPlane
Rect
)) { CExplosion *explosion = new CExplosion( enemy->GetPoint().x+18 , enemy->GetPoint().y + 18); PlaySound((LPCT
ST
R)IDR_WAVE,AfxGetIn
st
anceHandle(), SND_RESOURCE |SND_ASYNC); Li
st
Explosion.AddTail(explosion); //战机生命值减一 lifeNum_Me--; //删除敌机 Li
st
Enemy.RemoveAt(enemyTemp); delete enemy; if(lifeNum_Me == 0) { //删除战机对象 delete myplane; myplane=NULL; }//if break; }//if }//for }//if 战机子弹打中敌机、敌机炸弹打中战机以及战机子弹与敌机炸弹对象的碰撞实现同上。 4.5 游戏界面输出当前信息 if(myplane != NULL&& is_Pause== 0) { HFONT font; font=CreateFont(20,10,0,0,0,0,0,0,0,0,0,100,10,0); c
dc
.SelectObject(font); C
St
ring
st
r; c
dc
.SetTextColor(RGB(255,0,0));
st
r.Format(_T("当前关卡:%d"),pass_Num); c
dc
.TextOutW(10,20,
st
r);
st
r.Format(_T("当前得分:%d"),score_Me); c
dc
.TextOutW(10,40,
st
r);
st
r.Format(_T("剩余生命:%d"),lifeNum_Me); c
dc
.TextOutW(10,60,
st
r); }//if if(myplane !=NULL && lifeNum_Me >0) { if(score_Me > 10*count_Life*pass_Num) { lifeNum_Me++;//生命值加1 count_Life++;//已增加生命值加1 } } 游戏进入下一关,以及结束游戏界面设计代码与上类似。 5. 项目程序测试 5.1战机移动及子弹发射模块测试 用例 预期结果 实际结果 问题描述 修改方案 点击A键或鼠标左移 战机向左移动 战机向左移动 点击D键或鼠标右移 战机向右移动 战机向右移动 点击W键或鼠标上移 战机向上移动 战机向上移动 点击S键或鼠标上移 战机向下移动 战机向下移动 5.2敌机及炸弹模块测试 用例 预期结果 实际结果 问题描述 修改方案 玩家得分50(通过第一关后) 敌机从上下两方向均可飞出,且速度不断增加 敌机从上下两方向均可飞出,且速度不断增加 5.3爆炸模块测试 用例 预期结果 实际结果 问题描述 修改方案 战机子弹打中敌机 敌机位置处爆炸,敌机消失,战机生命-1 敌机位置处爆炸,敌机消失,战机生命-1 敌机炸弹打中战机 战机位置处爆炸,战机生命-1 战机位置处爆炸,战机生命-1 敌机战机相撞 敌机位置处爆炸,敌机消失,战机生命-1 敌机位置处爆炸,敌机消失,战机生命-1 战机子弹与敌机炸弹相撞 敌机炸弹处爆炸,子弹与炸弹均消失消失 敌机炸弹处爆炸,子弹与炸弹均消失消失 战机生命值==0 战机消失,GameOver或者过关 战机消失,GameOver或者过关 6. 实训中遇到的主要问题及解决方法 (1)由于对C++的面向对象的思想和逻辑思路不熟悉,不明白其中的封装之类的以及多态的思想,致使开始真正的进入实训接触到项目时没有开发思路,通过逐步查询书籍整理C++面向对象编程思路,才逐步理清项目的开发步骤。 (2)本飞机大战的游戏要求使用链表实现各游戏对象的存储和释放,由于链表知识掌握的不牢固,使用起来总是出现这样那样的错误,给整个游戏开发带来了很大的障碍,通过不断的调试修改,最终使程序正确运行。 (3)在
绘制
各种游戏对象—敌机和敌机炸弹时,开始使用随机
函数
,画出敌机时而很少,总是打不到预定的效果,后来经过修改使用定时器产生敌机和敌机炸弹,使整个游戏更加人性化。 7. 实训体会 (1)在本次飞机大战游戏项目的开发过程中遇到很多问题,大部分是因为对MFC编程的不熟悉以及链表掌握不牢固所导致的。 (2)MFC编程中有很多可以直接调用的
函数
,由于之前缺乏对这方面编程的经验,以至于本次项目开发过程中走了很多弯路。 (3)通过寻求老师和同学的帮助,解决了开发中遇到的很多问题,也提升了自己调试错误的能力。 (4)通过本次实训,使我熟悉了MFC编程技术、巩固了链表的使用方法并加深了对面向对象编程思想的理解,对以后程序的编写打下了良好的基础。
GAME,图形处理/多媒体
1,183
社区成员
14,336
社区内容
发帖
与我相关
我的任务
GAME,图形处理/多媒体
Delphi GAME,图形处理/多媒体
复制链接
扫一扫
分享
社区描述
Delphi GAME,图形处理/多媒体
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章