本系列文章由七十一雾央编写,转载请注明出处。

http://blog.csdn.net/u011371356/article/details/9430645

作者:七十一雾央 新浪微博:http://weibo.com/1689160943/profile?rightmod=1&wvr=5&mod=personinfo

在上一节雾央讲解了一下平面的障碍物判定,本来打算讲解一下斜坡的障碍物判定,但是有朋友推荐了一片文章,对障碍物判定讲解的非常好,雾央就直接把地址贴出来,就不重复了。

2D游戏中的障碍判定

这篇文章讲解了2D游戏的很多东西,大家可以好好看看,雾央也很希望研究游戏开发的朋友们可以推荐下好文章,一起进步,在此谢过。

另外,从本节笔记开始,雾央决定把系列名称改为《C++编程》,具体原因请移步笔记一

在之前雾央讲解了背景滚动,不知道大家发现了没有,在人物移动的时候,画面是一卡一卡的,原因是由于大家按下方向键移动后,人物突然移动一段距离,而背景也跟随着移动一段距离,突然变化的画面给大家带来的就是一种卡的感觉。在之后的粒子系统中有同学提到颤抖的小雪花同样是这样,在每次绘制的时候,都让雪花增加了一段确定的位移,但是由于计算机状态的不断变化,每两帧之间的时间差不同,所以雪花的移动也就不规律。有颤抖的感觉。

由上面的讨论,大家应该能知道,在游戏中,通过增加确定的位移来改变某个事物的位置往往是不准确的,也会导致画面不流畅。但是思考这种抖动产生的原因,我们可以受到很多启发,我们是不是可以减少每次移动的距离,而增加移动的频率来达到同样的位置呢?这样就可以大大降低画面的违和感,而最高的频率也就是和画面绘制的频率一致了,因此我们可以在每次绘制画面的时候进行较小幅度的更新,而为了摆脱两次绘制之间的时间差的波动,我们需要计算时间差,更新幅度和时间差关联起来。

最直接的实现方式当然就是通过时间和速度来达到上述效果了。

比如对于人物,我们给予他x和y方向上的速度,通过时间乘以速度来更新他的位置。玩家的操作会改变人物的方向和速度。粒子系统亦然。

大家知道了原理之后,在实现之前,我们首先要解决几个问题

首先是获取两帧之间的时间差。这个要求我们记录下上次绘制的时间,在每次绘制时候获取当前时间,两者之间差值即为所求。大家可以使用全局变量来记录上次绘制的时间,在这里雾央使用的是静态变量。

static float lastTime=timeGetTime();
static float currentTime=timeGetTime();
currentTime=timeGetTime();
//状态更新
lastTime=currentTime;

这样之后即可以得到平滑的动画了,雾央仍然是以之前的粒子系统中的雪花为例,这样大家就可以和以前的方式进行一下对比,就可以更清楚的看出差别了。

另外,像这样获取帧之间的时间差后,大家就可以计算出FPS了,帧数的计算很简单,就是画面绘制的次数除以绘制花费的时间即可,为了不让帧数不断的跳动,大家可以隔一段时间比如1秒计算一次。

//获取当前FPS
float CalculateFPS()
{static float  fps = 0;           //FPS值   static int    frameCount = 0;    //帧数   static float  currentTime =0.0f;   static float  lastTime = 0.0f;  //每绘制一次,帧数加1frameCount++;           currentTime = timeGetTime()/1000.0f;//大家是可以每绘制一次就计算一次帧数,即1/时间即可,这样得到的是实时帧数,
//但是由于时间间隔太短,帧数变化太快,显示在窗口上也看不清,所以每隔一段时间计算一次 if(currentTime - lastTime > 1.0f) //将时间控制在1秒钟   {  fps = frameCount /(currentTime - lastTime);//计算这1秒钟的FPS值   lastTime = currentTime; frameCount    = 0;        }  return fps;
}

在之前的笔记中,涉及到动画都使用了定时器,雾央在初学游戏的时候,代码里是一大堆的定时器,呵呵。但是现在我们就可以不再使用这么多的定时器,如果大家直接使用WIN32,可以完全不使用定时器了,但是用MFC的话,大家需要使用一个定时器驱动OnPaint绘图,剩余的动画大家就可以利用计算时间差来更新了

比如每100毫秒更新一帧的动画

if(currentTime-lastTime>100.0f)更新下一帧

另外大家需要注意一点,贴图的时候是按像素定的位置,也就是位置必须是整型的数值,但是由于采用时间*速度来计算位置,每次加上的部分很小,所以xy坐标都要是浮点型的,但是绘制的时候是取了整的。

和上一次讲粒子系统不同的是,这一次雾央封装了一个粒子类,代码看上去比以前整洁多了,另外雾央这一次把宏定义放到stdafx.h中去了,因为多个文件都需要使用到一些宏定义。

雾央先定义了一个粒子的结构体

struct snow
{float x;float y;float speed; //速度int number;  //粒子图像编号
};

下面是粒子类

class CParticle
{
private:int m_number; //数量struct snow *m_pSnow;  //雪花CImage m_snowMap[7]; //七种雪花图像
public:CParticle(int number);~CParticle();public:void Init();  //初始化粒子void Draw(CDC &cDC);  //绘制粒子void Update(float time);//更新粒子
};

粒子类的具体实现

#include"stdafx.h"
#include"particle.h"CParticle::CParticle(int number)
{m_number=number;m_pSnow=new struct snow[m_number];
}void CParticle::Init()
{//加载雪花图像char buf[20];for(int i=0;i<7;i++)    //加载七种图像{sprintf(buf,"Snow//%d.png",i);m_snowMap[i].Load(buf);TransparentPNG(&m_snowMap[i]);}//初始化雪花粒子for(int i=0;i<m_number;i++){m_pSnow[i].x=rand()% WINDOW_WIDTH;   //最初雪花在水平方向上随机出现m_pSnow[i].y=rand()% WINDOW_HEIGHT; //垂直方向上也是随机出现m_pSnow[i].number=rand()%7;         //七种雪花中的一种m_pSnow[i].speed=(rand()%5+1)/20.0;}
}void CParticle::Draw(CDC &cDC)
{//绘制雪花粒子for(int i=0;i<m_number;i++)m_snowMap[m_pSnow[i].number].Draw(cDC,m_pSnow[i].x,m_pSnow[i].y,32,32);
}void CParticle::Update(float time)
{for(int i=0;i<m_number;i++){m_pSnow[i].y+=time*m_pSnow[i].speed;if(m_pSnow[i].y>WINDOW_HEIGHT)m_pSnow[i].y=-32;  }
}CParticle::~CParticle()
{delete[] m_pSnow;
}

最后是CChildView头文件

// ChildView.h : CChildView 类的接口
//#pragma once
#include "particle.h"// CChildView 窗口class CChildView : public CWnd
{
// 构造
public:CChildView();// 特性
public:CRect m_client;    //保存客户区大小CImage m_bg;      //背景图片CParticle *m_snow;CDC m_cacheDC;   //缓冲DCCBitmap m_cacheCBitmap;//缓冲位图
// 操作
public:// 重写protected:virtual BOOL PreCreateWindow(CREATESTRUCT& cs);// 实现
public:virtual ~CChildView();// 生成的消息映射函数
protected:afx_msg void OnPaint();DECLARE_MESSAGE_MAP()
public:afx_msg void OnTimer(UINT_PTR nIDEvent);afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
};

CPP文件

//-----------------------------------【程序说明】----------------------------------------------
// 【MFC游戏开发】笔记十一 平滑动画 配套源代码
// VS2010环境
// 更多内容请访问雾央CSDN博客 http://blog.csdn.net/u011371356/article/category/1497651
// 雾央的新浪微博: @七十一雾央
//------------------------------------------------------------------------------------------------// ChildView.cpp : CChildView 类的实现
//#include "stdafx.h"
#include "GameMFC.h"
#include "ChildView.h"#include "mmsystem.h"
#pragma comment(lib,"winmm.lib")//导入声音头文件库#ifdef _DEBUG
#define new DEBUG_NEW
#endif// CChildViewCChildView::CChildView()
{
}CChildView::~CChildView()
{mciSendString("stop bgMusic ",NULL,0,NULL);delete m_snow;
}BEGIN_MESSAGE_MAP(CChildView, CWnd)ON_WM_PAINT()ON_WM_TIMER()ON_WM_CREATE()
END_MESSAGE_MAP()// CChildView 消息处理程序BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
{if (!CWnd::PreCreateWindow(cs))return FALSE;cs.dwExStyle |= WS_EX_CLIENTEDGE;cs.style &= ~WS_BORDER;cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);//-----------------------------------游戏数据初始化部分-------------------------//加载背景m_bg.Load("bg.png");//打开音乐文件mciSendString("open background.mp3 alias bgMusic ", NULL, 0, NULL);mciSendString("play bgMusic repeat", NULL, 0, NULL);m_snow=new CParticle(100);//初始化m_snow->Init();return TRUE;
}void CChildView::OnPaint()
{static float lastTime=timeGetTime();    static float currentTime=timeGetTime();//获取窗口DC指针CDC *cDC=this->GetDC();//获取窗口大小GetClientRect(&m_client);//创建缓冲DCm_cacheDC.CreateCompatibleDC(NULL);m_cacheCBitmap.CreateCompatibleBitmap(cDC,m_client.Width(),m_client.Height());m_cacheDC.SelectObject(&m_cacheCBitmap);//————————————————————开始绘制——————————————————————//贴背景,现在贴图就是贴在缓冲DC:m_cache中了m_bg.Draw(m_cacheDC,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,0,0,WINDOW_WIDTH,WINDOW_HEIGHT);//贴雪花m_snow->Draw(m_cacheDC);//更新雪花currentTime=timeGetTime();m_snow->Update(currentTime-lastTime);lastTime=currentTime;//最后将缓冲DC内容输出到窗口DC中cDC->BitBlt(0,0,m_client.Width(),m_client.Height(),&m_cacheDC,0,0,SRCCOPY);//————————————————————绘制结束—————————————————————//在绘制完图后,使窗口区有效ValidateRect(&m_client);//释放缓冲DCm_cacheDC.DeleteDC();//释放对象m_cacheCBitmap.DeleteObject();//释放窗口DCReleaseDC(cDC);
}//定时器响应函数
void CChildView::OnTimer(UINT_PTR nIDEvent)
{OnPaint();
}int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{if (CWnd::OnCreate(lpCreateStruct) == -1)return -1;// TODO:  在此添加您专用的创建代码//创建一个10毫秒产生一次消息的定时器SetTimer(TIMER_PAINT,10,NULL);return 0;
}

来看几张运行图片,大家可能从画面上看不出和上次的区别,但是实际运行一下,大家就知道要比上次的强几百倍了


另外,这次每个雪花的速度雾央也设置成一定范围内随机的,看起来更带感了

具体的就留给大家下载demo回去自己体会了

PS:背景音乐是雅尼的夜莺,很好听的曲子,希望大家能喜欢。

本节笔记源代码点击这里下载

《C++游戏开发》笔记十一到这里就结束了,更多精彩请关注下一篇。如果您觉得文章对您有帮助的话,请留下您的评论,点个赞,能看到你们的留言是我最高兴的事情,因为这让我知道我正在帮助曾和我一样迷茫的少年,你们的支持就是我继续写下去的动力,愿我们一起学习,共同努力,复兴国产游戏。

对于文章的疏漏或错误,欢迎大家的指出。

《C++游戏开发》笔记十一 平滑动画:不再颤抖的小雪花相关推荐

  1. 【Visual C++】游戏开发笔记之八——基础动画显示(二)游戏循环的使用

    在笔记七中我们讲解了用定时器来产生动画的效果.定时器的使用固然简单方便,但是事实上这样的方法仅适合用在显示简易动画及小型的游戏程序中.因为一般而言,游戏本身需要显示顺畅的游戏画面,使玩家感觉不到延迟的 ...

  2. 【Visual C++】游戏开发笔记四十三 浅墨DirectX教程十一 为三维世界添彩:纹理映射技术(二)...

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 作者:毛星云(浅墨)    邮箱: happylifemxy@163.com 本篇文章里,我们首先对Direct3D之中固定功能流水线中的 ...

  3. 【Visual C++】游戏开发笔记四十三 浅墨DirectX教程十一 为三维世界添彩 纹理映射技术 二

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  4. 【Visual C++】游戏开发笔记四十一 浅墨DirectX教程之九 为三维世界添彩:纹理映射技术(一)...

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhmxy555/article/details/8523341 作者:毛星云(浅墨 ...

  5. 【Visual C++】游戏开发笔记二十一 游戏基础物理建模 三 摩擦力系统模拟

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  6. 【Visual C++】游戏开发笔记三十一 回归季:游戏开发资料整理打包下载专栏行文思路整理

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. http://blog.csdn.net/zhmxy555/article/details/8147229 作者:毛星云    邮箱: h ...

  7. 【Visual C++】游戏开发笔记三十一 回归季 游戏开发资料整理打包下载 专栏行文思路整理

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  8. 【Visual C++】游戏开发笔记四十 浅墨DirectX教程之八 绘制真实质感的三维世界:光照与材质专场...

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 作者:毛星云(浅墨)    邮箱: happylifemxy@163.com 本篇文章里,我们对Direct3D之中固定功能流水线中的3D ...

  9. 【Visual C++】游戏开发笔记三十四 浅墨DirectX提高班之三 起承转合的艺术:Direct3D渲染五步曲...

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhmxy555/article/details/8223965 作者:毛星云(浅墨 ...

最新文章

  1. android media sessiom,如何使用新的MediaSession类在Android 5.x上接收...
  2. C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决
  3. Effective Java之努力使失败保持原子性(六十四)
  4. Windows 8实例教程系列 - 布局控制
  5. Java SE 9:使用IntelliJ IDE开发和测试模块之间的隐式可读性(第5部分)
  6. R语言怎么写积分_2020年上海居住证积分申请表怎么填?个人履历该怎么写?—积分落户服务站...
  7. java等额本息、等额本金计算 记录一下
  8. java实现正态分布累积分布,标准正态分布变量的累积概率分布函数
  9. 血泪合集,uniapp超长实践精华总结~
  10. win10开启移动热点,手机无法获取ip地址
  11. EasyNVR网页/微信播放RTSP摄像机HLS/RTMP播放时出现起播等待问题的优化过程
  12. 10.27Unity事件、场景烘焙、Render Texture
  13. SQL Server数据库学习总结及T-SQL语法使用实战
  14. 有哪些微信小程序值得推荐?
  15. 高新技术企业的申报条件及要求
  16. android恢复出厂设置流程分析
  17. 6009. 【THUWC2019模拟2019.1.18】Counting
  18. tikz中谐振子(弹簧)的绘制,以及声子色散关系的绘制
  19. 通达信dll接口什么意思?
  20. 听说尤雨溪在开发vue4.0?是谁煽动了前端圈的焦虑情绪

热门文章

  1. 42、C++ Primer 4th笔记,IO库,未格式化IO操作
  2. 在Windows下编译OpenSSL(VS2005)【转】
  3. C# 最小化到系统托盘的实现(一)
  4. idea 包拆分_idea springboot项目拆分多模块踩坑记(1)
  5. edittext在哪可以获取有效值_java-从EditText获取文本字符串?
  6. 高性能游戏本搭服务器,为吃鸡而生,这几款高性能游戏本不容错过!
  7. 怎么只要小数部分C语言,如何得出一个浮点数的小数部分,要把各个位保存到一个数组里边。...
  8. python如何制作一个工程软件_如何利用python制作一个解压缩软件-Go语言中文社区...
  9. mfc搜索新建access字段_vs2010MFC中使用ODBC链接ACCESS数据库,怎样编写查找功能?...
  10. mysql存储过程应用_mysql 存储过程的应用