Pixel-Perfect Android для обнаружения столкновений - PullRequest
13 голосов
/ 06 мая 2011

Хорошо, так что я работаю над игрой на Android.Мне нужно реализовать идеальное обнаружение столкновений пикселей.У меня уже есть ограничивающие рамки вокруг каждого из изображений, каждый ограничивающий прямоугольник преобразуется в соответствии с текущим поворотом изображения.Это все прекрасно работает.У меня также есть данные пикселей из каждого растрового изображения, хранящиеся в массиве.Может ли кто-нибудь помочь мне найти наиболее эффективный способ обнаружения совпадения пикселей?Заранее спасибо за любую помощь!

Ответы [ 4 ]

14 голосов
/ 15 сентября 2011

Я основал свой код на примере Mayra и выполнил обработку битовых пикселейЯ надеюсь, что это поможет.

public class CollisionUtil {
    public static boolean isCollisionDetected(Sprite sprite1, Sprite sprite2){
        Rect bounds1 = sprite1.getBounds();
        Rect bounds2 = sprite2.getBounds();

        if( Rect.intersects(bounds1, bounds2) ){
            Rect collisionBounds = getCollisionBounds(bounds1, bounds2);
            for (int i = collisionBounds.left; i < collisionBounds.right; i++) {
                for (int j = collisionBounds.top; j < collisionBounds.bottom; j++) {
                    int sprite1Pixel = getBitmapPixel(sprite1, i, j);
                    int sprite2Pixel = getBitmapPixel(sprite2, i, j); 
                    if( isFilled(sprite1Pixel) && isFilled(sprite2Pixel)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private static int getBitmapPixel(Sprite sprite, int i, int j) {
        return sprite.getBitmap().getPixel(i-(int)sprite.getX(), j-(int)sprite.getY());
    }

    private static Rect getCollisionBounds(Rect rect1, Rect rect2) {
        int left = (int) Math.max(rect1.left, rect2.left);
        int top = (int) Math.max(rect1.top, rect2.top);
        int right = (int) Math.min(rect1.right, rect2.right);
        int bottom = (int) Math.min(rect1.bottom, rect2.bottom);
        return new Rect(left, top, right, bottom);
    }

    private static boolean isFilled(int pixel) {
        return pixel != Color.TRANSPARENT;
    }
}
14 голосов
/ 06 мая 2011

Основная идея состоит в том, чтобы создать битовую маску для каждого объекта, где вы указываете в каждом пикселе, есть ли объект на самом деле там или нет.Затем вы сравниваете каждый пиксель битовых масок для двух объектов.

Вы можете минимизировать количество пикселей, которое необходимо проверить, рассчитав прямоугольную область, в которой перекрываются два ограничивающих прямоугольника.Вам необходимо проверить пиксели в этой области.

Переберите все эти пиксели и проверьте, заполнен ли пиксель обоими объектами.Если какой-либо из них, то у вас есть столкновение.

Если ваши прямоугольники выровнены по оси x / y, чтобы найти перекрытие, найдите левый, правый, верхний и нижний участки перекрытия.Это выглядело бы примерно так (я мог бы испортить крайние случаи, не пробовал это):

int left = max(obj1.left, obj2.left)
int right = min(obj1.right, obj2.right)
int top = min(obj1.top, obj2.top)
int bottom = max(obj1.bottom, obj2.bottom)

for (int x = left; x < right; x++) {
  for (int y = top; y < bottom; y++) {
     if (obj1.isFilled(x,y) && obj2.isFilled(x,y)) {
        return true;
     }
  }
}
6 голосов
/ 26 ноября 2013

Я изменил код arcones, поэтому метод работает с растровыми изображениями вместо спрайтов.

import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;


public class KollisionsErkennung {

/**
 * @param bitmap1 First bitmap
 * @param x1 x-position of bitmap1 on screen.
 * @param y1 y-position of bitmap1 on screen.
 * @param bitmap2 Second bitmap.
 * @param x2 x-position of bitmap2 on screen.
 * @param y2 y-position of bitmap2 on screen.
 */
public static boolean isCollisionDetected(Bitmap bitmap1, int x1, int y1,
        Bitmap bitmap2, int x2, int y2) {

    Rect bounds1 = new Rect(x1, y1, x1+bitmap1.getWidth(), y1+bitmap1.getHeight());
    Rect bounds2 = new Rect(x2, y2, x2+bitmap2.getWidth(), y2+bitmap2.getHeight());

    if (Rect.intersects(bounds1, bounds2)) {
        Rect collisionBounds = getCollisionBounds(bounds1, bounds2);
        for (int i = collisionBounds.left; i < collisionBounds.right; i++) {
            for (int j = collisionBounds.top; j < collisionBounds.bottom; j++) {
                int bitmap1Pixel = bitmap1.getPixel(i-x1, j-y1);
                int bitmap2Pixel = bitmap2.getPixel(i-x2, j-y2);
                if (isFilled(bitmap1Pixel) && isFilled(bitmap2Pixel)) {
                    return true;
                }
            }
        }
    }
    return false;
}

private static Rect getCollisionBounds(Rect rect1, Rect rect2) {
    int left = (int) Math.max(rect1.left, rect2.left);
    int top = (int) Math.max(rect1.top, rect2.top);
    int right = (int) Math.min(rect1.right, rect2.right);
    int bottom = (int) Math.min(rect1.bottom, rect2.bottom);
    return new Rect(left, top, right, bottom);
}

private static boolean isFilled(int pixel) {
    return pixel != Color.TRANSPARENT;
}
}

Для моих нужд это работает достаточно быстро.

2 голосов
/ 29 мая 2014

Если кому-то из вас это интересно, я хотел бы поделиться кодом, который я написал:

Важно знать, что Sprite.getWidth () и Sprite.getHeight () просто возвращают ширину / высоту растрового изображения, которое содержит Sprite. Вы можете легко настроить код под свои нужды, должно быть довольно легко понять, как работает код:)

public static boolean touchesSprite(Sprite s1, Sprite s2) {

    Bitmap b1 = s1.getBmp();
    Bitmap b2 = s2.getBmp();

    int xshift = s2.getX()-s1.getX();
    int yshift = s2.getY()-s1.getY();

    //Test if the Sprites overlap at all
    if((xshift > 0 && xshift > s1.getWidth()) || (xshift < 0 && -xshift > s2.getWidth())) {
        return false;
    }

    if((yshift > 0 && yshift > s1.getHeight()) || (yshift < 0 && -yshift > s2.getHeight())) {
        return false;
    }

    //if they overlap, find out in which regions they do
    int leftx, rightx, topy, bottomy;
    int leftx2, topy2;

    if(xshift >= 0) {
        leftx = xshift;
        leftx2 = 0;

        rightx = Math.min(s1.getWidth(), s2.getWidth()+xshift);
    } else {
        rightx = Math.min(s1.getWidth(), s2.getWidth()+xshift);

        leftx = 0;
        leftx2 = -xshift;
    }

    if(yshift >= 0) {
        topy = yshift;
        topy2 = 0;

        bottomy = Math.min(s1.getHeight(), s2.getHeight()+yshift);
    } else {
        bottomy = Math.min(s1.getHeight(), s2.getHeight()+yshift);

        topy = 0;
        topy2 = -yshift;
    }

    //then compare the overlapping regions,
    //if in any spot both pixels are not transparent, return true

    int ys = bottomy-topy;
    int xs = rightx-leftx;

    for(int x=0; x<xs; x++) {
        for(int y=0; y<ys; y++) {
            int pxl = b1.getPixel(leftx+x, topy+y);
            int pxl2 = b2.getPixel(leftx2+x, topy2+y);

            if(!((pxl & 0xff000000) == 0x0) && !((pxl2 & 0xff000000) == 0x0)) {
                return true;
            }
        }
    }

    return false;
}
...