到上海找到住的地方之后,干的第一件事,就是抓紧时间学习,为了找到工作努力ing。。。
备注:以下请参考http://blog.csdn.net/hust_xy/article/details/9374935来看,本文是对其详细说明、解释和加深。
为了防止看不懂,补充一个到目前为止(下面的九截止,即本篇结束)的Hero类声明和类方法定义
<span style="font-size:14px;">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.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坐标的绝对数值 } void Hero::SetXY(double X, double Y, int R, int 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; } 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; //最后就是没有碰撞 }</span>
(七)粒子系统
粒子实质上就是一堆图片的集合。
在实际运用中,给每一个图片一个坐标,然后移动其,就会产生粒子效果。例如雪花。
如类:
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,指怪物会自动追踪玩家。在某个范围内会自动追击,脱离某个范围则停止追击。(原文是仅仅判断相对大地图绝对坐标而移动)
综合来看,就是以怪物为中心,进入上下左右若干范围内,怪物会不断靠近玩家。
以下代码是矩形范围。
但也可以通过修改代码,通过计算其x、y坐标差(例如计算两个矩形中心的x、y坐标之差),求得其直线距离(三角形求斜边),在直线距离满足一定条件会进行追击。
void Hero::SetXY(double X, double Y, int R, int 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类对象用于计算坐标)。
x、y指的是图形左上角顶点相对于背景地图(左上角)的绝对坐标。
(九)检测碰撞
检测碰撞,实际上就是两个图片有重合之处。被检测对象不动(例如玩家),检测对象贴着被检测对象最外围,绕行一圈。会发现,检测对象的矩形中心的运动轨迹,是一个围绕被检测对象的一个矩形。(反着理解也可以,即被检测对象为中心,检测对象绕着转)
上图是我自己制作的图,注意,绿色的表达式表示是he的right和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