如何检测两个图像是否在Java中发生冲突?

时间:2014-04-28 03:37:18

标签: java

我正在用Java创建游戏,我正在使用BufferedImages在屏幕上显示项目。这些图像的边框上有透明像素,因此对象不是正方形而是唯一的形状。另外,对象可以在屏幕上旋转。如何检测两个图像之间的碰撞?

我的第一个想法是生成每个图像中像素的所有位置的列表,然后检测两个图像是否共享像素的任何位置。但是,这种方法似乎效率低下,我不知道如何生成这些列表。你有什么想法吗?

1 个答案:

答案 0 :(得分:9)

基本方法是首先确定是否存在图像边界的碰撞。

如果两个图像重叠,则需要确定图像中是否有任何非透明像素重叠/碰撞。

这可以通过使用BufferedImage#getRGB

来实现

作为example,请查看答案中的第二个示例

更新了示例

Collision

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TransparentImageCollision {

    public static void main(String[] args) {
        new TransparentImageCollision();
    }

    public TransparentImageCollision() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private BufferedImage fly;
        private BufferedImage spider;

        private Rectangle spiderBounds;
        private Rectangle flyBounds;

        private Point spiderDelta;
        private Point flyDelta;

        private Rectangle collision;

        public TestPane() {
            try {
                fly = ImageIO.read(getClass().getResource("/fly.png"));
                spider = ImageIO.read(getClass().getResource("/spider.png"));

                Dimension size = getPreferredSize();
                int width = size.width;
                int height = size.height;

                spiderBounds = new Rectangle();
                spiderBounds.setSize(spider.getWidth(), spider.getHeight());
                spiderBounds.setLocation(0, (height - spider.getHeight()) / 2);

                flyBounds = new Rectangle();
                flyBounds.setSize(fly.getWidth(), fly.getHeight());
                flyBounds.setLocation(width - fly.getWidth(), (height - fly.getHeight()) / 2);

                spiderDelta = new Point(1, 0);
                flyDelta = new Point(-1, 0);

                Timer timer = new Timer(40, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        update(spiderBounds, spiderDelta);
                        update(flyBounds, flyDelta);
                        detectCollision();
                        repaint();
                    }
                });
                timer.start();
            } catch (IOException exp) {
                exp.printStackTrace();
            }
        }

        protected void update(Rectangle bounds, Point delta) {
            bounds.x += delta.x;
            bounds.y += delta.y;
            if (bounds.x < 0) {
                bounds.x = 0;
                delta.x *= -1;
            }
            if (bounds.x + bounds.width > getWidth()) {
                bounds.x = getWidth() - bounds.width;
                delta.x *= -1;
            }
            if (bounds.y < 0) {
                bounds.y = 0;
                delta.y *= -1;
            }
            if (bounds.y + bounds.height > getHeight()) {
                bounds.y = getHeight() - bounds.height;
                delta.y *= -1;
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            g2d.drawImage(spider, spiderBounds.x, spiderBounds.y, this);
            g2d.drawImage(fly, flyBounds.x, flyBounds.y, this);

            if (collision != null) {
                g2d.setColor(new Color(255, 0, 0, 128));
                g2d.fill(collision);
            }

            g2d.dispose();
        }

        /**
         * Used to detect the collision between non-alpha portions of the two
         * images
         */
        protected void detectCollision() {
            collision = null;
            // Check if the boundires intersect
            if (spiderBounds.intersects(flyBounds)) {
                // Calculate the collision overlay
                Rectangle bounds = getCollision(spiderBounds, flyBounds);
                if (!bounds.isEmpty()) {
                    // Check all the pixels in the collision overlay to determine
                    // if there are any non-alpha pixel collisions...
                    for (int x = bounds.x; x < bounds.x + bounds.width; x++) {
                        for (int y = bounds.y; y < bounds.y + bounds.height; y++) {
                            if (collision(x, y)) {
                                collision = bounds;
                                break;
                            }
                        }
                    }
                }
            }
        }

        protected Rectangle getCollision(Rectangle rect1, Rectangle rect2) {
            Area a1 = new Area(rect1);
            Area a2 = new Area(rect2);
            a1.intersect(a2);
            return a1.getBounds();
        }

        /**
         * Test if a given x/y position of the images contains transparent
         * pixels or not...
         * @param x
         * @param y
         * @return 
         */
        protected boolean collision(int x, int y) {
            boolean collision = false;

            int spiderPixel = spider.getRGB(x - spiderBounds.x, y - spiderBounds.y);
            int flyPixel = fly.getRGB(x - flyBounds.x, y - flyBounds.y);
            // 255 is completely transparent, you might consider using something
            // a little less absolute, like 225, to give you a sligtly
            // higher hit right, for example...
            if (((spiderPixel >> 24) & 0xFF) < 255 && ((flyPixel >> 24) & 0xFF) < 255) {
                collision = true;
            }
            return collision;
        }
    }
}