本文由BlueCoder编写 转载请说明出处:
http://blog.csdn.net/crocodile__/article/details/17510463
我的邮箱:[email protected] 欢迎大家和我交流编程心得
我的微博:BlueCoder_黎小华 欢迎光临^_^
Hi,大家好,I'm here to see you again:)
今晚是一个特别而美好的日子哈——所以呢,BlueCoder在这里祝大家圣诞快乐,Merry Christmas~
……
Ok,我们继续聊聊2D游戏效果的那些事儿~
今天呢,在这个美好的夜晚,BlueCoder为大家敬献一个漂亮的程序:粒子系统高级应用之星光四射,下面进入今晚的效果模拟的殿堂^.~
你是否迫不及待看看程序的效果呢?呵呵——Then, go ahead……
一、效果演示
运行效果截图:
可以发现:星星想四面八方移动,并且随着移动时间增长,星星也越变越大,还是比较炫吧,O(∩_∩)O哈哈~
二、准备工作
1、一张背景,当然在这个特殊的夜晚就要选择有圣诞节氛围的图片作为背景,准备12张不同颜色的星星
2、一首悦耳的背景音乐
3、类视图
三、实现细节
我编写这个程序的灵感来自在网上偶然看见的一张图,如下:
看着这些星星,大小不一,加上联想到自己最近要再来做一个粒子系统的高级应用Demo,因此这个美好的想法就诞生了 : >
本次编程的思想还是沿用之前的面向对象的编程思想,电脑屏幕前的你觉得应该封装几个类、又该如何实现呢?
恩,对(我姑且认为 you & me 的想法一致哈),我封装了两个类:CStar(负责星星的处理操作)和CScene(负责贴背景和播放背景音乐)
实现剖析原理:
1、贴背景与播放音乐就不用详解了,这是之前所有MFC程序Demo的基础
2、主要来剖析一下星光四射实现的原理
在实际的笛卡尔坐标系中分四个象限:一、二、三、四(很简单,不用细说了)
而在windows窗口中,默认的窗口坐标系和我们熟悉的笛卡尔坐标系是不一样的,到底怎么个不一样法呢?来看看我为大家绘制的图解,你就能明白了:
(注:熟悉Win32的朋友可能知道,这个关系到windows窗口的一个技术:映射模式,这里windows窗口默认的映射模式是MM_TEXT,因此我们也可以用这个技术来让windows窗口使用笛卡尔坐标系模式,这里就不去赘述了,不熟悉或者感兴趣的朋友可以去看看P先生的《windows程序设计》关于映射模式的章节,里面有详细说明)
注意上图中四个象限的坐标x、y的符号,表示随着当前象限中的箭头所指方向移动,横坐标或纵坐标的变化趋势,负号表示越来越小;反之,越来越大
ok,下面结合一段核心代码来继续理解:(注意下面四个象限设置的mx、my的符号,和上图是对应的)
以下代码是设置星星数组参数(现在,你只需要关注设置象限那一块)
for(int i=0; i<STAR_MAX; i++) { //设置星星数组参数 m_star[i].id = rand()%STAR_TYPE; m_star[i].x = x; m_star[i].y = y; m_star[i].time = 0; m_star[i].exist = true; // //让星星在四个不同的象限移动 // switch(i%4) { //第一象限 case 0: m_star[i].mx = 3 + rand()%20; m_star[i].my = - (3 + rand()%20); break; //第二象限 case 1: m_star[i].mx = - (3 + rand()%20); m_star[i].my = - (3 + rand()%20); break; //第三象限 case 2: m_star[i].mx = - (3 + rand()%20); m_star[i].my = 3 + rand()%20; break; //第四象限 case 3: m_star[i].mx = 3 + rand()%20; m_star[i].my = 3 + rand()%20; } }
但愿经过我的精心讲解,电脑屏幕前的你已经懂了,或者多少已有一些思绪,呵呵
四、代码剖析
还是主要讲解一下我自行封装的两个类,其余在View窗口中的代码直接贴出(但依然注有较为详细的注释)
我封装的两个类:
1、CScene
这个类较为简单,也直接贴出来吧^:
头文件中的类核心定义
//------变量成员----- private: CImage m_bg;//背景 CRect m_rClient;//窗口客户区范围 //------成员函数----- private: //绘制字符串(按照特定的字体样式) void DrawString(CDC&); public: bool Init();//初始化场景 void Paint(CDC&);//绘制背景 //设置窗口客户区范围 void SetClientRect(CRect); void Release();//释放内存资源
函数成员的实现:
//---------------------------- // 成员函数的实现 //---------------------------- //初始化 bool CScene::Init() { m_bg.Load(L"res\\bg.jpg"); //如果加载失败 if(m_bg.IsNull()) { return false; } m_rClient.SetRectEmpty(); mciSendString(L"open res\\bgm.mp3 alias bgm", NULL, 0, NULL); mciSendString(L"play bgm repeat", NULL, 0, NULL); return true; } //设置客户区范围 void CScene::SetClientRect(CRect rClient) { m_rClient = rClient; } //绘制字符串 void CScene::DrawString(CDC &cdc) { CFont font; font.CreatePointFont(110, L"微软雅黑"); cdc.SelectObject(font); cdc.SetBkMode(TRANSPARENT); int y = m_rClient.bottom - 160; cdc.SetTextColor(RGB(0, 180, 0)); cdc.TextOutW(0, y, L"谨在平安夜这个美好的时刻,"); cdc.TextOutW(0, y+20, L"为大家献上这个漂亮的程序~"); cdc.SetTextColor(RGB(80, 147, 221)); cdc.TextOutW(0, y+50, L"祝大家在新的一年中梦想成真^_^~"); cdc.TextOutW(0, y+70, L"记住,别忘了自己最初的梦想~"); cdc.TextOutW(0, y+90, L"别放弃,Try Your Best O(∩_∩)O~"); cdc.SetTextColor(RGB(240, 50, 50)); cdc.TextOutW(0, y+120, L"~Hi, Guys, Merry Christmas~"); } //绘制场景 void CScene::Paint(CDC &cdc) { cdc.SetStretchBltMode(COLORONCOLOR); m_bg.StretchBlt(cdc, m_rClient, SRCCOPY); DrawString(cdc); } //释放内存资源 void CScene::Release() { mciSendString(L"close bgm", NULL, 0, NULL); m_bg.Destroy(); }
2、CStar
(1)先来看看星星结构体的定义,这是粒子系统中粒子的模板定义:
//星星结构体(粒子) typedef struct star { int id;//关联CImage对象的索性号(以匹配不同颜色的星星) int x;//横坐标 int y;//纵坐标 int mx;//横坐标每次移动的位移 int my;//纵坐标每次移动的位移 int time;//移动的次数(扩展:还用于确定当前星星的大小) bool exist;//是否存在 }STAR;(2)由于有12张不同颜色的星星图片(png),因此需要一个有12个元素的CImage数组
(3)另外,还需要一个粒子Star数组来控制每一个粒子的运动状态
(4)然后我们还需要一个int成员m_count来标识当前星星存在的个数
以下是该类的成员:
//--静态成员-- public: //星星的最大颗数 static const int STAR_MAX = 150; //星星的种类数 static const int STAR_TYPE = 12; //--成员变量-- private: STAR m_star[STAR_MAX];//星星结构体 CImage m_img[STAR_TYPE];//星星png int m_count;//星星的当前数目计数 CSize m_sClient;//view窗口客户区大小 //--成员函数-- public: bool Init();//初始化 void SetStarParam();//设置星星的参数 void MoveStar();//移动星星 void Paint(CDC&);//绘制星星 void Release();//释放内存资源
函数成员的实现:
//----------------------------------- // 成员函数的实现 //----------------------------------- //初始化 bool CStar::Init() { CString path; //加载图片 for(int i=0; i<STAR_TYPE; i++) { path.Format(L"res\\%d.png", i+1); m_img[i].Load(path); //加载失败 if(m_img[i].IsNull()) { return false; } } m_count = 0; m_sClient.SetSize(790, 568); return true; } //设置星星参数 void CStar::SetStarParam() { srand(GetTickCount()); //为了确保效果更佳, 需保证星星起点在窗口中央处 int x = rand()%(m_sClient.cx / 2) + m_sClient.cx / 4; int y = rand()%(m_sClient.cy / 2) + m_sClient.cx / 4; for(int i=0; i<STAR_MAX; i++) { //设置星星数组参数 m_star[i].id = rand()%STAR_TYPE; m_star[i].x = x; m_star[i].y = y; m_star[i].time = 0; m_star[i].exist = true; // //让星星在四个不同的象限移动 // switch(i%4) { //第一象限 case 0: m_star[i].mx = 3 + rand()%20; m_star[i].my = - (3 + rand()%20); break; //第二象限 case 1: m_star[i].mx = - (3 + rand()%20); m_star[i].my = - (3 + rand()%20); break; //第三象限 case 2: m_star[i].mx = - (3 + rand()%20); m_star[i].my = 3 + rand()%20; break; //第四象限 case 3: m_star[i].mx = 3 + rand()%20; m_star[i].my = 3 + rand()%20; } } //初始化后, 当前计数就是最大值 m_count = STAR_MAX; } //移动星星 void CStar::MoveStar() { //如果当前没有星星就需要重新设置星星的相关属性 if(m_count == 0) { SetStarParam(); } for(int i=0; i<STAR_MAX; i++) { //如果星星存在, 才需要移动 if(m_star[i].exist) { //移动坐标 m_star[i].x += m_star[i].mx; m_star[i].y += m_star[i].my; //移动次数加1 m_star[i].time ++; //如果星星出界或则移动的次数超过50次, 则星星不存在了 if(m_star[i].time > 50 || m_star[i].x < -20 || m_star[i].x > m_sClient.cx || m_star[i].y < -20 || m_star[i].y > m_sClient.cy) { m_star[i].exist = false; m_count --;//计数减1 } } } } //绘制星星 void CStar::Paint(CDC &cdc) { cdc.SetStretchBltMode(COLORONCOLOR); for(int i=0; i<STAR_MAX; i++) { //星星存在才需要绘制 if(m_star[i].exist) { int xy = m_star[i].time; /* 根据移动次数来决定星星显示的大小 随着移动次数的增加, 星星显示的也就越来越大 (这就是你看到的星星逐渐变大的效果) */ m_img[m_star[i].id].TransparentBlt( cdc, m_star[i].x, m_star[i].y, xy, xy, RGB(255, 255, 255)); } } } //释放内存资源 void CStar::Release() { for(int i=0; i<STAR_TYPE; i++) { m_img[i].Destroy(); } }
3、View窗口中相关实现:
//移动星星计时器ID #define ID_MOVESTAR 100
//-----------成员变量------------- private: CScene m_scene;//场景类实例对象 CStar m_star;//星星类实例对象 CRect m_rClient;//View窗口Rect
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CWnd::OnCreate(lpCreateStruct) == -1) return -1; //----------------初始化操作--------------- if(!m_scene.Init() || !m_star.Init()) { AfxMessageBox(L"图片资源加载失败"); exit(0); } m_star.SetStarParam(); SetTimer(ID_MOVESTAR, 40, NULL); return 0; }
void CChildView::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); //获取view窗口客户区大小 GetClientRect(m_rClient); m_scene.SetClientRect(m_rClient); }
void CChildView::OnTimer(UINT_PTR nIDEvent) { m_star.MoveStar(); InvalidateRect(NULL, false); CWnd::OnTimer(nIDEvent); }
void CChildView::OnPaint() { CPaintDC dc(this); // 用于绘制的设备上下文 CDC bufferDC; CBitmap bufferBmp; //--------------双缓冲贴图--------------------- bufferDC.CreateCompatibleDC(NULL); bufferBmp.CreateCompatibleBitmap(&dc, m_rClient.Width(), m_rClient.Height()); bufferDC.SelectObject(bufferBmp); m_scene.Paint(bufferDC); m_star.Paint(bufferDC); dc.BitBlt(0, 0, m_rClient.Width(), m_rClient.Height(), &bufferDC, 0, 0, SRCCOPY); //释放内存资源 bufferBmp.DeleteObject(); bufferDC.DeleteDC(); }
void CChildView::OnDestroy() { CWnd::OnDestroy(); //-------------------释放内存资源------------------ m_scene.Release(); m_star.Release(); }
五、零积分资源下载
点击下载源代码
送大家一句正能量的箴言:
优秀是一种习惯。
——亚里士多德
我们从现在起就要把优秀变成一种习惯,使我们的优秀行为习以为常,变成我们的第二天性。让我们习惯性地去创造性思考,习惯性地去认真做事情,习惯性地对别人友好,习惯性地欣赏大自然。
电脑屏幕前的你,养成了优秀的习惯吗?