• 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(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
    }

  • 相关阅读:
    nest exception is java.sql.SQLException:ORA-01476:除数为0
    java.lang.NullPointerException: No FileItemFactory has been set.
    With as 必须跟select
    作为一项技艺的管理——Leo鉴书81
    faultString = "java.lang.NullPointerException : null"
    org.apache.commons.fileupload.FileUploadBase$InvalidContentTypeException
    PERL
    Error:Error #2174
    ArgumentError:Error #2004:某个参数无效
    SecurityError:Error:#2148
  • 原文地址:https://www.cnblogs.com/happysky97/p/1884576.html
Copyright © 2020-2023  润新知