博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MFC学习笔记之三(粒子系统+怪物简单AI+碰撞检测)
阅读量:7031 次
发布时间:2019-06-28

本文共 7717 字,大约阅读时间需要 25 分钟。

到上海找到住的地方之后,干的第一件事,就是抓紧时间学习,为了找到工作努力ing。。。

备注:以下请参考http://blog.csdn.net/hust_xy/article/details/9374935来看,本文是对其详细说明、解释和加深。

为了防止看不懂,补充一个到目前为止(下面的九截止,即本篇结束)的Hero类声明和类方法定义

class Hero{	int x;	//人物的x坐标(指左上部分)	int y;//人物的y坐标(指左上部分)	int frame;		//人物的帧数	int direct;		//人物的方向public:	Hero(int X = 0, int Y = 0, int Frame = 0, int Direct = 0) :x(X), y(Y), frame(Frame), direct(Direct) {}	//默认构造函数	CImage m_hero;	//人物图	CRect xy;		//这里的xy是绝对坐标	int& GetX() { return x; }	//获得X坐标,可以通过这个函数修改x坐标(直接赋值的形式)	int& GetY() { return y; }	void SetXY(double X = 1.11, double Y = 1.11, int R = 0, int B = 0);	int GetFrame() { return frame; }	//获得帧数,用于绘图时决定是哪一帧	int& GetDirect() { return direct; }	//获得方向,用于绘图时决定是哪一个方向的图,鼠标直接指向时,需要通过其修改方向	int GetHeroX(CRect &m_client);	//根据背景图和实际坐标,获得在DC上的坐标	int GetHeroY(CRect &m_client);	int GetEnemyX(CRect &m_client);	int GetEnemyY(CRect &m_client);	void addX(int X);	//增加参数位移(包含正负)	void addY(int Y);	void addFrame();	//帧数变换	void SetClient(CRect &m_client);	//设置背景图在绘制时的坐标,人物移动拖动地图时使用	void move(Hero&he);	//当前对象检测另一个对象,然后决定是否移动,这里应该移动的是绝对坐标(相对大地图而非屏幕)	int Together(Hero&he);		//碰撞返回1,有部分重合返回2,无碰撞返回0};void Hero::addX(int X){	x += X;    //向右移动10个像素的单位  	if (X > 0)		direct = 2;	//修改方向	else		direct = 1;	SetXY();}void Hero::addY(int Y){	y += Y;    //向右移动10个像素的单位  	if (Y > 0)		direct = 0;	//修改方向	else		direct = 3;	SetXY();}void Hero::addFrame(){	frame++;	if (frame > 3)frame = 0;}void Hero::SetClient(CRect &m_client)	//注意,这里第二个参数是背景图的宽度{	if (x < 0)x = 0;	if (y < 0)y = 0;	if (x > m_client.Width() - 80)x = m_client.Width() - 80;	if (y > m_client.Height() - 80)y = m_client.Height() - 80;	int wi = m_client.Width();	int he = m_client.Height();	//往右	if (x + m_client.left > 600 && m_client.left + m_client.Width() > 800)		//如果人物的坐标在屏幕上位于靠右的200宽度了		m_client.left = 600 - x;	//往左	if (x + m_client.left < 200 && m_client.left < 0)		//如果人物的坐标在屏幕上位于靠右的200宽度了		m_client.left = 200 - x;	m_client.right = m_client.left + wi;	//根据屏幕的最左边的坐标,调整最右边的	//往下	if (y + m_client.top > 400 && m_client.top + m_client.Height() > 600)		m_client.top = 400 - y;	//往上	if (y + m_client.top < 200 && m_client.top < 0)		m_client.top = 200 - y;	m_client.bottom = m_client.top + he;	//根据屏幕最上面的坐标,调整最下面的,这里不能用Width()和Height()来调整(因为宽度和高度在减少),正常应该是固定尺寸的}//根据人物的实际坐标、地图在DC上画画时的坐标,以及地图宽度,返回人物在屏幕上显示的坐标int Hero::GetHeroX(CRect &m_client)	//参数1是人物的实际x坐标(即相对于大地图左上部分的坐标),参数2是地图的x坐标,参数3是大地图的宽度,返回值是人物在屏幕上的坐标{	if (x < 200)return x;	//当小于200时,显示部分位于地图最左边并且不动	else if (x>m_client.Width() - 200)return x - (m_client.Width() - 800);	//当大于地图宽度-200时,显示部分位于地图最右边并且不动,mapwidth-800是减去地图左边不显示的部分	else return x + m_client.left;	//如果在中间,则坐标是人物的x坐标-地图的x左边(相对于地图开始显示部分的x坐标 的坐标)	}int Hero::GetHeroY(CRect &m_client)	//返回人物在屏幕上的y坐标{	if (y < 200)return y;	//当小于200时,显示部分位于地图最左边并且不动	else if (y>m_client.Height() - 200)return y - (m_client.Height() - 600);	//当大于地图宽度-200时,显示部分位于地图最右边并且不动,mapwidth-800是减去地图左边不显示的部分	else return y + m_client.top;	//如果在中间,则坐标是人物的x坐标-地图的x左边(相对于地图开始显示部分的x坐标 的坐标)	}int Hero::GetEnemyX(CRect &m_client){	return x + m_client.left;}int Hero::GetEnemyY(CRect &m_client){	return y + m_client.top;}void Hero::move(Hero&he)	//当前对象相对于参数是否移动{	he.SetXY();		//先设置目前的各个参数(防止目标的绝对坐标和其x、y值不同步)——虽然我很怀疑有没有必要还设置x和y值,但暂且这样用吧	//前两个条件约束的是某个方向300范围内,第3,4个条件约束的是另外两个方向。最终形成的是在被检测对象左右上下300范围内,会进行追击,离开这个范围就停止	if (xy.right < he.xy.left&&xy.right>he.xy.left - 300 && xy.top < he.xy.bottom + 300 && xy.bottom>he.xy.top - 300)x++;		//在目标的左边300范围内(下同)	if (xy.left > he.xy.right&&xy.left 
he.xy.top - 300)x--; //在右边 if (xy.top > he.xy.bottom&&xy.top < he.xy.bottom + 300 && xy.right>he.xy.left - 300 && xy.left
he.xy.top - 300 && xy.right>he.xy.left - 300 && xy.left
LEFT&&Y > TOP&&Y < BOTTOM) //全小于说明重合 return 2; else if (X <= RIGHT&&X >= LEFT&&Y >= TOP&&Y <= BOTTOM) //否则是碰撞(刚好挨着) return 1; else return 0; //最后就是没有碰撞}

(七)粒子系统

粒子实质上就是一堆图片的集合。

在实际运用中,给每一个图片一个坐标,然后移动其,就会产生粒子效果。例如雪花。

如类:

class Snow

{

public:

int x;

int y;

int num; //用哪一个雪花

Snow();

void addXY();//坐标移动

};

 

类方法定义:

Snow::Snow()//默认构造函数,设置初始的坐标和选择是哪一个

{

x = rand() % 800; //雪花大小是32x32,所以不能让他出屏幕

y = rand() % 600;

num = rand() % 7; //0~6

}

void Snow::addXY()

{

x -= rand() % 4 - 1; //-1~2的变化值

y += 3;

if (x > 800)x = 1;//超右界

else if (x < 0)x = 768;//超左界

if (y > 600)y = 0;//超下面

}

 

加载:

#define SNOW_NUMBER 100

Snow snow[SNOW_NUMBER];

第一行用别名替代数量(这样方便修改)

第二行创建多少个粒子(这里是雪花)

 

char txtName[30];

for (int i = 0;i < 7;i++)//加载雪花图

{

sprintf_s(txtName, 30,"..\\练习\\res\\0%d.png", i);

snows_pi[i].Load(txtName);

TransparentPNG(&snows_pi[i]);

}

这里给7个雪花加载对应的图像。这个在PreCreateWindow()函数中

 

for (int i = 0;i < SNOW_NUMBER;i++)

{

snows_pi[snow[i].num].Draw(m_cacheDC, snow[i].x, snow[i].y, 16, 16);//画雪花图,原理是画一个雪花(哪个雪花看当前类对象的num),坐标是当前类对象的x和y值

snow[i].addXY(); //画完后,更新这个类对象的x和y值

}

这里是画雪花,每个雪花都预设了坐标和编号,具体画哪个雪花根据编号来(编号是雪花CImage对象的下标),然后有其坐标。也就是说,画100个,画的时候调用对应的雪花图案,于是每个雪花图案被重复画了很多次

第二行是修改雪花坐标,修改后就会产生雪花位移的效果。这个在OnPaint()函数中

 

 

(八)怪物简单AI

这里的简单AI,指怪物会自动追踪玩家。在某个范围内会自动追击,脱离某个范围则停止追击。(原文是仅仅判断相对大地图绝对坐标而移动)

综合来看,就是以怪物为中心,进入上下左右若干范围内,怪物会不断靠近玩家。

以下代码是矩形范围。

但也可以通过修改代码,通过计算其xy坐标差(例如计算两个矩形中心的xy坐标之差),求得其直线距离(三角形求斜边),在直线距离满足一定条件会进行追击。

 

void Hero::SetXY(double Xdouble Yint Rint B)//参数分别为左,上,宽和高

{

if (X != 1.11)

{

x = int(X);

y = (int)Y;

}

if (R == 0)

{

R = xy.Width();

B = xy.Height();

}

xy.left = x;

xy.top = y;

xy.right = x + R;

xy.bottom = y + B;

}

void Hero::move(Hero&he)//当前对象相对于参数是否移动

{

he.SetXY();//先设置目前的各个参数(防止目标的绝对坐标和其x、y值不同步)——虽然我很怀疑有没有必要还设置x和y值,但暂且这样用吧

//前两个条件约束的是某个方向300范围内,第3,4个条件约束的是另外两个方向。最终形成的是在被检测对象左右上下300范围内,会进行追击,离开这个范围就停止

if (xy.right < he.xy.left&&xy.right>he.xy.left - 300 && xy.top < he.xy.bottom + 300 && xy.bottom>he.xy.top - 300)x++;//在目标的左边300范围内(下同)

if (xy.left > he.xy.right&&xy.left <he.xy.right + 300 && xy.top < he.xy.bottom + 300 && xy.bottom>he.xy.top - 300)x--;//在右边

if (xy.top > he.xy.bottom&&xy.top < he.xy.bottom + 300 && xy.right>he.xy.left - 300 && xy.left <he.xy.right + 300)y--;//在下边

if (xy.bottom < he.xy.top&&xy.bottom>he.xy.top - 300 && xy.right>he.xy.left - 300 && xy.left <he.xy.right + 300)y++;//在上面

SetXY(); //每次移动,更改其xy坐标的绝对数值

}

 

这个代码中,xy.left、xy.top、xy.bottom、xy.right指的是该图形的上下左右四个坐标(是CRect类对象用于计算坐标)。

xy指的是图形左上角顶点相对于背景地图(左上角)的绝对坐标。

 

 

(九)检测碰撞

检测碰撞,实际上就是两个图片有重合之处。被检测对象不动(例如玩家),检测对象贴着被检测对象最外围,绕行一圈。会发现,检测对象的矩形中心的运动轨迹,是一个围绕被检测对象的一个矩形。(反着理解也可以,即被检测对象为中心,检测对象绕着转)

 

上图是我自己制作的图,注意,绿色的表达式表示是heright和left。

由图可以看出,假如被检测对象(假设形参名为he),其矩形中心(坐标为(left+right)/2, (top+bottom)/2)同时满足4个条件时,则表示其在绿色方框范围内:

①条件一:横坐标大于等于left-(he.right-he.left)/2; //左边

②条件二:横坐标小于等于right+((he.right-he.left)/2; //右边

③条件三:纵坐标大于等于top-(he.bottom-he.top)/2; //上边

④条件四:纵坐标小于等于bottom+(he.bottom-he.top)/2; //下边

如果假如是两个圆形的图(虽然绘图是时候用的是方形),则计算两点的距离,然后看是否等于或者小于两个圆半径之和,如果是,则说明碰撞或者有部分重叠了。

 

代码:

int Hero::Together(Hero&he)//碰撞返回1,有部分重合返回2,无碰撞返回0

{

SetXY();

he.SetXY();//设置两点重合

double X = (he.xy.left + he.xy.right) / 2;//被检测对象中心的横坐标

double Y = (he.xy.top + he.xy.bottom) / 2;//被检测对象中心的纵坐标

double LEFT = xy.left - (he.xy.right - he.xy.left) / 2;//碰撞范围的左边

double RIGHT = xy.right + (he.xy.right - he.xy.left) / 2;//碰撞范围的右边

double TOP = xy.top - (he.xy.bottom - he.xy.top) / 2;//碰撞范围的上边

double BOTTOM = xy.bottom + (he.xy.bottom - he.xy.top) / 2;//碰撞范围的下边

if (X < RIGHT&&X > LEFT&&Y > TOP&&Y < BOTTOM)//全小于说明重合

return 2;

else if (X <= RIGHT&&X >= LEFT&&Y >= TOP&&Y <= BOTTOM)//否则是碰撞(刚好挨着)

return 1;

else return 0;//最后就是没有碰撞

}

 

然后根据不同反馈,输出不同的文字:

//设置文字背景透明  

m_cacheDC.SetBkMode(TRANSPARENT);

//设置文字为红色  

m_cacheDC.SetTextColor(RGB(255, 0, 0));

if (enemy.Together(myHero) == 2)m_cacheDC.TextOut(0, 0, "发生重合");

else if (enemy.Together(myHero) == 1)m_cacheDC.TextOut(10, 10, "刚好碰撞");

else m_cacheDC.TextOutA(20, 0, "尚有一段距离");

 

TextOut()的前两个参数,表示输出文字左上角部分(包含文字的矩形)的坐标

 SetBkMode()函数,SetTextColor()函数,以及TextOut()函数,其具体用法和说明,后面更新在函数说明中

http://blog.csdn.net/qq20004604/article/details/50740574
你可能感兴趣的文章
使用cached的wrapper类读取请求响应内容
查看>>
[python][os]分离文件目录,文件名以及文件后缀
查看>>
解决Android Studio SDK无法下载问题
查看>>
雷军定AI+IoT为小米核心战略,牵手宜家推进生态布局
查看>>
书评:《All About Java 8 Lambdas》
查看>>
搜狗信息流推荐算法实践
查看>>
Visual Studio 2017 15.6发布
查看>>
2019年Java和JVM生态系统预测:OpenJDK将成为Java运行时市场领导者
查看>>
拥抱PostgreSQL,红帽再表态:SSPL的MongoDB坚决不用
查看>>
架构设计复杂度的6个来源
查看>>
360首席安全官谭晓生宣布离职
查看>>
在敏捷中应用测试驱动开发
查看>>
到底谁应该对软件开发的质量负责?
查看>>
微软Windows Core OS被曝应用了开源组件
查看>>
用Elm语言降低失败的风险
查看>>
资深专家都知道的Docker常用命令
查看>>
谈谈UCloud的秒级在线快照服务
查看>>
Spring Web Services 3.0.4.RELEASE和2.4.3.RELEASE发布
查看>>
EGO走进美团——追寻千亿市场背后的技术力量
查看>>
腾讯正式宣布成立技术委员会,要对组织架构下狠手
查看>>