【VC++游戏开发#六】2D篇 —— 粒子系统(一):浪漫唯美的场景之雪花飞舞

本文由BlueCoder编写   转载请说明出处:

http://blog.csdn.net/crocodile__/article/details/16887511

我的邮箱:bluecoder@yeah.net    欢迎大家和我交流编程心得

我的微博: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哈哈~

评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值