动能公式:

动量公式:

动量守恒:

能量守恒:

根据这些规律可以得到下列方程组:

解该方程组,得到下面的公式:

把这二个公式相减,可以得到:

即:

我们也经常利用这个公式简化运算

基本的动量守恒演示:

先给ball类添加一个质量"属性"

package {

import flash.display.Sprite;

//小球 类

public class Ball extends Sprite {

public var radius:uint;//半径

public var color:uint;//颜色

public var vx:Number=0;//x轴速度

public var vy:Number=0;//y轴速度

public var count:uint=0;//辅助计数变量

public var isDragged=false;//是否正在被拖动

public var vr:Number=0;//旋转速度

public var mass:Number = 1;//质量

public function Ball(r:Number=50,c:uint=0xff0000) {

this.radius=r;

this.color=c;

init();

}

private function init():void {

graphics.beginFill(color);

graphics.drawCircle(0,0,radius);

graphics.endFill();

}

}

}

一维单轴刚体碰撞测试:

package {

import flash.display.Sprite;

import flash.events.Event;

public class Billiard1 extends Sprite {

private var ball0:Ball;

private var ball1:Ball;

private var bounce:Number = -0.6;

public function Billiard1() {

init();

}

private function init():void {

ball0=new Ball(40);         

addChild(ball0);

ball1=new Ball(20,0x0000ff);            

addChild(ball1);                    

ReStart();

}

private function ReStart():void{

ball0.mass=2;

ball0.x=50;

ball0.y=stage.stageHeight/2;

ball0.vx=5;

ball1.mass=1;

ball1.x=300;

ball1.y=stage.stageHeight/2;

ball1.vx=-5;        

addEventListener(Event.ENTER_FRAME,EnterFrameHandler);  

}

private function EnterFrameHandler(event:Event):void {

ball0.x+=ball0.vx;

ball1.x+=ball1.vx;

var dist:Number=ball1.x-ball0.x;

//如果撞到了

if (Math.abs(dist)<ball0.radius+ball1.radius) {

var vdx:Number = ball0.vx - ball1.vx;

var vx0Final:Number=((ball0.mass-ball1.mass)*ball0.vx + 2*ball1.mass*ball1.vx)/(ball0.mass+ball1.mass);

var vx1Final:Number= vx0Final + vdx;

ball0.vx=vx0Final;

ball1.vx=vx1Final;

//不加下面这二句的话,从视觉效果上看,有可能会看到二个球相互撞入对方球体内了,这样就不符合物理学"刚体"模型的定义

ball0.x+=ball0.vx;

ball1.x+=ball1.vx;

}

//舞台边界反弹

if (ball0.x >=stage.stageWidth-ball0.radius || ball0.x<=ball0.radius){

ball0.x -= ball0.vx;

ball0.vx *= bounce;

}           

if (ball1.x >=stage.stageWidth-ball1.radius || ball1.x<=ball1.radius){

ball1.x -= ball1.vx;

ball1.vx *= bounce;

}

trace(ball1.vx,ball0.vx);

//如果二球都停了

if (Math.abs(ball1.vx)<=0.05 && Math.abs(ball0.vx)<=0.05){

removeEventListener(Event.ENTER_FRAME,EnterFrameHandler);   

ReStart();

}

}

}

}

二维坐标上的刚体碰撞:

先来看这张图,红球a以Va速度运动,蓝球b以Vb速度运动,二球的连线正好与x轴平行(即:水平对心碰撞),碰撞的过程可以理解为二球水平速度分量Vax,Vbx应用运量守恒与能力守恒的结果(y轴方向的速度不受影响!)

但很多情况下,二球的连线并非总是与坐标轴平行,比如下面这样:

思路:仍然利用坐标旋转,先将二个球反向旋转到连线水平位置,然后按常规方式处理,完事后再旋转回来。

var ballA:Ball=new Ball(80,Math.random()*0xffffff);

var ballB:Ball=new Ball(50,Math.random()*0xffffff);

var bounce:Number=-1;

ballA.x=ballA.radius+100;

ballB.x=ballA.radius+200;

ballA.y=120;

ballB.y=300;

ballA.mass=2;

ballB.mass=1;

ballA.vx = 5*(Math.random()*2-1);

ballB.vx = 5*(Math.random()*2-1);

ballA.vy = 5*(Math.random()*2-1);

ballB.vy = 5*(Math.random()*2-1);

addChild(ballA);

addChild(ballB);

addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

function EnterFrameHandler(e:Event):void {

ballA.x+=ballA.vx;

ballA.y+=ballA.vy;

ballB.x+=ballB.vx;

ballB.y+=ballB.vy;

//运量守恒处理开始

var dx:Number=ballB.x-ballA.x;

var dy:Number=ballB.y-ballA.y;

var dist:Number=Math.sqrt(dx*dx+dy*dy);

if (dist<(ballA.radius + ballB.radius)) {

var angle:Number=Math.atan2(dy,dx);

var cos:Number=Math.cos(angle);

var sin:Number=Math.sin(angle);

//以ballA中心为旋转中心反向旋转

var xA:Number=0;//ballA自身为旋转中心,所以自身旋转后的相对坐标都是0

var yA:Number=0;

var xB:Number=dx*cos+dy*sin;

var yB:Number=dy*cos-dx*sin;

//先(反向)旋转二球相对(ballA的)速度

var vxA=ballA.vx*cos+ballA.vy*sin;

var vyA=ballA.vy*cos-ballA.vx*sin;

var vxB=ballB.vx*cos+ballB.vy*sin;

var vyB=ballB.vy*cos-ballB.vx*sin;

//旋转后的vx速度处理运量守恒

var vdx=vxA-vxB;

var vxAFinal = ((ballA.mass - ballB.mass)*vxA + 2*ballB.mass*vxB)/(ballA.mass + ballB.mass);

var vxBFinal=vxAFinal+vdx;

//相对位置处理

xA+=vxAFinal;

xB+=vxBFinal;

//处理完了,再旋转回去

//先处理坐标位置

var xAFinal:Number=xA*cos-yA*sin;

var yAFinal:Number=yA*cos+xA*sin;

var xBFinal:Number=xB*cos-yB*sin;

var yBFinal:Number=yB*cos+xB*sin;

//处理最终的位置变化

ballB.x=ballA.x+xBFinal;

ballB.y=ballA.y+yBFinal;

ballA.x+=xAFinal;

ballA.y+=yAFinal;

//再处理速度

ballA.vx=vxAFinal*cos-vyA*sin;

ballA.vy=vyA*cos+vxAFinal*sin;

ballB.vx=vxBFinal*cos-vyB*sin;

ballB.vy=vyB*cos+vxBFinal*sin;

}

//<--- 运量守恒处理结束

CheckBounds(ballA);

CheckBounds(ballB);

}

//舞台边界检测

function CheckBounds(b:Ball) {

if (b.x<b.radius) {

b.x=b.radius;

b.vx*=bounce;

} else if (b.x>stage.stageWidth-b.radius) {

b.x=stage.stageWidth-b.radius;

b.vx*=bounce;

}

if (b.y<b.radius) {

b.y=b.radius;

b.vy*=bounce;

} else if (b.y>stage.stageHeight-b.radius) {

b.y=stage.stageHeight-b.radius;

b.vy*=bounce;

}

}

粘连问题:

反复运行上面这段动画,偶尔可能会发现二个球最终粘在一起,无法分开了,造成这种原因的情况很多,下面的示意图分析了可能的形成原因之一

解决思路:找出重叠部分,然后把二个小球同时反向移动适当距离,让二个球分开即可

先来一段测试代码:验证一下是否有效

var ballA:Ball=new Ball(80,0xff0000);

ballA.x=stage.stageWidth/2;

ballA.y=stage.stageHeight/2;

addChild(ballA);

var ballB:Ball=new Ball(60,0x00ff00);

ballB.x=stage.stageWidth/2-70;

ballB.y=stage.stageHeight/2;

addChild(ballB);

btn1.x=stage.stageWidth/2;

btn1.y=stage.stageHeight-btn1.height;

btn1.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);

function MouseDownHandler(e:MouseEvent):void {

var overlap:Number=ballA.radius+ballB.radius-Math.abs(ballA.x-ballB.x);//计算重叠部分

trace(overlap);

//计算每个球所占重叠部分中的比例

var aRadio:Number = ballA.radius/(ballA.radius + ballB.radius);

var bRadio:Number = ballB.radius/(ballA.radius + ballB.radius);

//分离判断

if (overlap>0){

if (ballA.x>ballB.x){

ballA.x += overlap*aRadio;

ballB.x -= overlap*bRadio;

}

else{

ballA.x -= overlap*aRadio;

ballB.x += overlap*bRadio;

}

}

}

ballA.addEventListener(MouseEvent.MOUSE_DOWN,startDragHandler);

ballB.addEventListener(MouseEvent.MOUSE_DOWN,startDragHandler);

ballA.addEventListener(MouseEvent.MOUSE_OVER,MouseOverHandler);

ballA.addEventListener(MouseEvent.MOUSE_OUT,MouseOutHandler);

ballB.addEventListener(MouseEvent.MOUSE_OVER,MouseOverHandler);

ballB.addEventListener(MouseEvent.MOUSE_OUT,MouseOutHandler);

stage.addEventListener(MouseEvent.MOUSE_UP,stopDragHandler);

var obj:Ball;

var rect:Rectangle = new Rectangle(0,stage.stageHeight/2,stage.stageWidth,0);

function startDragHandler(e:MouseEvent):void {

Mouse.cursor = MouseCursor.HAND;

obj=e.currentTarget as Ball;

obj.startDrag();

}

function stopDragHandler(e:MouseEvent):void {

if (obj!=null) {

obj.stopDrag(true,rect);

obj=null;

Mouse.cursor = MouseCursor.AUTO;

}

}

function MouseOverHandler(e:MouseEvent):void{

Mouse.cursor = MouseCursor.HAND;

}

function MouseOutHandler(e:MouseEvent):void{

Mouse.cursor = MouseCursor.AUTO;

}

水平拖动小球故意让它们重叠,然后点击“分开”按钮测试一下,ok,管用了!

再回过头来解决运量守恒中的粘连问题:

只要把EnterFrameHandler中的

//相对位置处理  

xA+=vxAFinal;  

xB+=vxBFinal;

换成:

//相对位置处理(同时要防止粘连)

//xA+=vxAFinal;

//xB+=vxBFinal;

var sumRadius = ballA.radius + ballB.radius;

var overlap:Number=sumRadius-Math.abs(xA-xB);//计算重叠部分

//trace(overlap);

//计算每个球所占重叠部分中的比例

var aRadio:Number = ballA.radius/sumRadius;

var bRadio:Number = ballB.radius/sumRadius;

//分离判断

if (overlap>0){

if (xA>xB){

xA += overlap*aRadio;

xB -= overlap*bRadio;

}

else{

xA -= overlap*aRadio;

xB += overlap*bRadio;

}

}

最后老规矩:来一个群魔乱舞,把一堆球放在一块儿乱撞

package {

import flash.display.Sprite;

import flash.events.Event;

import flash.geom.Point;

public class MultiBilliard extends Sprite {

private var balls:Array;

private var numBalls:uint=8;

private var bounce:Number=-1.0;

public function MultiBilliard() {

init();

}

private function init():void {

balls = new Array();

for (var i:uint = 0; i < numBalls; i++) {

var radius:Number=Math.random()*40+10;

var ball:Ball=new Ball(radius,Math.random()*0xffffff);

ball.mass=radius;

ball.x=i*100;

ball.y=i*50;

ball.vx=Math.random()*10-5;

ball.vy=Math.random()*10-5;

addChild(ball);

balls.push(ball);

}

addEventListener(Event.ENTER_FRAME, onEnterFrame);

}

private function onEnterFrame(event:Event):void {

for (var i:uint = 0; i < numBalls; i++) {

var ball:Ball=balls[i];

ball.x+=ball.vx;

ball.y+=ball.vy;

checkWalls(ball);

}

for (i = 0; i < numBalls - 1; i++) {

var ballA:Ball=balls[i];

for (var j:Number = i + 1; j < numBalls; j++) {

var ballB:Ball=balls[j];

checkCollision(ballA, ballB);

}

}

}

//舞台边界检测

function checkWalls(b:Ball) {

if (b.x<b.radius) {

b.x=b.radius;

b.vx*=bounce;

} else if (b.x>stage.stageWidth-b.radius) {

b.x=stage.stageWidth-b.radius;

b.vx*=bounce;

}

if (b.y<b.radius) {

b.y=b.radius;

b.vy*=bounce;

} else if (b.y>stage.stageHeight-b.radius) {

b.y=stage.stageHeight-b.radius;

b.vy*=bounce;

}

}

private function rotate(x:Number, y:Number, sin:Number, cos:Number, reverse:Boolean):Point {

var result:Point = new Point();

if (reverse) {

result.x=x*cos+y*sin;

result.y=y*cos-x*sin;

} else {

result.x=x*cos-y*sin;

result.y=y*cos+x*sin;

}

return result;

}

private function checkCollision(ball0:Ball, ball1:Ball):void {

var dx:Number=ball1.x-ball0.x;

var dy:Number=ball1.y-ball0.y;

var dist:Number=Math.sqrt(dx*dx+dy*dy);

if (dist<ball0.radius+ball1.radius) {

// 计算角度和正余弦值 

var angle:Number=Math.atan2(dy,dx);

var sin:Number=Math.sin(angle);

var cos:Number=Math.cos(angle);

// 旋转 ball0 的位置 

var pos0:Point=new Point(0,0);

// 旋转 ball1 的速度 

var pos1:Point=rotate(dx,dy,sin,cos,true);

// 旋转 ball0 的速度 

var vel0:Point=rotate(ball0.vx,ball0.vy,sin,cos,true);

// 旋转 ball1 的速度 

var vel1:Point=rotate(ball1.vx,ball1.vy,sin,cos,true);

// 碰撞的作用力 

var vxTotal:Number=vel0.x-vel1.x;

vel0.x = ((ball0.mass - ball1.mass) * vel0.x + 2 * ball1.mass * vel1.x) / (ball0.mass + ball1.mass);

vel1.x = vxTotal+vel0.x;

// 更新位置 

var absV:Number=Math.abs(vel0.x)+Math.abs(vel1.x);

var overlap:Number = (ball0.radius + ball1.radius) - Math.abs(pos0.x - pos1.x);

pos0.x += vel0.x/absV*overlap;

pos1.x += vel1.x/absV*overlap;

// 将位置旋转回来 

var pos0F:Object=rotate(pos0.x,pos0.y,sin,cos,false);

var pos1F:Object=rotate(pos1.x,pos1.y,sin,cos,false);

// 将位置调整为屏幕的实际位置 

ball1.x=ball0.x+pos1F.x;

ball1.y=ball0.y+pos1F.y;

ball0.x=ball0.x+pos0F.x;

ball0.y=ball0.y+pos0F.y;

// 将速度旋转回来 

var vel0F:Object=rotate(vel0.x,vel0.y,sin,cos,false);

var vel1F:Object=rotate(vel1.x,vel1.y,sin,cos,false);

ball0.vx=vel0F.x;

ball0.vy=vel0F.y;

ball1.vx=vel1F.x;

ball1.vy=vel1F.y;

}

}

}

}

注:这段代码做了优化,把一些公用的部分提取出来封装成function了,同时对于粘连问题的解决,采用了更一种算法

后记:弄懂了本文中的这些玩意儿有啥用呢?让我想想,或许...公司需要开发一款桌面台球游戏时,这东西就能派上用场吧.

Flash/Flex学习笔记(38):动量守恒与能量守恒相关推荐

  1. Flash/Flex学习笔记(30):不用startDrag和stopDrag的对象拖动

    对于从Sprite类继承来的对象,要实现拖放当然是Flash/Flex学习笔记(13):对象拖动(startDrag/stopDrag) 里讲的方法最方便,但是对于不是从Sprite类继承得来的对象, ...

  2. Flash/Flex学习笔记(51):3维旋转与透视变换(PerspectiveProjection)

    Flash/Flex学习笔记(49):3D基础 里已经介绍了3D透视的基本原理,不过如果每次都要利用象该文中那样写一堆代码,估计很多人不喜欢,事实上AS3的DisplayObject类已经内置了z坐标 ...

  3. Flash/Flex学习笔记(43):动量守恒与能量守恒

    动能公式: 动量公式: 动量守恒: 能量守恒: 根据这些规律可以得到下列方程组: 解该方程组,得到下面的公式: 把这二个公式相减,可以得到: 即: 我们也经常利用这个公式简化运算 基本的动量守恒演示: ...

  4. Flash/Flex学习笔记(50):3D线条与填充

    3D线条:把上一篇中的3D坐标旋转示例稍做修改,用线把各个小球连接起来即可. var balls:Array; var numBalls:uint=30;var fl:Number=250; var ...

  5. Flash/Flex学习笔记(53):利用FMS快速创建一个文本聊天室

    先来看客户端fla的构成: 第一帧:登录界面 第一帧的代码: show sourceview source print? 01 import flash.events.MouseEvent; 02 i ...

  6. Flash/Flex学习笔记(4):如何打开网页及Get/Post数据

    flash终究只是客户端技术,所以很多时候还是需要与服务端技术(比如asp,asp.net,jsp,php之类)进行数据交互的,下面的代码演示了如何在flash中打开网页,以及用GET/POST二种方 ...

  7. Flash/Flex学习笔记(2):捕获摄像头

    Flash中使用摄像头,个人感觉比silverlight要更容易 时间轴第一帧上敲以下代码就可以了: //import fl.controls.Label; //var camera:Camera = ...

  8. Flash/Flex学习笔记(34):AS3中的自定义事件

    类似C#中自定义事件需要一个自定义的EventArgs子类一样,AS3也需要开发者自定义一个Event类的子类,这里我们假设一种场景:设计一个Person(人物)类,里面有Age(年龄),Name(姓 ...

  9. Flash/Flex学习笔记(8):ActionScript3.0中的面对对象

    首先要习惯AS3.0的几个BT约定: 1.一个.as文件中,只能定义一个类 2.类名称必须与.as的文件名相同 3.类定义中必须要有package包声明 4.一个类最多只能有一个构造函数 5.包pac ...

最新文章

  1. 大数据WEB阶段(四)JavaScript
  2. 发现 postman 自动生成接口调用代码的一个问题
  3. 5 月份最热的 GitHub 项目
  4. osip2 代码分析
  5. linux中vim查看最后五行命令,Linux系统中Vi常用命令及高级应用
  6. 计算机应用技术试卷在线老师,试卷,计-计算机应用技术.pdf
  7. JVM的内存管理 Ⅰ
  8. 学会java要多久_多长时间可以学会Java?
  9. 【数据结构的魅力】004.堆、前缀树、桶排序、排序算法总结
  10. 任务管理器杀不了的进程如何关闭
  11. 免费沙龙:PRINCE2项目管理认证价值与应用
  12. Flink 结合 布隆过滤器(BloomFilter) 实现去重
  13. mac机c4d更改语言,Win/Mac版:C4D R18 三维软件 Cinema 4D C4D R18 正式完整版 + 中文/英文注册机版...
  14. mysql之第n高的薪水
  15. 如何退出Dos——附DOS命令大全
  16. 360浏览器不能导入html,360浏览器无法导入收藏夹的解决方法
  17. 大模型“涌现”的思维链,究竟是一种什么能力?
  18. EditText 获取不到焦点
  19. 如何压缩Json格式数据,减少Json数据的体积?
  20. patch diff 补丁文件

热门文章

  1. jsp+ssm计算机毕业设计亿上汽车在线销售管理系统【附源码】
  2. MenuInfo.java10
  3. 《会计学》账户与复式记账笔记的思维导图
  4. 微信公众平台测试号——模板消息发送Demo
  5. 计算机科学家帕斯卡,帕斯卡-世界上最伟大的科学家排行榜-天天排行网
  6. C#使用委托实现信用卡用户定时还款功能
  7. CVE-2022-22916
  8. M-K趋势检验以及突变检验
  9. oss回调异常 Private address is forbidden to callback, 502,404(400/403)
  10. 《高效能人士的7个习惯》——习惯三:要事第一 之 独立意志的重要性