OpenCV做飞机射击类游戏(二)

1.什么是封装:


这种事情需要

举例说明:

比如,我现在有一堆材料,我要用这些材料建一座房子,我现在有:泥瓦匠、会计、工程设计师、装修工、。

那么

泥瓦匠负责盖房子;

会计负责财务管理;

工程设计师负责总设计;

装修工负责对建好的房子进行装修。


这样就达到了分工明确的目的。能很高的提高效率。如果每干一件事的时候都要让所有的人一起,那么做事就会很乱。(如果在代码中把所有的代码实现堆在一起是很恐怖 的事情,因为如果代码过多,你写的代码 估计都不知道写的什么了。)会计提供预算,工程师根据预算来设计图纸,泥瓦匠根据图纸来建造房子,装修工根据建好的房子来装修。(后一个人根据前人完成的事情来干自己的事情,这就类似于代码中的接口)那么每个人只需要在已经完成的基础上进行工作。很是方便()。


映射到我们的代码上:

每一块代码负责一部分功能,然后将代码组合起来,代码与代码之间不需要知道是怎么实现的,只要一个接口,一般小程序的接口都是指的函数参数之间的传递。这样代码看起来很是整洁,封装好的代码还可以用来重用。这里就不提了。有点超纲。


代码:


封装了三个东西:


1.背景图片的显示

(因为我们在写这个游戏的时候,所有的外在体现都是图片的移动、显示等等,所以把图片的相关功能封装出去会是对代码的一种优化)


2.我方飞机

(游戏中用户的主要操作对象)


3.游戏的管理

(用来管理整个游戏的运行,比如:按下 了某个键会发生什么事情就是通过管理类来实现的)。


2.关于.h 文件和。cpp文件:

在创建一个类的时候都会产生一个.h 文件和.cpp文件.那么这俩个文件是干什么用的:

.h 文件用来声明,相当于一个说明书。

.cpp文件用来编写在.h文件中声明的函数,相当于将 函数 的实现单独了出来。

举个例子:

大家都知道stdio.h头文件。我们经常用scanf 和printf函数来输入输出数据。那么scanf 和printf 函数就是在stdio.h文件声明的。那么具体的功能的实现就是在stdio.cpp中完成的。我们做的工作也是这样的。我们通过要实现什么东西来编写我们需要的头文件和cpp文件。


3.代码讲解:


1.CGameUI类:


.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;
		}
	}
}



CMyPlane类:

(我方飞机)


.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;
 }


这段代码涉及到了按键按下的处理机制,GetAsyncKeyState函数功能:读取的是物理键状态,也是就是不管你怎么鼠标键盘映射,它只读取实际的按键状态,即:某个键是否按下。那么为什么GetAsyncKeyState要 ‘与’上 0x8000这个常数呢?
答案是:获取按键状态,屏蔽掉其他的可能状态。

(有兴趣的同学可以 试一下自己写判断按下的代码,一般都是不可以检测多个键同时按下 的,而这段代码就是为了能够同时检测多个按键并做出处理,比如:你可以同时按下W和A,那么飞机就朝向 左上角移动)。

如果想要深究按键处理的同学可以看下这篇博客:

http://blog.sina.com.cn/s/blog_4e7834f50100qejf.html

我觉得还是不深究的好,因为知道了也没什么卵用。只会徒增烦恼。


2.飞机图片上多余背景的剔除:

原理:

飞机这张图是由一个个像素点构成的,那么我们可以将那些多余的位置的像素点大概估计出来一个范围,那么在显示在背景图上时,我们判断每一个位置上的像素数值,如果数值在此范围内,我们就不显示出来:

OpenCV做飞机射击类游戏(二)_第1张图片

CGameManage类:


,h文件:

#include "GameUI.h"
#include "MyPlane.h"

class CGameManage  
{
public:
	void Run();       //程序的运行相关处理
	CGameManage();         //构造函数
	virtual ~CGameManage(); //析构函数
private:
	CGameUI gameui;       //  背景UI类的对象
	CMyPlane myplane;       //飞机对象
	void MyPlaneUpdate();       //用来处理飞机图片的移动和移动后的重新显示更新
};



.cpp文件:
#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;
}

代码中的错误类型总结:

1.变量名:

比如:

mpos打成 mops 等等,这些不小心的操作。

2.用到变量没有定义:

比如:

用到了bg 成员变量,但是你在类中并没有声明此成员变量。

3.程序崩溃:

一般来说,程序崩溃时由指针找到不到地址产生的,如果程序崩溃了:

检查图片的路径

检查用到指针传递时是否传错了

4.没有加头文件:

比如:在CGameManage类中声明了一个飞机对象,但是并没有添加飞机类的头文件。

5.字母拼写:

比如:IplImage 的字母l打成数字1,大小写问题 ,名字没有拼全。

6.函数调用时没有加后面的括号。

其他的错误很多,我就不列出来了。自己能解决的自己解决哈,不能解决的找大神。


要想搞懂代码:

在写代码的时候,我们将代码功能划分,在看代码的时候就要将代码的功能在脑子里组合起来,找到接口,即:什么时候传递了什么参数,传递给谁了。搞懂了传递就基本可以了。

没懂很正常,因为刚接触的人基本都处于懵懂,而自己要做的就是去搞懂,而不是放弃,这就是月入1W以上跟以下的主要拉距点。

可以自己 根据我上面的讲解代码实现一遍。





你可能感兴趣的:(游戏,C++,opencv)