本系列文章由七十一雾央编写,转载请注明出处。
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;
//每绘制一次,帧数加1
frameCount++;
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; //缓冲DC
CBitmap 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
// CChildView
CChildView::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);
//创建缓冲DC
m_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);
//释放缓冲DC
m_cacheDC.DeleteDC();
//释放对象
m_cacheCBitmap.DeleteObject();
//释放窗口DC
ReleaseDC(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++游戏开发中,平滑动画是一种提升用户体验的关键技术,它使得游戏中的角色移动、物体旋转等动作看起来更加流畅自然。本笔记主要探讨的是如何在MFC(Microsoft Foundation Classes)框架下实现平滑动画,这通常是...
本文将深入探讨如何使用Visual C++进行游戏编程,特别是在实现多背景循环动画方面的技巧和注意事项。 首先,我们要理解多背景的概念。在游戏场景中,背景通常用于营造环境,可能是静态的,也可能是动态的。多背景则...
《Visual C++游戏开发经典案例详解》这本书是针对使用Visual C++进行游戏开发的专业指南,旨在帮助读者通过实例学习和掌握C++编程语言在游戏开发中的应用。书中的内容覆盖了从基础到高级的游戏开发技术,包括图形...
在游戏开发中,动画是至关重要的元素,它赋予游戏生命力和动态感。在Visual C++环境中,游戏编程涉及许多技术,其中之一就是透明动画的实现。本文将深入探讨如何使用Visual C++来创建具有透明效果的动画,这对于创建...
C++服务器开发精髓笔记 本笔记涵盖了C++服务器开发的精髓知识点,从RAII到Pimpl、C++11新特性、统一的类成员初始化语法、std::initializer_list、C++17注解标签等多方面内容。 1. RAII(Resource Acquisition Is ...
《Visual C++ 游戏开发案例实战》是一本专注于利用Microsoft的Visual C++环境进行游戏开发的实践教程。这本书由王浩编著,由清华大学出版社出版,提供了高清且带有书签的阅读体验,方便读者查阅和学习。在本书中,...
c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏c++小游戏...
在本篇中,我们将深入探讨使用Visual C++进行游戏编程的相关知识,特别是在开发回合制游戏方面的实践。Visual C++是一款强大的编程环境,尤其适合构建复杂的游戏应用,它提供了丰富的库支持,如MFC (Microsoft ...
C++学习笔记C++学习笔记C++学习笔记C++学习笔记C++学习笔记
在C++游戏开发中,战争迷雾是一种常见的游戏机制,用于模拟战场上的视线限制,增加游戏的策略性和趣味性。本笔记将详细讲解如何实现平滑过渡的战争迷雾效果,以及提供相应的源代码供参考。 战争迷雾的核心是通过...
在本篇【Visual C++】游戏编程学习笔记中,我们将探讨如何使用Microsoft的Visual C++进行2D游戏的开发。2D游戏小demo是初学者理解游戏编程基础的一个绝佳起点,它涵盖了游戏循环、图形绘制、事件处理以及基础的游戏...
《Visual C++.NET小游戏开发时尚编程百例》是一本专注于使用Microsoft的Visual C++.NET进行游戏开发的实战书籍。本书涵盖了从实例51到100的多种游戏编程案例,旨在帮助读者通过实际操作深入理解C++编程语言,以及...
【标题】:“C++ 游戏开发小项目” 在C++的世界里,游戏开发是一项充满挑战和乐趣的任务。C++是一种强大的、通用的编程语言,以其面向对象的特性、高效性能以及对底层硬件的直接控制而备受游戏开发者青睐。本项目以...
C++Primer中文第三版(C++从入门到精通)第一章的读书笔记,主要是C++程序、预处理器指示符、iostream库等的基础知识点读书笔记。
- **游戏开发**:C++ 因其高性能特性,被广泛应用于游戏开发。 - **科学计算**:在科研领域,C++ 能够处理复杂的数学模型和大规模的数据集。 - **网络通讯**:如 ACE(Adaptive Communication Environment)这样的...
在本文中,我们将深入探讨如何使用C++基于SDK来开发一款类似iPhone上的“求合体”小游戏。这种类型的游戏通常包含音乐元素、计分系统以及趣味性的互动玩法。首先,让我们了解一下C++语言和SDK在游戏开发中的角色。 ...
首先,标题“C++游戏编程入门.pdf”明确指出这是一本介绍如何使用C++进行游戏开发的教材。C++是一种广泛应用于游戏开发的强大编程语言,因为它提供了对硬件的直接访问能力,能够实现高性能的图形处理和复杂的逻辑...
了解类、对象、模板、指针等核心概念是开发游戏的基础。 2. **游戏引擎**:许多C++游戏可能使用了流行的游戏引擎,如Unity(部分支持C++)、Unreal Engine(主要支持C++)或其他开源引擎,如SFML或SDL。游戏引擎...
在本次C++编程实践中,我们以经典游戏“坦克大战”为例,结合Qt库进行开发。Qt是一个跨平台的应用程序开发框架,广泛应用于桌面和移动设备。这个项目的目标是帮助开发者深入理解和运用C++面向对象编程,同时体验游戏...
C++(qt)游戏实战项目:...此坦克游戏采用c++开发,有详细的源码解析 已实现功能 地图编辑m,地图加载l,地图保存s 玩家坦克移动asdw 发射子弹j 发挥你的才智实现以下功能吧: 关卡设计 坦克打怪升级 坦克魔法攻击 等