AS3中的Hittest在不规则物体和一个矩形并获得重叠区域的顶部y坐标

时间:2014-07-13 14:47:03

标签: actionscript-3 collision-detection hittest

如何在AS3中测试矩形和不规则对象之间的碰撞?那么我怎样才能获得重叠的最低y值(顶部)?

修改
非常感谢您的回答,但checkCollision方法始终返回false。这是我的代码:

public static function checkCollision(object1:*, object2:*):Boolean{
    var object1Rect:Rectangle = object1.getRect(stage);
    var object2Rect:Rectangle = object2.getRect(stage);

    var bitmapData1:BitmapData = new BitmapData(
        object1.width, 
        object1.height, 
        true, 
        0
    );
    var bitmapData2:BitmapData = new BitmapData(
        object2.width, 
        object2.height, 
        true, 
        0
    );

    bitmapData1.draw(object1);
    bitmapData2.draw(object2);

    var bCollide:Boolean = bitmapData1.hitTest(
        new Point(object1Rect.x, object1Rect.y),
        255,
        bitmapData2,
        new Point(object2Rect.x, object2Rect.y),
        255
    );

    bitmapData1.dispose();
    bitmapData2.dispose();

    return bCollide;
}

1 个答案:

答案 0 :(得分:1)

如果它是一个复杂的形状,请使用如下所示的bitmapdata。这适用于所有形状。

如果您仍然需要知道最低Y值,请不要忘记它也可能在X上。还有一个额外的函数(getHitPoint)来计算它,只需要在成功的命中测试中运行。它可以通过将原始函数中的位图数据传递到其中进行优化,但我将其保持独立以帮助它保持可读性。

package kazo 
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    /**
     * ...
     * @author KM
     */
    public class TestCases extends Sprite
    {

        private var rect    :Sprite;
        private var circle  :Sprite;

        public function TestCases() { }

        /**
         * Start the test case
         */
        public function start():void {
            trace('Starting test case');

            // Rectangle
            rect = new Sprite();
            // Circle
            circle = new Sprite();

            // Draw the rectangle. Center point must be TOP LEFT
            // If you're using Flash Professional, place everything at 0,0 inside the MC
            rect.graphics.beginFill(0xff0000);
            rect.graphics.drawRect(0, 0, 100, 100);
            rect.graphics.endFill();

            // Draw the circle. Center point is TOP LEFT, so the X and Y of the circle need to be equal to the radius
            circle.graphics.beginFill(0xffff00);
            circle.graphics.drawCircle(50, 50, 50);
            circle.graphics.endFill();

            // Add them
            addChild(rect);
            addChild(circle);

            // Position
            rect.x = 0;
            rect.y = 100;

            // Position
            circle.y = 400;
            circle.x = 0;

            // Frame loop
            addEventListener(Event.ENTER_FRAME, frameFunc);
        }

        /**
         * 
         * @param   e
         */
        private function frameFunc(e:Event):void {
            // move them around
            rect.y += 1;
            circle.y -= 1;
            circle.x += 1;
            rect.x += 1;

            // Check for collisions. If found, stop. Pass 'true' as the final param if you want it to draw to the screen
            if (checkCollision(rect, circle)) {
                trace(getHitPoint(rect, circle));

                removeEventListener(Event.ENTER_FRAME, frameFunc);
            }
        }

        /**
         * 
         * @param   _obj1
         * @param   _obj2
         * @param   _debug
         * @return
         */
        private function checkCollision(_obj1:Sprite, _obj2:Sprite, _debug:Boolean = false):Boolean {
            // Draw the first item to bitmapdata
            var bmd1:BitmapData = new BitmapData(_obj1.width, _obj1.height, true, 0);
            // ..and the second
            var bmd2:BitmapData = new BitmapData(_obj2.width, _obj2.height, true, 0);

            // Now draw them
            bmd1.draw(_obj1);
            bmd2.draw(_obj2);

            // If we're in debug, also add the bitmap to the stage so we can see where we are
            if (_debug) {
                var bmp:Bitmap = new Bitmap(bmd1);
                bmp.x = _obj1.x;
                bmp.y = _obj1.y;
                addChild(bmp);

                var bmp2:Bitmap = new Bitmap(bmd2);
                bmp2.x = _obj2.x;
                bmp2.y = _obj2.y;
                addChild(bmp2);
            }

            // Hit test including alpha channel. Obj1 X/Y, Obj2 X/Y, alpha channel
            var rtn:Boolean = bmd1.hitTest(new Point(_obj1.x, _obj1.y), 255, bmd2, new Point(_obj2.x, _obj2.y), 255);

            // Dispose the bitmap data, we dont need it anymore
            if (!_debug) {
                bmd1.dispose();
                bmd2.dispose();
            }


            // Return the boolean
            return rtn;
        }

        /**
         * This ONLY needs to be called once a collision has been confirmed. Don't run this on every frame!
         * @return
         */
        private function getHitPoint(_obj1:Sprite, _obj2:Sprite, _debug:Boolean = false):Point {
            var rtnPoint:Point = new Point();

            // Draw the first item to bitmapdata
            var bmd1:BitmapData = new BitmapData(_obj1.width, _obj1.height, true, 0);
            // ..and the second
            var bmd2:BitmapData = new BitmapData(_obj2.width, _obj2.height, true, 0);

            // Now draw them
            bmd1.draw(_obj1);
            bmd2.draw(_obj2);

            // Create two bitmaps
            var bmp1:Bitmap = new Bitmap(bmd1);
            var bmp2:Bitmap = new Bitmap(bmd2);

            // Position
            bmp1.x = _obj1.x;
            bmp2.x = _obj2.x;
            bmp1.y = _obj1.y;
            bmp2.y = _obj2.y;

            // Get the origins
            var bitmapOrigin:Point = new Point(_obj1.x, _obj1.y);
            var bitmapTwoOriginLocal:Point = bmp2.globalToLocal(bitmapOrigin);

            // Find all the overlapping pixels
            var overlappingPixels:Vector.<uint> = bmp2.bitmapData.getVector(new Rectangle(bitmapTwoOriginLocal.x, bitmapTwoOriginLocal.y, _obj1.width, _obj1.height));

            var i:uint = 0;
            var len:uint = overlappingPixels.length;

            // Run through them all until we find a non-zero value
            for(i; i < len; i++) {
                if(overlappingPixels[i] != 0) {
                    // Our X collision is i % _obj1.width plus whichever X value is highest
                    rtnPoint.x = (i % _obj1.width) + (bmp2.x > bmp1.x ? bmp2.x : bmp1.x);
                    // Our Y collision is i / _obj1.height plus whichever Y value is highest
                    rtnPoint.y = (uint((i + 1) / _obj1.width)) + (bmp2.y > bmp1.y ? bmp2.y : bmp1.y);

                    // If debug, draw the collision point
                    if (_debug) {
                        var test:Sprite = new Sprite();
                        test.graphics.beginFill(0x008c6f);
                        test.graphics.drawRect(rtnPoint.x , rtnPoint.y, 10, 10);
                        test.graphics.endFill();
                        addChild(test);
                    }

                    break;
                }
            }

            // Send it back
            return rtnPoint;
        }

    }

}