Flash/Flex学习笔记(41):碰撞检测
碰撞检测基本上可能分为二类:对象与对象的碰撞检测、对象与点的碰撞检测
为了方便测试,先写一个box类(生成一个小矩形)
01 package {
02
03 import flash.display.Sprite;
04
05 public class Box extends Sprite {
06
07 private var w:Number;
08 private var h:Number;
09 private var color:uint;
10 public var vx:Number=0;
11 public var vy:Number=0;
12
13 public function Box(width:Number=50, height:Number=50, color:uint=0xff0000) {
14 w=width;
15 h=height;
16 this.color=color;
17 init();
18 }
19
20 public function init():void {
21 graphics.beginFill(color);
22 graphics.drawRect(-w / 2, -h / 2, w, h);
23 graphics.endFill();
24 }
25 }
26 }
最基本的对象碰撞检测:hitTestObject
01 package {
02
03 import flash.display.Sprite;
04 import flash.events.Event;
05
06 public class Boxes extends Sprite {
07
08 private var box:Box;
09 private var boxes:Array;
10 private var gravity:Number=0.1;
11
12 public function Boxes() {
13 init();
14 }
15
16 private function init():void {
17 boxes = new Array();
18 createBox();
19 addEventListener(Event.ENTER_FRAME, onEnterFrame);
20 }
21
22 private function onEnterFrame(event:Event):void {
23 box.vy+=gravity;
24 box.y+=box.vy;
25
26 //如果物体下落到了舞台(最下)边界,则再造一个出来往下掉
27 if (box.y+box.height/2>stage.stageHeight) {
28 box.y=stage.stageHeight-box.height/2;
29 createBox();
30 } else{
31 for (var i:uint = 0; i < boxes.length; i++) {
32 //每个正在下掉的物体与其它物体做(矩形)碰撞检测
33 if (box!=boxes[i]&&box.hitTestObject(boxes[i])) {
34 box.y=boxes[i].y-boxes[i].height/2-box.height/2;
35 //堆到顶了,则停止
36 if (box.y<=box.height/2){
37 removeEventListener(Event.ENTER_FRAME,onEnterFrame);
38 }
39 else{
40 createBox();
41 }
42 }
43 }
44 }
45
46 }
47
48 private function createBox():void {
49 box=new Box(Math.random()*40+10,Math.random()*40+10,Math.random()*0xffffff);
50 box.x=Math.random()*stage.stageWidth;
51 addChild(box);
52 boxes.push(box);
53 }
54 }
55 }
如果把Box换成前面例子中的Ball,就是下面这个样子:
很明显:矩形换成球后,碰撞检测变得不精确了,有一些球似乎并没有真正撞到其它球也停下来了,这是为什么腻?


答案就在于:Flash对象碰撞检测默认采用“对象的矩形边界”做为检测依据。上面二张图演示了这一细节:第一张图虽然肉眼看上去只有二个矩形相交了,但是在Flash看来,其实每对图形都碰到了(第二张图),所以大家应该也能明白为啥换成球后,有些球会浮在空中了。
对象与点的碰撞检测:hitTestPoint
01 package {
02 import flash.display.Sprite;
03 import flash.events.Event;
04 import flash.text.TextField;
05
06 public class PointHitTest extends Sprite {
07 private var ball:Ball;
08 private var box:Box;
09 private var txt:TextField = new TextField();
10
11 public function PointHitTest() {
12 init();
13 }
14
15 private function init():void {
16 ball=new Ball;
17 addChild(ball);
18 ball.x=stage.stageWidth/2;
19 ball.y=stage.stageHeight/2;
20
21 box = new Box(90,90);
22 addChild(box);
23 box.x = 100;
24 box.y = ball.y;
25
26 addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
27
28 addChild(txt);
29 txt.selectable = false;
30 }
31
32 private function EnterFrameHandler(event:Event):void {
33 if (ball.hitTestPoint(mouseX,mouseY) || box.hitTestPoint(mouseX,mouseY)) {
34 txt.text = "碰到了!";
35 }
36 else{
37 txt.text = "";
38 }
39 txt.x = mouseX + 15;
40 txt.y = mouseY;
41 }
42 }
43 }
用鼠标在二个物体上划过,会看到鼠标所在点与矩形及小球的碰撞检测结果,同样这里也存在一个问题:对于小球而言,默认也是采用矩形边界检测的,所以鼠标移到小球的边角时,虽然还没碰到球,也提示"碰到了",还好Flash提供了一个可选参数,以改进检测的精确度,只要把hitTestPoint第三个可选参数设置为true即可
if (ball.hitTestPoint(mouseX,mouseY) || box.hitTestPoint(mouseX,mouseY,true)) {
基于距离的检测:即检测二个物体的中心点距离是否低于最小距离
01 var ball_1:Ball=new Ball(70,0xff0000);
02 var ball_2:Ball=new Ball(70,0x0000ff);
03
04 ball_1.x=stage.stageWidth/2;
05 ball_1.y=stage.stageHeight/2;
06
07 ball_2.x=stage.stageWidth/2;
08 ball_2.y=stage.stageHeight/2;
09
10 ball_1.vx = Math.random()*20 - 20;
11 ball_1.vy = Math.random()*20 - 20;
12 ball_2.vx = Math.random()*20 - 20;
13 ball_2.vy = Math.random()*20 - 20;
14
15 addChild(ball_1);
16 addChild(ball_2);
17
18 addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
19
20 function EnterFrameHandler(e:Event):void {
21 ball_1.x+=ball_1.vx;
22 ball_1.y+=ball_1.vy;
23 ball_2.x+=ball_2.vx;
24 ball_2.y+=ball_2.vy;
25 CheckBoundary(ball_1);
26 CheckBoundary(ball_2);
27
28 var dx:Number=ball_1.x-ball_2.x;
29 var dy:Number=ball_1.y-ball_2.y;
30 var dist:Number=Math.sqrt(dx*dx+dy*dy);
31 if (dist<(ball_1.radius + ball_2.radius)) {
32
33 var angle:Number=Math.atan2(dy,dx);
34 ball_1.vx=dist*Math.cos(angle)*0.1;
35 ball_1.vy=dist*Math.sin(angle)*0.1;
36 ball_2.vx=dist*Math.cos(angle)*-0.1;
37 ball_2.vy=dist*Math.sin(angle)*-0.1;
38 }
39 }
40
41 function CheckBoundary(b:Ball) {
42 if (b.x>stage.stageWidth-b.width/2||b.x<=b.width/2) {
43 b.x-=b.vx;
44 b.vx*=-1;
45 }
46
47 if (b.y>stage.stageHeight-b.height/2||b.y<=b.height/2) {
48 b.y-=b.vy;
49 b.vy*=-1;
50 }
51 }
很明显,这种方法对于圆形物体是十分精确的,但对于非规则形状,只能近似检测.
如果结合上二篇提到的弹性运动,可以做出更复杂的动画:
01 package {
02 import flash.display.Sprite;
03 import flash.events.Event;
04 public class Bubbles extends Sprite {
05 private var balls:Array;
06 private var numBalls:Number=10;
07 private var centerBall:Ball;
08 private var bounce:Number=-1;
09 private var spring:Number=0.2;
10 public function Bubbles() {
11 init();
12 }
13 private function init():void {
14 balls=new Array ;
15 centerBall=new Ball(100,0xcccccc);
16 addChild(centerBall);
17 centerBall.x=stage.stageWidth/2;
18 centerBall.y=stage.stageHeight/2;
19 for (var i:uint=0; i<numBalls; i++) {
20 var ball:Ball=new Ball(Math.random()*40+5,Math.random()*0xffffff);
21 ball.x=Math.random()*stage.stageWidth;
22 ball.y=Math.random()*stage.stageHeight;
23 ball.vx=(Math.random()*2-1)*10;
24 ball.vy=(Math.random()*2-1)*10;
25 addChild(ball);
26 balls.push(ball);
27 }
28 addEventListener(Event.ENTER_FRAME,onEnterFrame);
29 }
30 private function onEnterFrame(event:Event):void {
31 for (var i:uint=0; i<numBalls; i++) {
32 var ball:Ball=balls[i];
33 move(ball);
34 var dx:Number=ball.x-centerBall.x;
35 var dy:Number=ball.y-centerBall.y;
36 var dist:Number=Math.sqrt(dx*dx+dy*dy);
37 var minDist:Number=ball.radius+centerBall.radius;
38 if (dist<minDist) {
39 var angle:Number=Math.atan2(dy,dx);
40 var tx:Number=centerBall.x+Math.cos(angle)*minDist;//弹性运动的目标点x坐标
41 var ty:Number=centerBall.y+Math.sin(angle)*minDist;//弹性运动的目标点y坐标
42 ball.vx+=(tx-ball.x)*spring;
43 ball.vy+=(ty-ball.y)*spring;
44 }
45 }
46 }
47 private function move(ball:Ball):void {
48 ball.x+=ball.vx;
49 ball.y+=ball.vy;
50 if (ball.x+ball.radius>stage.stageWidth) {
51 ball.x=stage.stageWidth-ball.radius;
52 ball.vx*=bounce;
53 } else if (ball.x-ball.radius<0) {
54 ball.x=ball.radius;
55 ball.vx*=bounce;
56 }
57 if (ball.y+ball.radius>stage.stageHeight) {
58 ball.y=stage.stageHeight-ball.radius;
59 ball.vy*=bounce;
60 } else if (ball.y-ball.radius<0) {
61 ball.y=ball.radius;
62 ball.vy*=bounce;
63 }
64 }
65 }
66 }
原理图:

多物体基于距离的碰撞检测:
01 package {
02 import flash.display.Sprite;
03 import flash.events.Event;
04 public class Bubbles2 extends Sprite {
05 private var balls:Array;
06 private var numBalls:Number=20;
07 private var bounce:Number=-0.9;
08 private var spring:Number=0.2;
09 private var gravity:Number=1;
10 public function Bubbles2() {
11 init();
12 }
13 private function init():void {
14 balls = new Array();
15 for (var i:uint = 0; i < numBalls; i++) {
16 var ball:Ball=new Ball(Math.random()*30+20,Math.random()*0xffffff);
17 ball.x=Math.random()*stage.stageWidth;
18 ball.y=Math.random()*stage.stageHeight;
19 ball.vx=Math.random()*6-3;
20 ball.vy=Math.random()*6-3;
21 addChild(ball);
22 balls.push(ball);
23 }
24 addEventListener(Event.ENTER_FRAME, onEnterFrame);
25 }
26 private function onEnterFrame(event:Event):void {
27 for (var i:uint = 0; i < numBalls - 1; i++) {
28 var ball0:Ball=balls[i];
29 for (var j:uint = i + 1; j < numBalls; j++) {
30 var ball1:Ball=balls[j];
31 var dx:Number=ball1.x-ball0.x;
32 var dy:Number=ball1.y-ball0.y;
33 var dist:Number=Math.sqrt(dx*dx+dy*dy);
34 var minDist:Number=ball0.radius+ball1.radius;
35 if (dist<minDist) {
36 /*
37 var angle:Number=Math.atan2(dy,dx);
38 var tx:Number=ball0.x+Math.cos(angle)*minDist;
39 var ty:Number=ball0.y+Math.sin(angle)*minDist;
40 */
41 var tx:Number=ball0.x + (dx/dist)*minDist;
42 var ty:Number=ball0.y + (dy/dist)*minDist;
43 var ax:Number = (tx - ball1.x) * spring;
44 var ay:Number = (ty - ball1.y) * spring;
45 ball0.vx-=ax;
46 ball0.vy-=ay;
47 ball1.vx+=ax;
48 ball1.vy+=ay;
49 }
50 }
51 }
52 for (i = 0; i < numBalls; i++) {
53 var ball:Ball=balls[i];
54 move(ball);
55 }
56 }
57 private function move(ball:Ball):void {
58 ball.vy+=gravity;
59 ball.x+=ball.vx;
60 ball.y+=ball.vy;
61 if (ball.x+ball.radius>stage.stageWidth) {
62 ball.x=stage.stageWidth-ball.radius;
63 ball.vx*=bounce;
64 } else if (ball.x - ball.radius < 0) {
65 ball.x=ball.radius;
66 ball.vx*=bounce;
67 }
68 if (ball.y+ball.radius>stage.stageHeight) {
69 ball.y=stage.stageHeight-ball.radius;
70 ball.vy*=bounce;
71 } else if (ball.y - ball.radius < 0) {
72 ball.y=ball.radius;
73 ball.vy*=bounce;
74 }
75 }
76 }
77 }
浙公网安备 33010602011771号