获取像素完美的BitmapData碰撞的上、下、最右和最左点 [英] Get the upper, bottom, rightmost and leftmost point of a pixel-perfect BitmapData collision

查看:31
本文介绍了获取像素完美的BitmapData碰撞的上、下、最右和最左点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何获得像素完美的 BitmapData 碰撞的上、下、最右和最左点?这是我的碰撞检测代码:

How can I get the upper, bottom, rightmost and leftmost point of a pixel-perfect BitmapData collision? This is my collision-detection code:

public static function checkCollision(object1:*, object2:*, debug:Boolean = false):Boolean{
    var object1Rect:Rectangle = object1.getRect(stage);
    var object2Rect:Rectangle = object2.getRect(stage);
    var object1Point:Point = new Point(object1Rect.x, object1Rect.y);
    var object2Point:Point = new Point(object2Rect.x, object2Rect.y);

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

    var clr:ColorTransform = new ColorTransform();
    if(debug)
        clr.color = 0x00ff00;

    bitmapData1.draw(object1, new Matrix(1, 0, 0, 1, -object1Rect.x, -object1Rect.y), clr);
    bitmapData2.draw(object2, null, clr);

    if(debug){
        if(bmp1.stage)
            stage.removeChild(bmp1);
        bmp1 = new Bitmap(bitmapData1);
        bmp1.x = object1Point.x;
        bmp1.y = object1Point.y;
        stage.addChild(bmp1);

        if(bmp2.stage)
            stage.removeChild(bmp2);
        bmp2 = new Bitmap(bitmapData2);
        bmp2.x = object2Point.x;
        bmp2.y = object2Point.y;
        stage.addChild(bmp2);
    }

    var bCollide:Boolean = bitmapData1.hitTest(
        object1Point,
        255,
        bitmapData2,
        object2Point,
        255
    );

    if(!debug){
        bitmapData1.dispose();
        bitmapData2.dispose();
    }
        return bCollide;
}

它工作得很好.但是,我用来检测最高命中点的代码无法正常工作.这是代码:

And it works perfeclty fine. However, the code I use to detect the top hitpoint doesn't work properly. This is the code:

public static function getHitPoint(object1:*, object2:*):Point{
    var point:Point = new Point();

    var object1Rect:Rectangle = object1.getRect(stage);
    var object2Rect:Rectangle = object2.getRect(stage);
    var object1Point:Point = new Point(object1Rect.x, object1Rect.y);
    var object2Point:Point = new Point(object2Rect.x, object2Rect.y);

    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, new Matrix(1, 0, 0, 1, -object1Rect.x, -object1Rect.y));
    bitmapData2.draw(object2);

    var bitmap1:Bitmap = new Bitmap(bitmapData1);
    var bitmap2:Bitmap = new Bitmap(bitmapData2);

    bitmap1.x = object1Point.x;
    bitmap1.y = object1Point.y;
    bitmap2.x = object2Point.x;
    bitmap2.y = object2Point.y;

    var bitmapOrigin:Point = new Point(object1Point.x, object1Point.y);
    var bitmap2OriginLocal:Point = bitmap2.globalToLocal(bitmapOrigin);

    var overlappingPixels:Vector.<uint> = bitmap2.bitmapData.getVector(
        new Rectangle(bitmap2OriginLocal.x, bitmap2OriginLocal.y, object1Rect.width, object1Rect.height)
    );

    for(var i:String in overlappingPixels){
        var index:uint = uint(i);
        if(overlappingPixels[i] != 0){
            point.x = (index % object1.width) + (bitmap2.x > bitmap1.x ? bitmap2.x : bitmap1.x);
            point.y = (uint(index / bitmap1.height)) + (bitmap2.y > bitmap1.y ? bitmap2.y : bitmap1.y);
            break;
        }
    }

    return point;
}

我不知道为什么,但 getHitPoint() 函数有时会返回错误的坐标.谁能解释一下这是为什么?以及如何检测最底部、最左侧和最右侧的生命值?

I've got no idea why, but the getHitPoint() function sometimes returns the wrong coordinates. Can anyone please explain why that is? And how can I detect the bottommost, the leftmost and the rightmost hitpoint?

编辑
我现在知道为什么 getHitPoint() 有时会返回错误的值:point.y = (uint(index/bitmap1.height)) + (bitmap2.y > bitmap1.y ? bitmap2.y : bitmap1.y); 应该是 point.y = (uint(index/bitmap1.width)) + (bitmap2.y > bitmap1.y ? bitmap2.y : bitmap1.y);

Edit
I now know why getHitPoint() sometimes returned a wrong value: point.y = (uint(index / bitmap1.height)) + (bitmap2.y > bitmap1.y ? bitmap2.y : bitmap1.y); should be point.y = (uint(index/bitmap1.width)) + (bitmap2.y > bitmap1.y ? bitmap2.y : bitmap1.y);

编辑 2
我发现了如何获得底部生命值:

Edit 2
I found out how to get the bottom hitpoint:

public static function getHitPoint(object1:*, object2:*, direction:int = 0):*{
    var point:Point = new Point();

    var object1Rect:Rectangle = object1.getRect(stage);
    var object2Rect:Rectangle = object2.getRect(stage);
    var object1Point:Point = new Point(object1Rect.x, object1Rect.y);
    var object2Point:Point = new Point(object2Rect.x, object2Rect.y);

    var bitmapData1:BitmapData = new BitmapData(
        Math.round(object1Rect.width), 
        Math.round(object1Rect.height), 
        true, 
        0
    );
    var bitmapData2:BitmapData = new BitmapData(
        Math.round(object2Rect.width), 
        Math.round(object2Rect.height), 
        true, 
        0
    );

    bitmapData1.draw(object1, new Matrix(1, 0, 0, 1, -object1Rect.x, -object1Rect.y));
    bitmapData2.draw(object2);

    var bitmap1:Bitmap = new Bitmap(bitmapData1);
    var bitmap2:Bitmap = new Bitmap(bitmapData2);

    bitmap1.x = object1Point.x;
    bitmap1.y = object1Point.y;
    bitmap2.x = object2Point.x;
    bitmap2.y = object2Point.y;

    var bitmapOrigin:Point = new Point(object1Point.x, object1Point.y);
    var bitmap2OriginLocal:Point = bitmap2.globalToLocal(bitmapOrigin);

    var overlappingPixels:Vector.<uint> = bitmap2.bitmapData.getVector(
        new Rectangle(bitmap2OriginLocal.x, bitmap2OriginLocal.y, object1Rect.width, object1Rect.height)
    );

    switch(direction){
        case 0: //top
            for(var i:String in overlappingPixels){
                var index:uint = uint(i);
                if(overlappingPixels[i] != 0){
                    point.x = (index % bitmap1.width) + (bitmap2.x > bitmap1.x ? bitmap2.x : bitmap1.x);
                    point.y = (uint((index)/bitmap1.width)) + (bitmap2.y > bitmap1.y ? bitmap2.y : bitmap1.y);
                    return point;
                }
            }

        case 1: //right
            // I still need this

        case 2: //bottom
            overlappingPixels.reverse();

            for(var i:String in overlappingPixels){
                var index:uint = uint(i);
                if(overlappingPixels[i] != 0){
                    point.x = bitmap1.width - (index % bitmap1.width) + (bitmap2.x > bitmap1.x ? bitmap2.x : bitmap1.x);
                    point.y =  (bitmap2.y + bitmap2.height > bitmap1.y + bitmap1.height ? bitmap1.y + bitmap1.height : bitmap2.y + bitmap2.height) - (uint(index/bitmap1.width));
                    return point;
                }
            }

        case 3: //left
            // I still need this too
    }
    return false;
}

我仍然需要一种方法来获得最左边和最右边的生命值

I still need a way to get the left and rightmost hitpoints though

推荐答案

你不需要像在那里那样做.您可以在单个函数中完成所有操作,该函数将正确返回所有内容.我在下面添加了评论.请注意我所做的更改,因为当您像现在一样尝试这样做时,使用您更改的代码,这是不可能的.

You don't need to do it like you're doing there. You can do it all within a single function, which returns everything back correctly. I've added comments to the below. Please take note of what I've changed, as when you're trying to do it as you're doing now, with the code you changed, it is impossible.

这适用于任何形状、任何方向.它将为您提供碰撞的准确 X 和 Y.

This works for any shape, any direction. It'll give you the exact X and Y of the collision.

不要把它变成一个静态函数.把它放到一个全局类中,改为使用单例来管理它.当您使用静态函数并引用舞台时,事情开始变得非常糟糕.

Please do not make this into a static function. Put it into a global class and use a Singleton to manage it instead. Things start to go very badly wrong when you being using static functions and reference the stage.

此外,如果您要使用小于 1(即 99.75)的像素值,则下面的内容将需要进行一些调整以适应这种情况.鉴于您的 Math.round 用法,我假设您使用的是整个像素.

Also, if you're going to be working with pixel values of less than 1 (ie 99.75), the below will need a bit of adapting to cater for that. I've assumed you're using whole pixels, given your Math.round usage.

     /**
     * 
     * @param   object1
     * @param   object2
     * @return
     */
    private function getHitPoint(object1:*, object2:*):*{
        var point:Point;

        // X and Y where we hit
        // do NOT change this to a stage location or it does NOT work
        var object1Point:Point = new Point(object1.x, object1.y);
        var object2Point:Point = new Point(object2.x, object2.y);

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

        // Draw
        bitmapData1.draw(object1, new Matrix(1, 0, 0, 1, -object1.x, -object1.y));
        bitmapData2.draw(object2);

        // Create BMP's
        var bitmap1:Bitmap = new Bitmap(bitmapData1);
        var bitmap2:Bitmap = new Bitmap(bitmapData2);

        // Set X and Y and BMP
        bitmap1.x = object1Point.x;
        bitmap1.y = object1Point.y;
        bitmap2.x = object2Point.x;
        bitmap2.y = object2Point.y;

        // BMP origin is the object1 X and Y
        var bitmapOrigin:Point = new Point(object1Point.x, object1Point.y);
        // Create a local version of the bitmap2 so we can see what is overlapping
        var bitmap2OriginLocal:Point = bitmap2.globalToLocal(bitmapOrigin);

        // Create a rectangle from what we now know
        var rect:Rectangle = new Rectangle(bitmap2OriginLocal.x, bitmap2OriginLocal.y, object1.width, object1.height);

        // The overlapping pixels are within the rectangle, so get them all
        var overlappingPixels:Vector.<uint> = bitmap2.bitmapData.getVector(rect);

        // Run through all the overlapping pixels until we find a colourful one
        for (var i:uint = 0; i < overlappingPixels.length; i++ ) {
            var index:uint = overlappingPixels[i];

            // If the colour is not 0, we have found it
            if(index != 0){
                point = new Point();

                // Basically, instead of using width and getting 100, we're working out how
                // many pixles across the overlap is. The Vector doesn't tell us this, so we need to work it out
                var overlappingWidth:uint = object1.width - Math.abs(bitmap2OriginLocal.x);

                // The Y is object1.y, minus the local y, plus object1's width minus the X from the local
                point.y = object1.y - bitmap2OriginLocal.y + uint(i / overlappingWidth);
                // The X is the same as above, but % of the width
                point.x = object1.x - bitmap2OriginLocal.x + (i % overlappingWidth);

                // Found it, we're done
                break;
            }

        }

        // Only fires when you've got a collision that is less than 1 pixel from the width or height
        // Just a fail safe
        if (!point) {
            point = new Point(object1.width, object1.height);
        }

        return point;
    }

就上下文而言,我的整个课程都在下面,展示了我如何使用此功能.你可以复制/粘贴这个类,它会起作用.它显示了您如何在屏幕上移动精灵,一旦发现碰撞,就会确定碰撞发生的位置.

For context, my entire class is below which shows how I was using this function. You can copy/paste this class and it will work. It shows how you move sprites around the screen, once it finds a collision, then it works out where the collision took place.

该类用于绝对像素完美碰撞检测,包括示例.

This class is for absolute pixel perfect collision detection, including an example.

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;
    import flash.geom.Matrix;

    /**
     * ...
     * @author KM
     */
    public class TestCases2 extends Sprite
    {

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

        /**
         * 
         */
        public function TestCases2() 
        {
            addEventListener(Event.ADDED_TO_STAGE, init);
        }

        public function init(e:Event):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);

            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 = 225;
            rect.y = 75;

            // Position
            circle.x = 225;
            circle.y = 225;

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

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

            // 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)) {
                var ref:Point = getHitPoint(rect, circle);

                // Draws where the collision hit
                var loc:Sprite = new Sprite();
                loc.graphics.beginFill(0x000000);
                loc.graphics.drawRect(0, 0, 10, 10);
                loc.graphics.endFill();
                addChild(loc);

                loc.x = ref.x;
                loc.y = ref.y;

                trace(ref);

                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;
        }

        /**
         * 
         * @param   object1
         * @param   object2
         * @return
         */
        private function getHitPoint(object1:*, object2:*):*{
            var point:Point;

            // X and Y where we hit
            // do NOT change this to a stage location or it does NOT work
            var object1Point:Point = new Point(object1.x, object1.y);
            var object2Point:Point = new Point(object2.x, object2.y);

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

            // Draw
            bitmapData1.draw(object1, new Matrix(1, 0, 0, 1, -object1.x, -object1.y));
            bitmapData2.draw(object2);

            // Create BMP's
            var bitmap1:Bitmap = new Bitmap(bitmapData1);
            var bitmap2:Bitmap = new Bitmap(bitmapData2);

            // Set X and Y and BMP
            bitmap1.x = object1Point.x;
            bitmap1.y = object1Point.y;
            bitmap2.x = object2Point.x;
            bitmap2.y = object2Point.y;

            // BMP origin is the object1 X and Y
            var bitmapOrigin:Point = new Point(object1Point.x, object1Point.y);
            // Create a local version of the bitmap2 so we can see what is overlapping
            var bitmap2OriginLocal:Point = bitmap2.globalToLocal(bitmapOrigin);

            // Create a rectangle from what we now know
            var rect:Rectangle = new Rectangle(bitmap2OriginLocal.x, bitmap2OriginLocal.y, object1.width, object1.height);

            // The overlapping pixels are within the rectangle, so get them all
            var overlappingPixels:Vector.<uint> = bitmap2.bitmapData.getVector(rect);

            // Run through all the overlapping pixels until we find a colourful one
            for (var i:uint = 0; i < overlappingPixels.length; i++ ) {
                var index:uint = overlappingPixels[i];

                // If the colour is not 0, we have found it
                if(index != 0){
                    point = new Point();

                    // Basically, instead of using width and getting 100, we're working out how
                    // many pixles across the overlap is. The Vector doesn't tell us this, so we need to work it out
                    var overlappingWidth:uint = object1.width - Math.abs(bitmap2OriginLocal.x);

                    // The Y is object1.y, minus the local y, plus object1's width minus the X from the local
                    point.y = object1.y - bitmap2OriginLocal.y + uint(i / overlappingWidth);
                    // The X is the same as above, but % of the width
                    point.x = object1.x - bitmap2OriginLocal.x + (i % overlappingWidth);

                    // Found it, we're done
                    break;
                }

            }

            // Only fires when you've got a collision that is less than 1 pixel from the width or height
            // Just a fail safe
            if (!point) {
                point = new Point(object1.width, object1.height);
            }

            return point;
        }

    }

}

这篇关于获取像素完美的BitmapData碰撞的上、下、最右和最左点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆