这种事情需要
比如,我现在有一堆材料,我要用这些材料建一座房子,我现在有:泥瓦匠、会计、工程设计师、装修工、。
那么
泥瓦匠负责盖房子;
会计负责财务管理;
工程设计师负责总设计;
装修工负责对建好的房子进行装修。
这样就达到了分工明确的目的。能很高的提高效率。如果每干一件事的时候都要让所有的人一起,那么做事就会很乱。(如果在代码中把所有的代码实现堆在一起是很恐怖 的事情,因为如果代码过多,你写的代码 估计都不知道写的什么了。)会计提供预算,工程师根据预算来设计图纸,泥瓦匠根据图纸来建造房子,装修工根据建好的房子来装修。(后一个人根据前人完成的事情来干自己的事情,这就类似于代码中的接口)那么每个人只需要在已经完成的基础上进行工作。很是方便()。
每一块代码负责一部分功能,然后将代码组合起来,代码与代码之间不需要知道是怎么实现的,只要一个接口,一般小程序的接口都是指的函数参数之间的传递。这样代码看起来很是整洁,封装好的代码还可以用来重用。这里就不提了。有点超纲。
(因为我们在写这个游戏的时候,所有的外在体现都是图片的移动、显示等等,所以把图片的相关功能封装出去会是对代码的一种优化)
(游戏中用户的主要操作对象)
(用来管理整个游戏的运行,比如:按下 了某个键会发生什么事情就是通过管理类来实现的)。
在创建一个类的时候都会产生一个.h 文件和.cpp文件.那么这俩个文件是干什么用的:
.h 文件用来声明,相当于一个说明书。
.cpp文件用来编写在.h文件中声明的函数,相当于将 函数 的实现单独了出来。
举个例子:
大家都知道stdio.h头文件。我们经常用scanf 和printf函数来输入输出数据。那么scanf 和printf 函数就是在stdio.h文件声明的。那么具体的功能的实现就是在stdio.cpp中完成的。我们做的工作也是这样的。我们通过要实现什么东西来编写我们需要的头文件和cpp文件。
.h 文件:
#include <cv.h> #include <highgui.h> class CGameUI { public: void Build(); //用来完成在bg图的滚动 IplImage* GetNowImage(); //得到当前图片 void ShowImage(); //在某个窗口中显示背景图图片 void SetBgImage(IplImage* img); //在实现背景图的滚动时,我们需要得到背景图,需要克隆背景图 CGameUI(); //构造函数,用来对在此类中声明的变量初始化 virtual ~CGameUI(); //析构函数 private: IplImage *bg; //用于显示的背景图片 IplImage *clonebg; //拷贝的备份图片 int speed,mpos; //图片滚动的速度与坐标 };
.cpp实现:
#include "stdafx.h" #include "GameUI.h" CGameUI::CGameUI() { speed = 1; mpos = 0; } CGameUI::~CGameUI() { } void CGameUI::SetBgImage(IplImage *img) //根据传过来的参数完成bg 和clonebg的赋值 { bg = img; clonebg = cvCloneImage(bg); } void CGameUI::ShowImage() //显示背景图片 { cvShowImage("plane",bg); } IplImage* CGameUI::GetNowImage() //得到当前背景图片的指针 { return bg; } void CGameUI::Build() //用来实现背景图的滚动。上篇博文已经讲过了,不懂的拐回去看看哈。 { mpos -= speed; if( mpos < 0) mpos += clonebg->height; for(int i = 0 ; i < clonebg->height ; i++) { for(int j = 0 ; j < clonebg->width ; j ++) { int b = CV_IMAGE_ELEM(clonebg,uchar,(mpos+i)%clonebg->height,j*3); int g = CV_IMAGE_ELEM(clonebg,uchar,(mpos+i)%clonebg->height,j*3+1); int r = CV_IMAGE_ELEM(clonebg,uchar,(mpos+i)%clonebg->height,j*3+2); CV_IMAGE_ELEM(bg,uchar,i,j*3+0) = b; CV_IMAGE_ELEM(bg,uchar,i,j*3+1) = g; CV_IMAGE_ELEM(bg,uchar,i,j*3+2) = r; } } }
(我方飞机)
.h文件:
#include <cv.h> #include <highgui.h> class CMyPlane { public: void Draw( IplImage* bg ); //飞机这张图片在背景图上的显示 void Move( int width, int height ); //飞机的移动 CMyPlane(); //构造函数,用来给声明的变量初始化 virtual ~CMyPlane(); //析构函数 private: int hp; //血量 int atk; //攻击 int atk_cd; //攻击间隔 int posx,posy; //位置 IplImage* img; //图片 };
.cpp实现:
#include "stdafx.h" #include "MyPlane.h" CMyPlane::CMyPlane() { img = cvLoadImage(".\\resource\\airport_1.png"); hp = 100; //血量 atk = 50; //攻击 atk_cd = 250; //攻击间隔 posx = 300; //x轴位置 posy = 300; //y轴位置(这样就让飞机在程序刚开始运行时的位置为背景图的像素点<300,300>位置上) } CMyPlane::~CMyPlane() { } void CMyPlane::Move(int width, int height) { // 异步 //GetAsyncState() //short 1 int speed = 5; if(GetAsyncKeyState('W') & 0x8000) //如果按下W键,则向上移动,飞机在背景图上的位置将向上移动 { posy -= speed; } if(GetAsyncKeyState('S') & 0x8000) //如果按下S键,则向下移动,飞机在背景图上的位置将向下移动 { posy += speed; } if(GetAsyncKeyState('A') & 0x8000) //如果按下A键,则向左移动,飞机在背景图上的位置将向左移动 { posx -= speed; } if(GetAsyncKeyState('D') & 0x8000) //如果按下D键,则向右移动,飞机在背景图上的位置将向右移动 { posx += speed; } if(posx < 0) posx = 0; if(posx > width - 1 - img->width) posx = width - 1 - img->width; if(posy < 0) posy = 0; if(posy > height - 1 - img->height) posy = height - 1 - img->height; //如果达到背景图的边界,我们就强行让飞机的位置为当前所能允许的最大范围的位置上,防止越界 } void CMyPlane::Draw(IplImage *bg) //将飞机图显示在背景图上面。 { for(int i = 0 ; i < img->height ; i++) { for(int j = 0 ; j < img->width ; j ++) { int b = CV_IMAGE_ELEM(img,uchar,i,j*3); int g = CV_IMAGE_ELEM(img,uchar,i,j*3+1); int r = CV_IMAGE_ELEM(img,uchar,i,j*3+2); if( b == 91 && g == 132 && r == 178) continue; CV_IMAGE_ELEM(bg,uchar,i + posy,(j + posx)*3+0) = b; CV_IMAGE_ELEM(bg,uchar,i + posy,(j + posx)*3+1) = g; CV_IMAGE_ELEM(bg,uchar,i + posy,(j + posx)*3+2) = r; } } }
</pre><pre>1.说一下这个代码:
if(GetAsyncKeyState('W') & 0x8000) //如果按下W键,则向上移动,飞机在背景图上的位置将向上移动 { posy -= speed; }
(有兴趣的同学可以 试一下自己写判断按下的代码,一般都是不可以检测多个键同时按下 的,而这段代码就是为了能够同时检测多个按键并做出处理,比如:你可以同时按下W和A,那么飞机就朝向 左上角移动)。
如果想要深究按键处理的同学可以看下这篇博客:
http://blog.sina.com.cn/s/blog_4e7834f50100qejf.html
我觉得还是不深究的好,因为知道了也没什么卵用。只会徒增烦恼。
2.飞机图片上多余背景的剔除:
原理:
飞机这张图是由一个个像素点构成的,那么我们可以将那些多余的位置的像素点大概估计出来一个范围,那么在显示在背景图上时,我们判断每一个位置上的像素数值,如果数值在此范围内,我们就不显示出来:
,h文件:
#include "GameUI.h" #include "MyPlane.h" class CGameManage { public: void Run(); //程序的运行相关处理 CGameManage(); //构造函数 virtual ~CGameManage(); //析构函数 private: CGameUI gameui; // 背景UI类的对象 CMyPlane myplane; //飞机对象 void MyPlaneUpdate(); //用来处理飞机图片的移动和移动后的重新显示更新 };
#include "stdafx.h" #include "GameManage.h" CGameManage::CGameManage() //用来初始化在类中声明的成员变量 { cvNamedWindow("plane"); //建立窗口 IplImage *img = cvLoadImage(".\\resource\\bg.jpg"); //得到背景图 gameui.SetBgImage(img); //那么将背景图传到 游戏背景对象中 } CGameManage::~CGameManage() { } void CGameManage::Run() { while(1)//桢 //循环 { gameui.Build(); //实现背景图的滚动 MyPlaneUpdate(); //飞机的移动和移动后的重新绘制显示 gameui.ShowImage(); //显示背景图 cvWaitKey(20); //等待信息输入 } } void CGameManage::MyPlaneUpdate() //飞机移动和移动后在背景图的重新显示(因为位置变了,所以要重新在背景图上显示) { myplane.Move(gameui.GetNowImage()->width,gameui.GetNowImage()->height); myplane.Draw(gameui.GetNowImage()); }
主程序:
#include "stdafx.h" #include <stdio.h> #include <cv.h> #include <highgui.h> #include "GameManage.h" int main(int argc, char* argv[]) { CGameManage manage; //创建管理类对象 manage.Run(); //运行管理 return 0; }
比如:
mpos打成 mops 等等,这些不小心的操作。
比如:
用到了bg 成员变量,但是你在类中并没有声明此成员变量。
一般来说,程序崩溃时由指针找到不到地址产生的,如果程序崩溃了:
检查图片的路径
检查用到指针传递时是否传错了
比如:在CGameManage类中声明了一个飞机对象,但是并没有添加飞机类的头文件。
5.字母拼写:
比如:IplImage 的字母l打成数字1,大小写问题 ,名字没有拼全。
其他的错误很多,我就不列出来了。自己能解决的自己解决哈,不能解决的找大神。
在写代码的时候,我们将代码功能划分,在看代码的时候就要将代码的功能在脑子里组合起来,找到接口,即:什么时候传递了什么参数,传递给谁了。搞懂了传递就基本可以了。
没懂很正常,因为刚接触的人基本都处于懵懂,而自己要做的就是去搞懂,而不是放弃,这就是月入1W以上跟以下的主要拉距点。
可以自己 根据我上面的讲解代码实现一遍。