基于Win API,通过算法实现圆与鼠标指针(点)的碰撞的模拟

最近稍微研究了一下通过移动鼠标指针来与一个二维球(就是个圆)碰撞的算法,一开始的思路是通过纯几何+物理公式的方式来实现功能,但是后来发现这样实现起来实在是很复杂,用了太多的三角函数,而且还设计到了很多不同情况的处理。后来想到了物理当中力的传导本质上是两个矢量的相加和相减,试了一下用矢量的方式来表示碰撞情况,果然比纯几何的要简单很多,计算也简单了一些,如果有更好的算法也各位看官也可以探讨一下。

先明确几个概念。首先,在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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值