Flash/Flex学习笔记(41):碰撞检测

碰撞检测基本上可能分为二类:对象与对象的碰撞检测、对象与点的碰撞检测

为了方便测试,先写一个box类(生成一个小矩形)

show sourceview source

print?

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

show sourceview source

print?

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

show sourceview source

print?

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)) {

基于距离的检测:即检测二个物体的中心点距离是否低于最小距离

show sourceview source

print?

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
}

很明显,这种方法对于圆形物体是十分精确的,但对于非规则形状,只能近似检测.

如果结合上二篇提到的弹性运动,可以做出更复杂的动画:

show sourceview source

print?

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
}

原理图:

多物体基于距离的碰撞检测:

show sourceview source

print?

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
}

posted @ 2010-11-22 17:23  模西的哥哥  阅读(223)  评论(0编辑  收藏  举报