游戏开发技术总结(经典之作)第三集 让图片动起来----快速切换图形实现动画

源代码下载地址---------->第三集源代码

转载请注明出处

 

3-1 任务


       我们这里将利用VC 的时钟消息函数,在屏幕上显示变换的图形,由此形成动画的
效果。


3-2 建立时钟消息


       在 VC 编程环境中选择菜单View 下的“ ClassWizard”项,进入MFC 的类向导(MFC
ClassWizard) 。

游戏开发技术总结(经典之作)第三集 让图片动起来----快速切换图形实现动画_第1张图片

                                                        图3-1
       在类向导中选择WM_TIMER, 双击后,在成员功能栏(Member functions)可以看到已生
成的时钟消息函数ON_WM_TIMER。再按编辑代码(Edit Code), 就进入到时钟消息函数
OnTimer()中了。


void CMyDlg::OnTimer(UINT nIDEvent) //时钟函数,[类向导中定义生成]
{
CDialog::OnTimer(nIDEvent);
}



在程序中我们只要执行命令SetTimer(1,150,NULL),在程序运行期间每隔150 毫秒,
时钟消息函数OnTimer()中的程序就会被执行一次。
SetTimer 参数说明:
SetTimer(1, 150, NULL);
设定时器(第1 个定时器, 间隔时间(毫秒), 空值);

 


3-3 让角色动起来


       我们在时钟消息里面写上图形显示的程序,图形就可以快速地变化了。当然你要
调入的图形是变化的,而要调入的图形变化,只要调入的图形文件名变化就行了。


3-3-1 变化的文件名


        如果图形文件在 C 盘的game 目录下,并且文件名为“b0p.bmp”,我们要给出的文
件名格式就应该为“c:/game/b0p.bmp”。其中p 是顺序变化的。

                                                  图 3-2
        在C++语言中我们可以用sprintf(cc,"c:/game/b%02d.bmp", p); 来设置图形文件名。cc
是字符串型变量,springf 是字符格式化函数,它可以将其它数据类型的值转换为字符串。
注意,引号内的写法 %02d,“%”的意义是转换的数p 是整形数,其中“2”的意义
是转换后的字串是两位,“0”的意义是数字不足两位时前面用“ 0”补位。
如 p=2, 执行sprintf(cc,"c:/game/b%02d.bmp", p);后。
cc 的值就是“c:/game/b02.bmp”。
如 p=12, 执行sprintf(cc,"c:/game/b%02d.bmp", p);后。
cc 的值就是“c:/game/b12.bmp”。
为了使用方便,这里我们把动态获取将调用的图形文件名也写成一个功能函数
getpic(⋯)。


3-3-2 getpic(⋯)调图片到相关位图


 

//**************************************************
// getpic(CString cc,int p) 调图片到相关位图
// 由p 得到将调的图形文件名。
// 在指定目录中调入图形到相关位图bit
//**************************************************
BOOL getpic(CString cc,int p)//调图片到相关位图[2 章]
{ char name[256];
SetCurrentDirectory(appdir); //置当前目录
sprintf(name,"%s%s/c%05d.bmp",dir,cc,p);//生成将调的图形文件名
loadbmp(name); //调BMP 图片
return TRUE;
}



这个函数的功能是:根据输入的目录名cc 和图形编号p,生成将调用的图形文
件名。
例如:dir = “图形/”,cc =“人” ,p =15;
则:name=“图形/人/00015.bmp” ;
接着就将调入 loadbmp(“图形/人/00015.bmp”) 图片到bit 中。
好了,我们可以将前面的调入图形文件和显示图形的代码写入时钟消息OnTimer()
中(程序中的行号是为了便于注释加的)。


3-3-3 可以动了


设定:p=0;m0=0; m1=400;dir="c:/game/";
现在,程序每隔150 毫秒, 时钟消息函数OnTimer()中的程序就会被执行一次。

void CMyDlg::OnTimer(UINT nIDEvent) //时钟函数,[类向导中定义生成]
1{ CClientDC dc(this); //客户区设备环境
2 if(getpic("人",p)==FALSE) //调角色图片
3 {AfxMessageBox(cc+"没找到!");return;}
4 SelectObject(MemDC,bit); //设备相关位图关联到暂存设备场景
5 BitBlt(dc.m_hDC,200,160,w,h,MemDC,0,0,SRCCOPY);//显示游戏角色
6 p++; //下一动作
7 if(p>m1) p=m0; //若动作完成,重复。
CDialog::OnTimer(nIDEvent);
}



当第一次执行时,p=0;
第 2 行,调入"c:/game/人/c00000.bmp"到bit。
第 4 行,将目录bit 内容调入并关联到MemDC 中。
第 5 行,将MemDC 中的图形拷贝到当前显示区在屏幕上显示。
第 6 行,p 加1,变成了1。
第二次执行时,p=1;
程序将目录 c:/game/人/下名为"c00001.bmp"图片内容在屏幕上显示。
第 n 次执行时, p=n-1;
程序将目录 c:/game/人/下名为"c0000p.bmp"图片内容在屏幕上显示。
每次在第 7 行判断p 是否达到m1(=400)的值,若达到,p=m0(=0); 从头开始。
如此400 幅动作不同的图片在屏幕上循环显示,一个活生生的人物就出现了。


3-3-4 OnOK()启动时钟


         现在我们还有一个事,在OnOK()中写入时钟消息函数的启动程序。

void CMyDlg::OnOK() //确定键,[类向导中定义生成]
{ GetDlgItem(IDC_EDIT1)→ShowWindow(SW_HIDE);//隐藏文本框
CClientDC dc(this); //客户区设备环境
GetWindowRect(rect); //取当前窗口尺寸
BitBlt(dc.m_hDC,0,0,rect.Width(),rect.Height(),MemDC,0,0,SRCCOPY);
//将背景拷贝到当前屏幕
//启动时钟
SetTimer(1,150,NULL); //设定时器150 毫秒
}



        现在程序运行后,只要一按“确定键”,程序首先将背景拷贝到当前屏幕,然后启
动时钟,活动的人就在屏幕上出现了。


3-4 窗口、控件的基本操作


       在程序的界面设计中,我们常需要对窗口和各种控件的大小、位置进行控制。
VC++对窗口、各种控件的一些基本操作命令如下:
GetDlgItem(IDC_EDIT1)→EnableWindow(FALSE); //控件失效、有效TRUE
GetDlgItem(IDC_LIST1)→ShowWindow(SW_HIDE); //控件隐藏、显示SW_SHOW
GetDlgItem(IDC_EDIT1)→SetWindowText(“cc”); //控件上显示cc 字串
GetDlgItem(IDCANCEL)→MoveWindow( x,y,w,h,TRUE); //控件大小、定位
SetDlgItemText(IDC_STATIC0, "cc"); //控件上显示cc 字串
GetDlgItemText(IDC_STATIC0, "cc"); //取控件上文字到字串cc
MoveWindow(x,y,w,h); //当前窗口定位
CenterWindow(); //当前窗口居中
例如:SetDlgItemText(IDC_EDIT1,cc) 将字符串cc 显示在编辑框“ IDC_EDIT1”上。
例如:MoveWindow(0,0,640,480); 当前窗口大小、定位。
例如:CenterWindow(); 当前窗口居中。
例如:GetDlgItem(IDOK)→MoveWindow(580,0,55,18,TRUE)
确定按钮控件“IDOK”的大小(w=55,h=18)、定位(x=580,y=0)。
为了给读者一个对程序的完整的认识 ,下面我们给出这一章的全部程序和注释。
在这一章我们编的程序只在“让我动吧Dlg.cpp”这一个文件中。


3-5 “让我动吧 Dlg.cpp”完整的程序和注释


3-5-1 全局定义


注,灰色部份为MFC 自动产生的。

#include "stdafx.h"
#include "让我动吧.h"
#include "让我动吧Dlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////
// 全局变量定义
/////////////////////////////////////////////
HBITMAP bit; //设备相关位图
HDC MemDC; //角色设备场景
int w,h; //图形尺寸
CString dir; //定义路径变量
CString cc; //公用变量
char appdir[256]; //当前目录
CRect rect; //定义窗口尺寸变量
int js; //角色[0 男,1 女]
int fw; //方位[0 南1 西南2 西3 西北4 北5 东北6 东7 东南]
int m0; //动画初值
int m1; //动画终值
int p; //当前图形序号
////////////////////////////////////////////
// 函数定义
////////////////////////////////////////////
BOOL getpic(CString cc,int p); //调图片到相关位图
BOOL loadbmp(CString cc); //调BMP 图片

 

 


3-5-2 程序的初始入口


 

 

CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/)//[MFC 自动生成]
: CDialog(CMyDlg::IDD, pParent)
{⋯⋯}
void CMyDlg::DoDataExchange(CDataExchange* pDX)//[MFC 自动生成]
{⋯⋯}
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)//[MFC 自动生成]
⋯⋯
END_MESSAGE_MAP()
BOOL CMyDlg::OnInitDialog()//对话框程序的初始入口,[MFC 自动产生]
{CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
//A.显示说明信息
CString cc;
cc="\r\n 这是《学VC 编游戏》的第二个示例:\r\n\r\n";
cc+=" 在这一章我们使用了以下知识、技术\r\n";
cc+="1.介绍计算机动画的基本知识和实现方法\r\n";
cc+="2.在VC++中建立时钟消息,使用时钟消息产生动画。\r\n";
cc+="3. 介绍格式化函数springf()的用法。由此动态地获取图形文件名。\r\n";
cc+="4.介绍在VC++中窗口、控件大小控制和定位的方法。\r\n";
SetDlgItemText(IDC_EDIT1,cc);
//B.窗口定位
MoveWindow(0,0,640,480); //窗口定位
CenterWindow(); //居中窗口
GetDlgItem(IDOK)→MoveWindow(640-60,0,55,18,TRUE);//确定按钮控件位置
//C.建立图形环境
MemDC =CreateCompatibleDC(0); //创建设备场景
//D.设主角数据
js=0; //角色,0 号人物
fw=0; //方位,0 南
m0=js*400+fw*4; //初值,0 号人物首位置
m1=(js+1)*400-1; //终值,1 号人物首位置
p=m0; //当前图形序号
//E.设置路径
GetCurrentDirectory(256,appdir); //取当前目录
dir=appdir;
if(dir.Right(8)=="运行程序")
dir="图片/";
else dir="../运行程序/图片/"; //图片路径
//F.调入显示背景
loadbmp(dir+"地面.BMP"); //调背景图片
SelectObject(MemDC,bit); //调入位图关联到地图设备场景
//G.在背景上显示文字
SetTextColor(MemDC,RGB(255,255,255)); //设置地图设备场景字色
SetBkMode(MemDC,TRANSPARENT); //字为透明方式
cc="嘿嘿!我可以动了!!走走、跑跑,世间真美好!!!"; //设文字内容
TextOut(MemDC,150,100,cc,lstrlen(cc)); //在MemDC 显示文字
SetTextColor(MemDC,RGB(255,255,255)); //设置地图设备场景字色
cc="不对呀,弄个框框把我笼起? 放开我 !!!"; //
TextOut(MemDC,150,220,cc,lstrlen(cc)); //在MemDC 显示文字
cc="BMP 图片本身就是矩形的,图片的底色是白色。";
TextOut(MemDC,151,250,cc,lstrlen(cc)); //在MemDC 显示文字
return TRUE;
}

 

 

 

3-5-3 OnOK()确定键


 

void CMyDlg::OnPaint() //[MFC 自动生成]
{⋯⋯
}
HCURSOR CMyDlg::OnQueryDragIcon()//[MFC 自动生成]
{⋯⋯
}
void CMyDlg::OnOK() //确定键,[类向导中定义生成]
{GetDlgItem(IDC_EDIT1)→ShowWindow(SW_HIDE);//隐藏文本框
CClientDC dc(this); //客户区设备环境
GetWindowRect(rect); //取当前窗口尺寸
BitBlt(dc.m_hDC,0,0,rect.Width(),rect.Height(),MemDC,0,0,SRCCOPY);
//启动时钟
SetTimer(1,150,NULL); //设定时器150 毫秒
}


 

3-5-4 OnCancel()退出程序


 

void CMyDlg::OnCancel() //退出,[类向导中定义生成] 

{ 

DeleteDC(MemDC); //删除暂存设备场景 

DeleteObject(bit); //删除暂存设备相关位图

CDialog::OnCancel(); 

}


 

 

 

3-5-5 时钟函数


 

void CMyDlg::OnTimer(UINT nIDEvent) //时钟函数,[类向导中定义生成]
{ CClientDC dc(this); //客户区设备环境
if(getpic("人",p)==FALSE) //调角色图片
{AfxMessageBox(cc+"没找到!");return;}
SelectObject(MemDC,bit); //设备相关位图关联到暂存设备场景
BitBlt(dc.m_hDC,200,160,w,h,MemDC,0,0,SRCCOPY); //显示游戏角色
p++; //下一动作
if(p>m1) p=m0; //若动作完成,重复。
CDialog::OnTimer(nIDEvent);
}


 


3-5-6 调图片到相关位图


 

//**************************************************
// getpic(CString cc,int p) 调图片到相关位图
// 由p 得到将调的图形文件名。
// 在指定目录中调入图形到相关位图bit
//**************************************************
BOOL getpic(CString cc,int p)//调图片到相关位图
{ char name[256];
SetCurrentDirectory(appdir); //置当前目录
sprintf(name,"%s%s/c%05d.bmp",dir,cc,p); //生成将调的图形文件名
loadbmp(name); //调BMP 图片
return TRUE;
}


 


3-5-7 调BMP 图片


 

//**************************************************
// loadbmp(CString cc)//调BMP 图片
// 调cc 指定的图形;取得的图形在设备相关位图bit 中
// 图形的宽、高存于全局变量w,h 中
//**************************************************
BOOL loadbmp(CString cc)//调BMP 图片
{ DeleteObject(bit); //删除上次的位图内存。
bit=(HBITMAP)LoadImage //调入cc 指定的图形
(AfxGetInstanceHandle(),//
cc, //文件名
IMAGE_BITMAP, //位图方式
0, //图形宽
0, //图形高
LR_LOADFROMFILE|LR_CREATEDIBSECTION//方式
);
if(bit==NULL) return FALSE; //调图失败
DIBSECTION ds; //
BITMAPINFOHEADER &bm = ds.dsBmih; //
GetObject(bit,sizeof(ds),&ds); //取位图的信息→bminfo
w = bm.biWidth; //得到位图宽度值
h = bm.biHeight; //得到位图高度值
return TRUE;
}


 

 

3-6 有关程序运行时的目录


注意,在OnInitDialog()中设置路径一段程序的作用(行号是为了说明加上的)。

//E.设置路径
1 GetCurrentDirectory(256,appdir); //取当前目录
2 dir=appdir;
3 if(dir.Right(8)=="运行程序")
4 dir="图片/"; //图片路径
5 else dir="../运行程序/图片/"; //图片路径



第 1 行,是一个Windows 的API 函数调用,是将当前程序运行的目录取到字符数组
变量appdir 中。
第 2 行,将当前目录赋于字符串变量dir。因为字符串变量支持我们下面要用到的
一些灵活的操作。
第 3 行,如果dir 后边8 个字符(4 个汉字)是“运行程序”,
则第4 行dir="图片/";
否则第5 行dir="../运行程序/图片/";
第3~5 行算法的意义是:我们整个教学范例的运行程序,都集中放在与各个范例
源程序同等的目录“ 运行程序”下的,而所有动画图片都在目录“运行程序”的下级
目录“图片”下;即在与源程序同级目录“运行程序/图片/”下的。(见图3-3)

游戏开发技术总结(经典之作)第三集 让图片动起来----快速切换图形实现动画_第2张图片
                                                       图3-3
       当程序直接在VC 中编译运行时,当前目录为范例源程序目录( 如本例是“ 02.让我
动吧”这个目录)。按第3 行判断不是“运行程序”目录,所以取图片的目录就应该是
dir="../运行程序/图片/",即上一级目录下的“运行程序/图片/”。
如果在“ 运行程序”下执行程序“02.让我动吧.exe”;当前目录就是"运行程序",所
以取图片的目录就应该是dir="图片/"。即本级目录下的"图片/"。
这样做的作用是,无论是在VC 中直接编译运行程序,还是在目录“运行程序”下
运行程序,都可以准确地取图片所在的目录。
好了,现在可以编译运行这个程序了。


3-7 流程图


       程序流程主要分三块:程序开始执行时的初始化, 按键后设置参数和启动时钟函
数,由时钟函数和调图形函数构成的程序主循环。注意,我们下面对游戏的进一步编
程主要是在这个主循环中进行的。

游戏开发技术总结(经典之作)第三集 让图片动起来----快速切换图形实现动画_第3张图片
                                                    图3-4 主流程图
具体的程序请看本章实例程序:“让我动吧”。


3-8 小结


在这一章里我们学了以下知识和方法。
1.介绍计算机动画的基本知识和实现方法
2.在VC++中建立时钟消息,使用时钟消息产生动画。
3.介绍格式化函数sprintf()的用法。由此动态地获取图形文件名。
4.介绍在VC++中窗口、控件大小控制和定位的方法。
5.程序运行时的目录定位方法。

你可能感兴趣的:(C++,bitmap,游戏开发,游戏引擎,Visual,游戏编程)