【问题标题】:Pixel-Perfect Collision Detection Android像素完美碰撞检测 Android
【发布时间】:2011-08-20 08:54:14
【问题描述】:

好的,我正在开发一款 Android 游戏。我需要实现像素完美碰撞检测。我已经在每个图像周围设置了边界框,每个边界框都经过转换以匹配图像的当前旋转。这一切都很好。我还将每个位图的像素数据存储在一个数组中。有人可以帮我找出检测像素是否重叠的最有效方法吗?提前感谢您的帮助!

【问题讨论】:

  • 这并不是一个特定于 android 或 java 的问题。这更像是一个一般的游戏编程问题。也许 gamedev.stackexhange.com 是问这个问题的更好地方?

标签: java android bitmap collision-detection


【解决方案1】:

基本思想是为每个对象创建一个位掩码,您在每个像素中指示该对象是否实际存在。然后比较两个对象的位掩码的每个像素。

您可以通过计算两个边界框重叠的矩形区域来最小化需要检查的像素数。该区域内的像素是您需要检查的。

遍历所有这些像素,并检查像素是否填充在两个对象中。如果其中任何一个是,那么你有一个碰撞。

如果您的矩形与 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;
     }
  }
}

【讨论】:

  • 好的,谢谢你的解释。我也在考虑同样的思路,我认为计算边界框重叠的区域肯定会有所帮助。有什么计算技巧吗哈哈?还要感谢您指出位掩码的想法。我将在加载图像时创建这些数组,这样可以加快速度,而不必检查每个索引中是否有彩色像素。
  • 太棒了!太感谢了!这完全有道理,我不敢相信我没想到哈哈......我也可以在很多其他情况下使用它。
  • 感谢您发布此 Mayra,非常有帮助。我很想在离线时挑选你的大脑,如果有兴趣,请给我发一封电子邮件,在我的个人资料中找到。
  • 不应该是int top = max(obj1.top, obj2.top)吗?您正在从左侧迭代 x,这是最大值。这同样适用于顶部,反之亦然。
【解决方案2】:

我的代码基于 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;
    }
}

【讨论】:

  • 感谢分享!如果有人看到这篇文章,最好有一个很好的代码示例供他们查看。我不了解你,但我觉得很难找到人们为 Android 或 Java 进行像素碰撞检测的例子。我认为这将是一个受欢迎的主题......
【解决方案3】:

我更改了 arcones 的代码,因此该方法适用于位图而不是 Sprite。

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

根据我的需要,它运行得足够快。

【讨论】:

    【解决方案4】:

    如果你们有兴趣,我想分享我写的代码:

    您需要知道的重要一点是 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-22
      • 1970-01-01
      • 2013-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      相关资源
      最近更新 更多