我正在用Java创建游戏,我正在使用BufferedImages在屏幕上显示项目。这些图像的边框上有透明像素,因此对象不是正方形而是唯一的形状。另外,对象可以在屏幕上旋转。如何检测两个图像之间的碰撞?
我的第一个想法是生成每个图像中像素的所有位置的列表,然后检测两个图像是否共享像素的任何位置。但是,这种方法似乎效率低下,我不知道如何生成这些列表。你有什么想法吗?
答案 0 :(得分:9)
基本方法是首先确定是否存在图像边界的碰撞。
如果两个图像重叠,则需要确定图像中是否有任何非透明像素重叠/碰撞。
这可以通过使用BufferedImage#getRGB
作为example,请查看答案中的第二个示例
更新了示例
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;
}
}
}