由于要写一个模拟多个小球相互碰撞的程序,所以我就研究了下两个小球相互碰撞之后的速度变化规律(理想环境下无摩擦无碰撞损耗等的完全弹性碰撞)。

 

我们都知道,两个小球如果是在同一条直线上相向而行,则我们可以通过能量守恒(动能守恒)和动量守恒来计算碰撞之后的速度,若两小球质量相等,则交换它们的速度。若质量不相等,则可以求出:

 

设两小球的质量为m1,m2,碰撞前速度为v1,v2,碰撞后速度为v'1,v'2。

则有以下两个公式同时成立:

m1 * v1 + m2 * v2 = m1 * v'1 + m2 * v'2 ---------------------------------------------------------------------(1)

1/2 * m1 * v1^2 + 1/2 * m2 * v2^2 = 1/2 * m1 * v'1^2 + 1/2 * m2 * v'2^2 -----------------------------(2)

 

(1)、(2)可化为:

m1 * v1 - m1 * v'1 = m2 * v'2 - m2 * v2 ----------------------------------------------------------------------(3)

1/2 * m1 * v1^2 - 1/2 * m1 * v'1^2  = 1/2 * m2 * v'2^2 - 1/2 * m2 * v2^2 -----------------------------(4)

 

(4)/(3)得:

v1 + v'1 = v2 + v'2 -------------------------------------------------------------------------------------------------(5)

 

(1)、(5)可化为:

m2 * v'2 = m1 * v1 + m2 * v2 - m1 * v'1 ---------------------------------------------------------------------(6)

v'2 = v1 + v'1 - v2 --------------------------------------------------------------------------------------------------(7)

 

把(7)代入(6)得:

m2 * v1 + m2 * v'1 - m2 * v2 = m1 * v1 + m2 * v2 - m1 * v'1 -------------------------------------------(8)

 

由(8)化简得:

v'1 = [ 2 * m2 * v2 + ( m1 - m2 ) * v1 ] / ( m1 + m2 )

同理可得:

v'2 = [ 2 * m1 * v1 + ( m2 - m1 ) * v2 ] / ( m1 + m2 )

 

这样就计算出碰撞后两小球的速度了。

 

但是,两小球能在同一直线上相向而行的概率基本为零,最常发生的碰撞则是这样的:

 

 

所以,我们得想办法把这样的碰撞情况转化为我们会处理的情况,加以计算。

 

那么我们来分析一下:

两小球的碰撞点在两球心的连线上,那么在理想状态下,两小球的相互作用力应该是沿着两球心的连线方向的。

根据牛顿第一定律,力是改变屋里运动状态的唯一原因,既然在垂直于两球心的连线的方向上没有力的作用,那么两小球在垂直于两球心的连线的方向上的分运动是不变的。

这样的话,两小球在垂直于两球心的连线的方向上的速度是不变的,那么同样这个方向上的动量和动能也是不变的。

那么,两小球在两球心的连线方向的分运动也遵循动量守恒和动能守恒。

 

这样,我们就能把一般碰撞事件转化为我们可以解决的在同一条直线上相向而行的碰撞事件来进行计算了。

 

现在,我们来分析一下分速度应该如何计算。

设小球速度为a,两球心连线为b,小球速度在两球心连线方向上的分速度为c,小球速度在垂直于两球心连线的方向上的分速度为d。

那么· = ( ) · · · b

db相互垂直,所以· b = 0。

那么· = c · b

 

b的斜率为k,那么可以再设= ( x1, y1 ),= ( x2, k · x2 ),= ( x, k · x ),

那么代入得:

x1 · x2 + y1 · ( k · x2 ) = x · x2 + ( k · x ) · ( k · x2 )。

x = ( x1 · x2 + k · y1 · x2 ) / ( x2 + k^2 · x2 )

x = ( x1 + k · y1 ) / ( k^2 + 1 )

那么k · x为:

k · x = k · ( x1 + k · y1 ) / ( k^2 + 1 )

k · x =  ( x1 + k · y1 ) / ( k + 1 / k )

 

现在回过头来,把这个公式代入小球的数据:

设碰撞时,小球1的速度为( v1x, v1y ),位置为( x1, y1 );小球2的速度为( v1x, v1y ),位置为( x2, y2 ),两球心连线方向的斜率为k。

 

那么小球1在两球心连线方向上的分速度v1_1,

它在x轴方向的分速度为:

v1_1x = ( v1x + k · v1y ) / (  k^2 + 1 )

它在x轴方向的分速度为:

v1_1y = k * v1_1x

即:

v1_1 = ( ( v1x + k · v1y ) / (  k^2 + 1 ), ( v1x + k · v1y ) / ( k + 1 / k ) )

 

那么小球1在垂直于两球心连线的方向上的分速度v1_0,

 

它在x轴方向的分速度为:

v1_0x = v1x - v1_1x

它在x轴方向的分速度为:

v1_0y = v1y - v1_1y = v1y - ( v1x + k · v1y ) / ( k + 1 / k )

即:

v1_0 = ( v1x - ( v1x + k · v1y ) / (  k^2 + 1 ), v1y - ( v1x + k · v1y ) / ( k + 1 / k ) )

 

 

同理,小球2在两球心连线方向上的分速度为:

v2_1 = ( v2_1x, v2_1y ) = ( ( v2x + k · v2y ) / (  k^2 + 1 ), ( v2x + k · v2y ) / ( k + 1 / k ) )

小球2在垂直于两球心连线的方向上的分速度为:

v2_0 = ( v2_0x, v2_0y ) = ( v2x - ( v2x + k · v2y ) / (  k^2 + 1 ), v2y - ( v2x + k · v2y ) / ( k + 1 / k ) )

 

经过计算,我们可以得到碰撞后两小球在两球心连线方向上的分速度v_1_1,v_2_1。

那么碰撞后两小球的速度为v_1 = v1_0 + v_1_1,v_2 = v2_0 + v_2_1

 

当然,若写成代码则不需要那么麻烦,只要定义几个中间变量,这些结果都可以由计算机计算(当然,下面的代码只是理想状况下的,因为线程的刷新是有间隔的,那么就会出现两小球边缘越过相切,达到相交的状态,解决这个问题还是相当简单的,自己去考虑吧。):

public void bumpEachother(Ball ball1, Ball ball2) {
		//获取两小球的质量
		float m1 = ball1.getM();
		float m2 = ball2.getM();
		
		//获取两小球的位置
		Point2D.Float position1 = ball1.getPosition();
		Point2D.Float position2 = ball2.getPosition();
		
		//获取两小球的速度
		Point2D.Float velocity1 = ball1.getVelocity();
		Point2D.Float velocity2 = ball2.getVelocity();
		
		//定义碰撞后两小球的速度
		Point2D.Float velocity_1 = new Point2D.Float();
		Point2D.Float velocity_2 = new Point2D.Float();
		
		//定义两小球在垂直于两球心连线的方向的速度
		Point2D.Float velocity1_0 = new Point2D.Float();
		Point2D.Float velocity2_0 = new Point2D.Float();
		
		//定义碰撞前两小球在两球心连线方向的速度
		Point2D.Float velocity1_1 = new Point2D.Float();
		Point2D.Float velocity2_1 = new Point2D.Float();
		
		//定义碰撞后两小球在两球心连线方向的速度
		Point2D.Float velocity_1_1 = new Point2D.Float();
		Point2D.Float velocity_2_1 = new Point2D.Float();
		
		//定义两球心连线的斜率为k
		float k = (position1.y - position2.y) / (position1.x - position2.x);
		
		//计算碰撞前两小球在两球心连线方向的速度
		velocity1_1.x = (velocity1.x + k * velocity1.y) / (1 + k * k);
		velocity1_1.y = k * velocity1_1.x;
		velocity2_1.x = (velocity2.x + k * velocity2.y) / (1 + k * k);
		velocity2_1.y = k * velocity2_1.x;
		
		//计算两小球在垂直于两球心连线的方向的速度
		velocity1_0.x = velocity1.x - velocity1_1.x;
		velocity1_0.y = velocity1.y - velocity1_1.y;
		velocity2_0.x = velocity2.x - velocity2_1.x;
		velocity2_0.y = velocity2.y - velocity2_1.y;
		
		//计算碰撞后两小球在两球心连线方向的速度
		velocity_1_1.x = (2 * m2 * velocity2_1.x + (m1 - m2) * velocity1_1.x) / (m1 + m2);
		velocity_1_1.y = (2 * m2 * velocity2_1.y + (m1 - m2) * velocity1_1.y) / (m1 + m2);
		velocity_2_1.x = (2 * m1 * velocity1_1.x + (m2 - m1) * velocity2_1.x) / (m1 + m2);
		velocity_2_1.y = (2 * m1 * velocity1_1.y + (m2 - m1) * velocity2_1.y) / (m1 + m2);
		
		//计算碰撞后两小球的速度
		velocity_1.x = velocity1_0.x + velocity_1_1.x;
		velocity_1.y = velocity1_0.y + velocity_1_1.y;
		velocity_2.x = velocity2_0.x + velocity_2_1.x;
		velocity_2.y = velocity2_0.y + velocity_2_1.y;
		
		//重设两小球速度
		ball1.setVelocity(velocity_1);
		ball2.setVelocity(velocity_2);
	}

 

 

好了,这样的话,两小球之间的相互碰撞事件的处理就完成啦,只要判断出两小球相撞,就可以调用这个类来改变小球速度啦~~


 

有人要Ball类的代码,我找了半天才找到,毕竟已经过了这么久了,不一定是一个版本的,不知道有没有问题…… 

import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;

/**
 * @author lolo
 *
 */
public class Ball {
	
	//小球半径
	private float radius;
	
	//小球位置
	private Point2D.Float position;
	
	//小球速度
	private Point2D.Float velocity;
	
	//小球形状
	private Ellipse2D.Float sphere;
	
	/**
	 * 构造方法
	 * 
	 * @param radius the radius to set
	 * @param density the density to set
	 * @param position the position to set
	 * @param velocity the velocity to set
	 */
	public Ball(float radius, float density, Point2D.Float position, Point2D.Float velocity) {
		this.radius = radius;
		this.position = position;
		this.velocity = velocity;
		this.sphere = new Ellipse2D.Float(0, 0, 2 * radius, 2 * radius);
	}
	
	/**
	 * 绘制小球
	 * 
	 * @param g the Graphics2D which is used to draw
	 */
	public void drawBall(Graphics2D g){
		sphere.x = position.x - radius;
		sphere.y = position.y - radius;
		g.fill(sphere);
	}

	/**
	 * @return the radius
	 */
	public float getRadius() {
		return radius;
	}

	/**
	 * @return the position
	 */
	public Point2D.Float getPosition() {
		return position;
	}

	/**
	 * @param position the position to set
	 */
	public void setPosition(Point2D.Float position) {
		this.position = position;
	}

	/**
	 * @param positionX the position.x to set
	 */
	public void setPositionX(float positionX) {
		this.position.x = positionX;
	}

	/**
	 * @param positionY the position.y to set
	 */
	public void setPositionY(float positionY) {
		this.position.y = positionY;
	}

	/**
	 * @return the velocity
	 */
	public Point2D.Float getVelocity() {
		return velocity;
	}

	/**
	 * @param velocity the velocity to set
	 */
	public void setVelocity(Point2D.Float velocity) {
		this.velocity = velocity;
	}

	/**
	 * @param velocityX the velocity.x to set
	 */
	public void setVelocityX(float velocityX) {
		this.velocity.x = velocityX;
	}

	/**
	 * @param velocityY the velocity.y to set
	 */
	public void setVelocityY(float velocityY) {
		this.velocity.y = velocityY;
	}
}