先明确几个概念。首先,在Win API编程当中,屏幕的坐标空间和平时物理上用的坐标空间是不一样的
由于在电脑屏幕上y轴的正方向与一般的物理坐标系y轴方向是相反的,因此在一些涉及到方向的计算当中要额外注意一下
其次在本次表达速度的时候,都是通过x轴方向速度和y轴方向速度结合的矢量来表达的
下面进入正题
碰撞的触发条件是通过不断获取指针坐标,当指针与圆的距离小于圆的半径时,即满足触发条件
<span style="white-space:pre"> </span>double Distance = sqrt(double(pow((double)(nowCursor.x - currpt.x), 2) + pow((double)(nowCursor.y - currpt.y), 2)));
if(Distance <= DIAMETER)
{
if(InCircleFlag == FALSE)
{
Speed_Pollo = CalVectorSpeed();
InCircleFlag = TRUE;
}
}
那么在触发了碰撞的那一刻,指针相对于圆心的坐标就成为了碰撞的方向,由于指针是不会被弹走的,所以在这里是完全弹性碰撞,那么当指针不动时,给圆的力如下图所示
假设是理想情况,碰撞的面永远只有这一个点,那么给予圆的力的方向永远是指向圆心的方向,那么多余的没有指向圆心方向的力就会提供给旋转,旋转在本篇先不(mei)讨(nong)论(hao)了。
由于刷新率的问题,碰撞肯定不是刚好在边上的,但是在这里这个碰撞点提供的只是一个方向,因此跟是不是在边上没关系,只要指向圆心就行了。
那么这个弹力的大小要怎么算呢,由于是完全弹性碰撞,因此圆本身的速度在这个弹力方向上的分量不仅被完全抵消,而且还被完全弹了回来。
最后得出的2a,就是当指针不动时,圆撞到指针上获得的速度大小和方向。但是由于圆本身就有一个速度,因此撞完之后肯定不是完全按照速度a的方向来走,那么这里就用到了矢量相减
以上所有图的速度都是用矢量表示,因此长度就代表大小,箭头所指方向就是速度方向
现在概念大概理解了,就要开始算啦
计算所有向量角度的函数
double Action::CalVectorAngle(SPEED Vector)
{
double Angle = atan((double)(Vector.ySpeed/(Vector.xSpeed + 0.0001))); //为了防止除以0
//arctan值域为-2/PI到2/PI,无法表达所有角度,因此要根据相对位置来加减角度得到真实角度
if (Vector.xSpeed < 0 && Vector.ySpeed >= 0)
{
Angle = Angle + PI;
}
else if (Vector.xSpeed < 0 && Vector.ySpeed < 0)
{
Angle = Angle - PI;
}
return Angle;
}
在这里说明一下角度与实际的区别
接下来就可以开始着手计算2a所表示的速度矢量了,算出来2a之后b的速度矢量也直接就出来了。
等了个等,为什么要说计算2a的就能直接得出b的速度。因为他们的所指向的点是同一个点呀,所以算出2a就等于算出b,但是算出b其实并不是最后实际的速度,因为在他的起点不是从圆心开始。因此在算出b之后,还应当在v'的方向上进行一个平移,把起点移动到圆心,才是我们最终的速度向量b'
b'就是我们最后要的速度矢量了
下面贴代码
SPEED Action::CalVectorSpeed()
{
//总是叫圆不好听,叫个Pollo好了
SPEED CursorImpect; //指针碰撞时的矢量
CursorImpect.xSpeed = nowCursor.x - currpt.x;
CursorImpect.ySpeed = nowCursor.y - currpt.y;
double CursorAngle = CalVectorAngle(CursorImpect); //指针与pollo碰撞时的角度
double RealCursorAngle = CursorAngle - PI; //得到指针给予速度的角度(上图中的2a)
double CircleAngle = CalVectorAngle(Speed_Pollo); //pollo本身的速度角度(上图中的v)
double CursorMoveAngle = CalVectorAngle(Speed_Cursor); <span style="white-space:pre"> </span>//指针移动时的角度
double CursorSpeedAngle = fabs(CursorMoveAngle - RealCursorAngle); //指针的速度通过该角度传入有效速度
double ResultAngle;
SPEED ResultSpeed; //碰撞结果的速度矢量(既用作上图中的b也用作b')
ResultSpeed.xSpeed = Speed_Pollo.xSpeed;
ResultSpeed.ySpeed = Speed_Pollo.ySpeed;
double MPolloSpeed; //pollo Speed的模
double MCursorSpeed;//指针速度的模
ResultAngle = abs(CircleAngle - CursorAngle);
//只有当pollo速度角度和指针碰撞角度的夹角小于90度时,结果向量才为矢量相减计算
if(ResultAngle <= PI/2 && ResultAngle >= -PI/2)
{
MPolloSpeed = sqrt(pow(Speed_Pollo.xSpeed, 2) + pow(Speed_Pollo.ySpeed, 2)); //Pollo本身速度的模
MCursorSpeed = MPolloSpeed * cos(ResultAngle) * 2; //通过碰撞,指针给予pollo的速度的模(v'只取了大小没取方向,速度大小与v是相同的)
MCursorSpeed += sqrt(pow(Speed_Cursor.xSpeed, 2) + pow(Speed_Cursor.ySpeed, 2)) * cos(CursorSpeedAngle); //指针能提供的有效速度加成
ResultSpeed.xSpeed = MCursorSpeed * cos(RealCursorAngle); //通过速度的模和指针给的速度的方向,计算出两个矢量相减之后的速度向量
ResultSpeed.ySpeed = MCursorSpeed * sin(RealCursorAngle);
ResultSpeed.xSpeed = ResultSpeed.xSpeed + Speed_Pollo.xSpeed; //通过与pollo自身速度矢量的平行位移,得出真实的速度向量
ResultSpeed.ySpeed = ResultSpeed.ySpeed + Speed_Pollo.ySpeed;<span style="white-space:pre"> </span>(本来是减v',但是-v = v'所以就变成了加上v了)
}
//当碰撞夹角不符合上面的角度时,即为矢量相加计算
else
{
MCursorSpeed = (sqrt(pow(Speed_Cursor.xSpeed, 2) + pow(Speed_Cursor.ySpeed, 2)) * 3) * cos(CursorSpeedAngle); //指针能提供的有效速度加成
ResultSpeed.xSpeed = MCursorSpeed * cos(RealCursorAngle); //通过指针提供的速度的模和指针碰撞的方向,计算出指针提供的速度的向量
ResultSpeed.ySpeed = MCursorSpeed * sin(RealCursorAngle);
ResultSpeed.xSpeed += Speed_Pollo.xSpeed; //通过将pollo自身速度向量平移到指针速度向量上,算出向量相加的结果
ResultSpeed.ySpeed += Speed_Pollo.ySpeed;
}
return ResultSpeed;
}
代码当中涉及到了另外两个概念,一个是指针速度,另一个是矢量相加
在碰撞的时候大多数情况下都不是指针静止的时候,因此碰撞的时候如果能够算上指针的速度肯定会更真实一些。那么指针的速度就是由定时器连续取的两个位置计算得到的。而指针提供的速度的方向也跟碰撞时2a的方向完全相同,在计算时仅仅只用在2a上额外加上指针速度在该方向的分量就可以了。
第二个是矢量相加。可以看到在上面的例子中,都是圆一头撞到指针上的情况,那么如果是指针速度比较快,从后面爆菊追上了圆呢,那么这种情况下实际上是矢量相加的情况。
这时候改变原本运动状态的力完全由指针本身的移动速度提供,这种情况相对于矢量相减的要简单很多,就不重新说明了
程序压缩包链接,使用的是vs2010平台
http://download.csdn.net/detail/polley88/9434737