【VC++游戏开发#二】2D篇 —— 平滑的幻灯片放映效果
2013-10-23 18:09
253 查看
本文由BlueCoder编写 转载请说明出处:http://blog.csdn.net/crocodile__/article/details/12975077我的邮箱:bluecoder@yeah.net 欢迎大家和我交流编程心得我的微博:BlueCoder_黎小华 欢迎光临^_^ 前段时间为了系统学习MFC,花了一星期的课余时间复习了c++,将<<C++ Primer>>看了一遍,感觉关于c++的见识涨了不少,毕竟看完大师级人物写的书籍确实应该有如获光明的感觉。不过由于时间的原因,有几章节没怎么祥看——当然,以后必定细细品味
因此感叹一句:好久没写博客了啊——呵呵
ok,归入正题
今天上午看见Yorhom用HTML5实现了一个『HTML5梦幻之旅』-滚动播放的幻灯片效果,感觉不错。于是借着他给的灵感(刚好最近在学MFC),我用MFC来实现类似的效果(思想略有不同),先上一个效果gif:
下面我来讲解一下具体的实现
一、编译环境
VS2008企业完整版,创建一个MFC工程(项目名称为SlideBmp):
二、剖析实现思想先来看看我制作的一张示意图:
ClientWidth表示窗口客户区的宽度——这里就是View窗口
(1). 从左边出现的图片,预先放在横坐标为-ClientWidth的位置,然后启动计时器,增加横坐标,逐渐移动,直到横坐标为0——也就是到达了窗口客户区,就关闭计时器——这时图片刚好停留在客户区
(2). 同理,从右边出现的图片预先放在横坐标为ClientWidth处,后面的同(1)
这样实现的效果就是每次移动的图片会覆盖上一次的图片
怎么样,还是挺简单的吧?
三、剖析实现代码(1). 改变MFC默认的窗口外观——在CMainFrame::PreCreateWindow中,也就是创建框架窗口之前修改——代码如下:BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
//更改窗口外观
CSize size;
//获取屏幕像素大小
size.cx = ::GetSystemMetrics(SM_CXSCREEN);
size.cy = ::GetSystemMetrics(SM_CYSCREEN);
//窗口风格(快捷菜单式的窗口, 无边框、标题栏)
cs.style = WS_POPUPWINDOW;
//去掉菜单
cs.hMenu = NULL;
//窗口标题名称
cs.lpszName = _T("SlideBmp");
//自定义窗口大小, 并窗口居中显示
cs.x = (size.cx - 486 ) / 2;
cs.y = (size.cy - 360) / 2;
cs.cx = 486;
cs.cy = 360;
return TRUE;
}
(2). 因为View窗口是MainFrame框架窗口的子窗口,因此应该在它上面做工作——贴图片
(3). 在构造函数中进行初始化工作CSlideBmpView::CSlideBmpView()
{
//初始化
m_index = 0;
m_start = 0;
m_isStart = true;
m_isLeft = false;
m_isLoad = true;
}
(4). 所需变量或成员的定义,在SlideBmpView.h头文件中
全局变量//计时器ID
#define ID_TIMER_PAINT 100
//全局变量N——表示有几张图片
const int N = 5;
//图片的名称(这里也是路径)
const LPCTSTR pszPngName[] = {_T("0.png"), _T("1.png"),
_T("2.png"), _T("3.png"), _T("4.png")};
CSlideBmpView类私有成员private:
CRect m_rClient;//客户区大小
CImage m_img;//用于在内存中绘图
CDC m_bufferDC;//缓冲DC
CBitmap m_bufferBmp;//缓冲Bitmap
int m_index;//标记图片索引
int m_start;//标记图片起始位置
bool m_isStart;//标记是否是开始
bool m_isLeft;//标记图片移动方向
bool m_isLoad;//标记是否加载图片
(5). 由于我是用键盘控制图片出现的方向——方向键左键表示从左边往右出现,右键表示从右边往左边出现——添加键盘消息处理函数(WM_KEYDOWN)void CSlideBmpView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
//如果图片起始位置是起点,表示图片已经移动结束.这时才允许继续移动图片
if(!m_start)
{
//如果按下左键
if(nChar == VK_LEFT)
{
m_isLeft = true;
m_start = m_rClient.Width();
}
//如果按下右键
else if(nChar == VK_RIGHT)
{
m_isLeft = false;
m_start = -m_rClient.Width();
}
//设置定时器, 触发动画的开始
if(nChar == VK_LEFT || nChar == VK_RIGHT)
{
m_index = (m_index + 1) % N;
m_isLoad = true;
SetTimer(ID_TIMER_PAINT, 10, NULL);
InvalidateRect(NULL, false);
}
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
(6). 响应计时器消息——WM_TIMERvoid CSlideBmpView::OnTimer(UINT_PTR nIDEvent)
{
if(m_isLeft)
{
m_start -= 8;
}
else
{
m_start += 8;
}
//如果图片刚好布满客户区, 就停止移动
if(!m_start)
{
KillTimer(ID_TIMER_PAINT);
}
//在移动图片时不需要加载图片
m_isLoad = false;
/*
重绘
因为, 每次绘制都会覆盖上一次的, 因此不需要重绘背景
否则它会使用默认的白色画刷重绘背景, 闪屏会很厉害
*/
InvalidateRect(NULL, false);
CView::OnTimer(nIDEvent);
}
(7). 在CSlideView::OnDraw函数中绘图——响应Win32的WM_PAINTvoid CSlideBmpView::OnDraw(CDC* pDC)
{
CSlideBmpDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
//如果是窗口建立之初
if(m_isStart)
{
m_isStart = false;
GetClientRect(&m_rClient);
//创建缓冲DC
m_bufferDC.CreateCompatibleDC(NULL);
//创建缓冲Bitmap
m_bufferBmp.CreateCompatibleBitmap(pDC,
800, 600);
//将缓冲Bitmap选入缓冲DC中
m_bufferDC.SelectObject(m_bufferBmp);
}
//如果需要加载图片
if(m_isLoad)
{
//加载图片
m_img.Load(pszPngName[m_index]);
//在内存中绘图(缓冲DC)
m_img.Draw(m_bufferDC, 0, 0);
//取消图片与m_img对象的关联, 以使下一次顺利关联下一个图片
m_img.Detach();
}
//贴图
pDC->SetStretchBltMode(COLORONCOLOR);
pDC->StretchBlt(m_start, 0, m_rClient.Width(), m_rClient.Height(),
&m_bufferDC, 0, 0, 800, 600, SRCCOPY);
}
(8). 记得回收内存资源——在析构函数中处理CSlideBmpView::~CSlideBmpView()
{
//在析构中回收内存资源
m_bufferBmp.DeleteObject();
m_bufferDC.DeleteDC();
m_img.~CImage();
}
四、免费资源下载点击下载项目工程及源代码 欢迎对MFC感兴趣的朋友和我交流,结识志同道合的朋友是我的荣幸(^_^)
因此感叹一句:好久没写博客了啊——呵呵
ok,归入正题
今天上午看见Yorhom用HTML5实现了一个『HTML5梦幻之旅』-滚动播放的幻灯片效果,感觉不错。于是借着他给的灵感(刚好最近在学MFC),我用MFC来实现类似的效果(思想略有不同),先上一个效果gif:
下面我来讲解一下具体的实现
一、编译环境
VS2008企业完整版,创建一个MFC工程(项目名称为SlideBmp):
二、剖析实现思想先来看看我制作的一张示意图:
ClientWidth表示窗口客户区的宽度——这里就是View窗口
(1). 从左边出现的图片,预先放在横坐标为-ClientWidth的位置,然后启动计时器,增加横坐标,逐渐移动,直到横坐标为0——也就是到达了窗口客户区,就关闭计时器——这时图片刚好停留在客户区
(2). 同理,从右边出现的图片预先放在横坐标为ClientWidth处,后面的同(1)
这样实现的效果就是每次移动的图片会覆盖上一次的图片
怎么样,还是挺简单的吧?
三、剖析实现代码(1). 改变MFC默认的窗口外观——在CMainFrame::PreCreateWindow中,也就是创建框架窗口之前修改——代码如下:BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
//更改窗口外观
CSize size;
//获取屏幕像素大小
size.cx = ::GetSystemMetrics(SM_CXSCREEN);
size.cy = ::GetSystemMetrics(SM_CYSCREEN);
//窗口风格(快捷菜单式的窗口, 无边框、标题栏)
cs.style = WS_POPUPWINDOW;
//去掉菜单
cs.hMenu = NULL;
//窗口标题名称
cs.lpszName = _T("SlideBmp");
//自定义窗口大小, 并窗口居中显示
cs.x = (size.cx - 486 ) / 2;
cs.y = (size.cy - 360) / 2;
cs.cx = 486;
cs.cy = 360;
return TRUE;
}
(2). 因为View窗口是MainFrame框架窗口的子窗口,因此应该在它上面做工作——贴图片
(3). 在构造函数中进行初始化工作CSlideBmpView::CSlideBmpView()
{
//初始化
m_index = 0;
m_start = 0;
m_isStart = true;
m_isLeft = false;
m_isLoad = true;
}
(4). 所需变量或成员的定义,在SlideBmpView.h头文件中
全局变量//计时器ID
#define ID_TIMER_PAINT 100
//全局变量N——表示有几张图片
const int N = 5;
//图片的名称(这里也是路径)
const LPCTSTR pszPngName[] = {_T("0.png"), _T("1.png"),
_T("2.png"), _T("3.png"), _T("4.png")};
CSlideBmpView类私有成员private:
CRect m_rClient;//客户区大小
CImage m_img;//用于在内存中绘图
CDC m_bufferDC;//缓冲DC
CBitmap m_bufferBmp;//缓冲Bitmap
int m_index;//标记图片索引
int m_start;//标记图片起始位置
bool m_isStart;//标记是否是开始
bool m_isLeft;//标记图片移动方向
bool m_isLoad;//标记是否加载图片
(5). 由于我是用键盘控制图片出现的方向——方向键左键表示从左边往右出现,右键表示从右边往左边出现——添加键盘消息处理函数(WM_KEYDOWN)void CSlideBmpView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
//如果图片起始位置是起点,表示图片已经移动结束.这时才允许继续移动图片
if(!m_start)
{
//如果按下左键
if(nChar == VK_LEFT)
{
m_isLeft = true;
m_start = m_rClient.Width();
}
//如果按下右键
else if(nChar == VK_RIGHT)
{
m_isLeft = false;
m_start = -m_rClient.Width();
}
//设置定时器, 触发动画的开始
if(nChar == VK_LEFT || nChar == VK_RIGHT)
{
m_index = (m_index + 1) % N;
m_isLoad = true;
SetTimer(ID_TIMER_PAINT, 10, NULL);
InvalidateRect(NULL, false);
}
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
(6). 响应计时器消息——WM_TIMERvoid CSlideBmpView::OnTimer(UINT_PTR nIDEvent)
{
if(m_isLeft)
{
m_start -= 8;
}
else
{
m_start += 8;
}
//如果图片刚好布满客户区, 就停止移动
if(!m_start)
{
KillTimer(ID_TIMER_PAINT);
}
//在移动图片时不需要加载图片
m_isLoad = false;
/*
重绘
因为, 每次绘制都会覆盖上一次的, 因此不需要重绘背景
否则它会使用默认的白色画刷重绘背景, 闪屏会很厉害
*/
InvalidateRect(NULL, false);
CView::OnTimer(nIDEvent);
}
(7). 在CSlideView::OnDraw函数中绘图——响应Win32的WM_PAINTvoid CSlideBmpView::OnDraw(CDC* pDC)
{
CSlideBmpDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
//如果是窗口建立之初
if(m_isStart)
{
m_isStart = false;
GetClientRect(&m_rClient);
//创建缓冲DC
m_bufferDC.CreateCompatibleDC(NULL);
//创建缓冲Bitmap
m_bufferBmp.CreateCompatibleBitmap(pDC,
800, 600);
//将缓冲Bitmap选入缓冲DC中
m_bufferDC.SelectObject(m_bufferBmp);
}
//如果需要加载图片
if(m_isLoad)
{
//加载图片
m_img.Load(pszPngName[m_index]);
//在内存中绘图(缓冲DC)
m_img.Draw(m_bufferDC, 0, 0);
//取消图片与m_img对象的关联, 以使下一次顺利关联下一个图片
m_img.Detach();
}
//贴图
pDC->SetStretchBltMode(COLORONCOLOR);
pDC->StretchBlt(m_start, 0, m_rClient.Width(), m_rClient.Height(),
&m_bufferDC, 0, 0, 800, 600, SRCCOPY);
}
(8). 记得回收内存资源——在析构函数中处理CSlideBmpView::~CSlideBmpView()
{
//在析构中回收内存资源
m_bufferBmp.DeleteObject();
m_bufferDC.DeleteDC();
m_img.~CImage();
}
四、免费资源下载点击下载项目工程及源代码 欢迎对MFC感兴趣的朋友和我交流,结识志同道合的朋友是我的荣幸(^_^)
相关文章推荐
- 【VC++游戏开发#四】2D篇 —— 物理建模(一) 匀速直线运动:字符串"撞墙反弹"效果
- 【VC++游戏开发#六】2D篇 —— 粒子系统(一):浪漫唯美的场景之雪花飞舞
- 【VC++游戏开发#七】2D篇 —— 物理建模(二) 重力模拟:让愤怒的小鸟来感受一次自由落体运动
- 【VC++游戏开发#五】2D篇 —— 游戏之二:看看你能坚持多少秒
- 【VC++游戏开发#九】2D篇 —— 粒子系统(二):平安夜特别版——星光四射
- 【VC++游戏开发#八】2D篇 —— 动画:一个跑酷游戏的小Demo
- 【VC++游戏开发#三】2D篇 —— 游戏之一:空中大战(SpaceWar)
- 【VC++游戏开发#十】2D篇 —— 人工智能(一):滚动地图 & 用鼠标控制人物的走动
- 【VC++游戏开发#十二】2D篇 —— 人工智能(二):最短路径 & 智能越过障碍 By BlueCoder
- 【COCOS2DX-游戏开发之三十】抖动效果 CCShake
- 【 Visual C++】游戏开发笔记之二——最简单的DirectX,vc窗口的编写
- 基于VC的扫雷游戏开发
- 游戏开发笔记之二——最简单的DirectX,vc窗口的编写
- 【cocos2d-x IOS游戏开发-城市跑酷7】设计烟囱与烟的效果
- VC++游戏开发基础系列从入门到精通
- 【 Visual C++】游戏开发笔记之二——最简单的DirectX,vc窗口的编写
- 【小松教你手游开发】【游戏渲染】unity海边波浪效果的实现
- VC++游戏开发基础系列从入门到精通
- 图像平滑滚动效果的VC实现(http://www.zahui.com/html/1/569.htm)
- C++ 3D游戏开发 逼真的鱼池效果-ogre