本文由BlueCoder编写 转载请说明出处:
http://blog.csdn.net/crocodile__/article/details/16887511
我的邮箱:[email protected] 欢迎大家和我交流编程心得
我的微博:BlueCoder_黎小华 欢迎光临^_^
注:
上次上传的关于如何创建适合游戏编程的MFC工程不能更改窗口的大小,是我疏漏了一步,按如下继续操作就行了:
int CSnowFlakeApp::ExitInstance() { AfxOleTerm(FALSE); CleanState(L"WorkSpace");//清除工程状态 return CWinAppEx::ExitInstance(); }之后创建工程都加上这句代码就好了,另外感谢网友Enjoy__Coding的提醒
——By BlueCoder
2013/12/10
冬季来了,雪花是她最美的象征,象征着纯洁、美丽,同时也象征着美好的爱情……
呵呵,今天来实现一个浪漫唯美的场景——雪花漫天飞舞
一、效果演示
老规矩,上一个gif演示效果:
看着雪花漫天飘动的感觉,还是蛮爽的哈^_^
二、准备工作
在此之前,我想先声明一点:
最近,我发现VS2008有些让我苦恼了,由于它没有VS2010那样强大的代码规范性以及正确性的提示功能,加上写代码时难免出一些小错误,所以我发现开发大型点儿的程序,VS2008有点儿力不从心了。于是呢,我果断决定以后使用VS2010来写程序。
另外呢,对于写游戏而言,一般我们都只需要一个窗口就够了,而MFC封装了很多不必要的东西,为此,我精心准备了一个教程,专门教你精简MFC工程,以适合游戏编程(尽管这样,MFC还是不适合写大型的游戏,但一般而言是足够了的)
ps:此教程面向初学者,仅供借鉴、参考——教程为一个word文档,在后面的资源下载中提供
ok,继续……
(1). 抠图——准备一张图,利用ps或美图等美工软件将雪花提取出来,然后制作一个透明png:
提取之前 提取之后(前两个是抠的, 第三个是自带的)
(2). 准备一首唯美动听的轻音乐
(3). 按照我所提供的MFC工程创建教程用VS2010来创建一个工程——SnowFlake
类视图
三、实现细节
从上面的gif效果演示图可以发现,程序中背景是静态的,雪花是动态的——并且,雪花是以向右倾斜、颤抖、匀速的状态"飘动着"。哪么,是如何实现的呢?Well,我做了一个示意图,请过目:
其中蓝色区域表示雪花png左上角点的飘动范围,红色边框表示窗口客户区
那么,只要给雪花png在水平x轴上设定一个速度分量、在垂直y轴上设定一个速度分量,就能达到雪花向右倾斜、匀速飘动的效果
至于,雪花颤抖,我们可以设定一个水平偏移分量offset,offset为正表示向右偏移;反之,向左偏移。那么只要不断的这样偏移,就能达到雪花颤抖的效果
当然,一张唯美浪漫的背景,加上漫天飞舞的雪花,貌似差点儿什么……
对,再加上一首动听的轻音乐——恩,浪漫的感觉似乎加分不少——呵呵,来一首林海的《星空》
四、代码剖析
(1). 定义一个枚举类型SNOWTYPE,表示三种不同雪花类型
//雪花类型 实心 空心 箭头 enum SNOWTYPE{SOLID, HOLLOW, ARROW};
(2). 定义一个雪花结构体
//雪花结构体 typedef struct s { int x; //x坐标 int y; //y坐标 SNOWTYPE type;//雪花类型(两种不同的雪花形状) }SNOW;
(3). CChildView类中的自定义成员变量
private: CImage m_bg;//背景 CImage m_solid, m_hollow, m_arrow;//实心、空心、箭头雪花 SNOW m_snow[MAX_SNOW];//雪花数组 int m_count;//雪花计数 CSize m_sClient;//客户区大小 CSize m_sPng;//雪花大小
成员函数
public: //初始化 bool Initialize(); //绘制雪花 void PaintSnow(CDC*);
初始化代码// 初始化 bool CChildView::Initialize() { //加载图片 m_bg.Load(_T("res\\bg.jpg")); m_solid.Load(_T("res\\0.png")); m_hollow.Load(_T("res\\1.png")); m_arrow.Load(_T("res\\2.png")); //判断图片是否加载成功 if(m_bg.IsNull() || m_solid.IsNull() || m_hollow.IsNull() || m_arrow.IsNull()) { return false; } //计数为0 m_count = 0; //获取客户区大小 m_sClient.SetSize(640, 480); //获取雪花大小 m_sPng.SetSize(30, 30); //设置时间种子 srand(GetTickCount()); //来一首唯美浪漫的背景音乐 mciSendString(L"open res\\星空.mp3 alias bgm", NULL, 0, NULL); mciSendString(L"play bgm repeat", NULL, 0, NULL); return true; }
雪花绘制函数// 绘制雪花 void CChildView::PaintSnow(CDC *pDC) { //-------------------绘制---------------------- CDC bufferDC; CBitmap bufferBmp; //创建兼容内存DC和Bmp bufferDC.CreateCompatibleDC(NULL); bufferBmp.CreateCompatibleBitmap(pDC, m_sClient.cx, m_sClient.cy); bufferDC.SelectObject(bufferBmp); //绘制背景 bufferDC.SetStretchBltMode(COLORONCOLOR); m_bg.StretchBlt(bufferDC, 0, 0, m_sClient.cx, m_sClient.cy, SRCCOPY); //现在内存中绘制雪花以及背景 for(int i=0; i<m_count; i++) { //以x轴移动3像素、y轴移动7像素匀速移动 m_snow[i].y += 7; m_snow[i].x += 3; //如果雪花出了下边界, 返回起始位置并重新分配雪花类型 if(m_snow[i].y > m_sClient.cy || m_snow[i].x > (m_sClient.cx - m_sPng.cx)) { m_snow[i].x = rand()%(m_sClient.cx + 100) - m_sPng.cx - 100; m_snow[i].y = 0; m_snow[i].type = static_cast<SNOWTYPE>(rand()%3); continue; } //水平x坐标的偏移量, 以达到雪花颤抖的效果 int offset; //1为向右偏移 if(rand()%2) { offset = rand()%3; } //0为向左偏移 else { offset = -rand()%3; } //根据雪花类型绘制对应形状的雪花 switch(m_snow[i].type) { case SOLID: m_solid.TransparentBlt(bufferDC, m_snow[i].x + offset, m_snow[i].y, m_sPng.cx, m_sPng.cy, RGB(0, 0, 0)); break; case HOLLOW: m_hollow.TransparentBlt(bufferDC, m_snow[i].x + offset, m_snow[i].y, m_sPng.cx, m_sPng.cy, RGB(0, 0, 0)); break; case ARROW: m_arrow.TransparentBlt(bufferDC, m_snow[i].x + offset, m_snow[i].y, m_sPng.cx, m_sPng.cy, RGB(0, 0, 0)); } } //最后将内存中绘制的雪花一次性绘制到客户区中 pDC->BitBlt(0, 0, m_sClient.cx, m_sClient.cy, &bufferDC, 0, 0, SRCCOPY); //释放内存兼容DC和Bmp bufferBmp.DeleteObject(); bufferDC.DeleteDC(); }
(4). 在WM_CREATE消息中进行一系列的初始化操作
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CWnd::OnCreate(lpCreateStruct) == -1) return -1; //如果加载失败, 直接退出程序 if(!Initialize()) { AfxMessageBox(L"图片加载失败"); exit(0); } //设置计时器 SetTimer(ID_TIMER, 80, NULL); return 0; }
(5). 在WM_TIMER消息中添加雪花、重绘客户区
void CChildView::OnTimer(UINT_PTR nIDEvent) { //如果小于指定的雪花最大个数, 继续添加 if(m_count < MAX_SNOW) { m_snow[m_count].x = rand()%(m_sClient.cx + 100) - m_sPng.cx - 100; m_snow[m_count].y = 0; m_snow[m_count].type = static_cast<SNOWTYPE>(rand()%3); m_count++; } //重绘客户区 InvalidateRect(NULL, false); CWnd::OnTimer(nIDEvent); }
(6). 在WM_PAINT消息中绘制整个场景
void CChildView::OnPaint() { CPaintDC dc(this); // 用于绘制的设备上下文 //用双缓冲绘制雪花 PaintSnow(&dc); }
(7). 别忘了回收内存资源
void CChildView::OnDestroy() { CWnd::OnDestroy(); //关闭音乐、计时器、释放资源 mciSendString(L"close bgm", NULL, 0, NULL); KillTimer(ID_TIMER); m_bg.Destroy(); m_solid.Destroy(); m_hollow.Destroy(); }
五、零积分资源下载
点击下载源码以及MFC工程创建教程
OK,你可以自己添加一点儿创意,然后送给你的那个她,相信她会很喜欢的,O(∩_∩)O哈哈~