版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/felicityWSH/article/details/70695816
大家应该在游戏中见过这种场景:很多雨滴或者雪花,在屏幕上动态地飞舞。基于MFC和GDI+,我也实现了一个类似的玫瑰花飞舞效果,先看下运行的效果截图 (要是在代码中再写一段浪漫的话 一定是情人节时最有创意的祝福吧):
每一朵花,都看成一个独立的渲染对象,这个对象具有一些属性和参数,在渲染整个窗口时,依次画出每一个渲染对象。很明显,整个运行过程中,需要定时更新对象(旋转、下落、水平移动)同时也要定时更新UI界面,达到动态的效果。 根据分析,每个渲染对象需要这些信息:
待渲染的图片数据,
图片的缩放比例,
对象移动过程中是否旋转,
对象水平移动的x坐标区间,
对象水平移动的速度,
对象垂直下落的速度, 根据以上信息,实时更新图片的位置信息:x坐标、y坐标、旋转角度,再结合图片信息,就可以渲染出一个对象。于是有了以下设计:
typedef std::tr1::shared_ptr<Gdiplus::Image> GDI_IMAGE_PTR; // 智能指针
typedef std::tr1::shared_ptr<CDrawItem> DRAW_ITEM_PTR; // 智能指针
struct tItemParam // 对象参数 与位置更新以及渲染图片有关
{
GDI_IMAGE_PTR m_pImage; // 图片对象(使用智能指针)
float m_fScale; // 缩放比例
bool m_bRotate; // 是否旋转
int xLeft; // 能移动到的最左侧位置
int xRight; // 能移动到的最右侧位置
int speedMove; // 每秒水平方向移动的像素
int speedDown; // 每秒下坠的像素
};
class CDrawItem // 一个CDrawItem对象 代表一个图片
{
tItemParam m_tInfo;
DWORD m_dwStart;
DWORD m_dwPreUpdate;
DWORD m_dwPreRotate;
bool m_bToRight; // 当前水平方向是否正向右移动
long m_xPos; // 当前x坐标
long m_yPos; // 当前y坐标
Gdiplus::RotateFlipType m_eRotate; // 当前旋转信息
public:
static RECT s_rcScreen; // 所有对象运动的有效区间 demo中是以屏幕坐标为准
explicit CDrawItem(tItemParam& info);
~CDrawItem();
// 根据对象中的参数,定时更新对象的位置信息。如果下落到屏幕的底部则返回false,调用者就会将这个对象移除
bool Update();
// 根据对象当前的位置信息画出对象(实际的渲染功能,就是在这个函数中实现)这个函数会在CDrawPool::Draw(...)中被调用
void Draw(Gdiplus::Graphics& grh);
};
然后再写一个管理所有对象的类CDrawPool,CDrawPool中会创建一个多线程,多线程函数做以下功能:
1、定时生成一些新的对象,对象的信息(图片、缩放、x范围、移动速度)都是随机的;
2、定时更新所有对象的位置信息;
3、更新对象位置时,如果发现对象已经无效(下落到底部了)就将对象移除
1、定时生成一些新的对象,对象的信息(图片、缩放、x范围、移动速度)都是随机的;
2、定时更新所有对象的位置信息;
3、更新对象位置时,如果发现对象已经无效(下落到底部了)就将对象移除
class CDrawPool
{
public:
CImagePool m_Images;
CRITICAL_SECTION m_cs;
vector<DRAW_ITEM_PTR> m_Items;
HANDLE m_hExitEvent;
HANDLE m_hThreadUpdate;
public:
CDrawPool();
~CDrawPool();
void Start(); // 开始
void Stop(); // 结束
void Draw(Gdiplus::Graphics& grh); // 渲染所有对象
private:
static unsigned __stdcall ThreadUpdateItems(void* pParam);
void InnerThread();
};
在函数CDrawPool::Draw(Gdiplus::Graphics& grh)中,依次调用所有对象的渲染函数CDrawItem::Draw(Gdiplus::Graphics& grh)
void CDrawPool::Draw(Gdiplus::Graphics& grh)
{
CAutoLockCS AutoLock(m_cs);
vector<DRAW_ITEM_PTR>::iterator itr = m_Items.begin();
for (; itr != m_Items.end() ;++itr)
{
(*itr)->Draw(grh); // 调用每一个对象的渲染函数
}
}
其中多线程函数如下:
void CDrawPool::InnerThread()
{
DWORD dwPreCreateItem = 0; // 上一次创建一个对象的时刻
while (!IsHandleSigned(m_hExitEvent, 10))
{
CAutoLockCS AutoLock(m_cs);
vector<DRAW_ITEM_PTR>::iterator itr = m_Items.begin();
while (itr != m_Items.end() && !IsHandleSigned(m_hExitEvent, 0))
{
if ((*itr)->Update()) // 更新对象的位置信息
{
itr++;
}
else
{
itr = m_Items.erase(itr); // 对象已经移动到底 移除对象
}
}
if (GetTickCount() - dwPreCreateItem >= 100) // 每隔100毫秒 就创建一个新的对象
{
dwPreCreateItem = GetTickCount();
tItemParam info;
info.image = m_Images.GetRandomImage(); // 从图片池中获取一张随机的图片
info.m_fScale = CToolFunc::GetRandomNumber(0.2f, 0.4f);
info.xLeft = CToolFunc::GetRandomNumber(0, CDrawItem::s_rcScreen.right - 100);
info.xRight = info.xLeft + CToolFunc::GetRandomNumber(50, CDrawItem::s_rcScreen.right - info.xLeft);
info.speedMove = CToolFunc::GetRandomNumber(10, 200);
info.speedDown = CToolFunc::GetRandomNumber(10, 200);
info.m_bRotate = (timeGetTime() % 3) == 0;
DRAW_ITEM_PTR pItem(new CDrawItem(info));
m_Items.push_back(pItem);
}
}
}
CToolFunc::GetRandomNumber是一个生成随机数的函数:可以生成一个指定范围的随机数。
#include <ctime>
#include <cstdlib>
#define FLOAT_INT_TEST 100000
int CToolFunc::GetRandomNumber(int min, int max)
{
srand(unsigned(timeGetTime() * timeGetTime()));
return (min + rand() % (max - min));
}
float CToolFunc::GetRandomNumber(float min, float max)
{
int left = int(min * FLOAT_INT_TEST);
int right = int(max * FLOAT_INT_TEST);
int res = GetRandomNumber(left, right);
return float(res) / FLOAT_INT_TEST;
}
注意,渲染窗口使用了扩展窗口属性WS_EX_LAYERED。如果还不清楚这个属性,
看这篇博文。界面模块的调用如下:
// 定义对象
CDrawPool m_ItemPool;
// 初始化函数
funcInit()
{
m_ItemPool.m_Images.PushImage(L“C:\\image1.png”);
m_ItemPool.m_Images.PushImage(L“C:\\image2.png”);
m_ItemPool.m_Images.PushImage(L“C:\\image3.png”);
m_ItemPool.Start();
}
// 渲染函数
funcPaint()
{
HDC hDC = ::GetDC(m_hWnd);
Gdiplus::Graphics grh(hDC);
grh.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
grh.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAlias);
m_ItemPool.Draw(grh);
::ReleaseDC(m_hWnd, hDC);
}