复杂形状的碰撞检测
Posted
技术标签:
【中文标题】复杂形状的碰撞检测【英文标题】:Collision detection with complex shapes 【发布时间】:2021-12-18 23:33:29 【问题描述】:我想做一个游戏,每个关卡都从图像加载。 我想在 Photoshop 中绘制整个关卡,然后将其设置为背景并允许玩家走过它。 我想要另一个不可见的图像越过顶部,它在我想要碰撞的所有地方都是黑色的。
我不想使用矩形碰撞等更容易的瓷砖的原因是因为会有复杂的角并且并非所有东西都是矩形。
这是一个好主意,是否可以轻松做到? 这会占用大量 CPU,还是有更好的方法来做到这一点?
关卡图片
障碍物以红色显示
【问题讨论】:
“会有复杂的角,并不是所有的东西都是矩形的。”我认为这可以通过绘制和处理Shape
和Area
实例来实现。
您实际上可以同时进行:近似(廉价)初始矩形碰撞检查以快速排除大多数候选对象,如果前一个成功,则进行更精确的后续碰撞检查。
查看html5 canvas
不要忘记添加@TheodorosChatzigiannakis(或其他人)以通知他们有新评论。顺便说一句,你在回复谁? 3 个不同的人提出了 3 种不同的方法。为了“支持”我的方法,我添加了一个 SSCCE。
@TheodorosChatzigiannakis 忘了提到你概述的方法是一个很好的优化。也许它可能是/应该是一个答案。或者至少是其中的一部分。我从来没有真正在接受的答案中加载这种方法,其中包含 100 多个形状,或者许多缩放和转换的大型形状..
【参考方案1】:
..会有复杂的角落,并不是所有的东西都是矩形的。
这可以通过绘制和处理Shape
和Area
实例来实现。例如
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
class ShapeCollision
private BufferedImage img;
private Area[] obstacles = new Area[4];
private Area walls;
int x;
int y;
int xDelta = 3;
int yDelta = 2;
/** A method to determine if two instances of Area intersect */
public boolean doAreasCollide(Area area1, Area area2)
boolean collide = false;
Area collide1 = new Area(area1);
collide1.subtract(area2);
if (!collide1.equals(area1))
collide = true;
Area collide2 = new Area(area2);
collide2.subtract(area1);
if (!collide2.equals(area2))
collide = true;
return collide;
ShapeCollision()
int w = 400;
int h = 200;
img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
final JLabel imageLabel = new JLabel(new ImageIcon(img));
x = w/2;
y = h/2;
//circle
obstacles[0] = new Area(new Ellipse2D.Double(40, 40, 30, 30));
int[] xTriangle = 330,360,345;
int[] yTriangle = 60,60,40;
//triangle
obstacles[1] = new Area(new Polygon(xTriangle, yTriangle, 3));
int[] xDiamond = 60,80,60,40;
int[] yDiamond = 120,140,160,140;
//diamond
obstacles[2] = new Area(new Polygon(xDiamond, yDiamond, 4));
int[] xOther = 360,340,360,340;
int[] yOther = 130,110,170,150;
// other
obstacles[3] = new Area(new Polygon(xOther, yOther, 4));
walls = new Area(new Rectangle(0,0,w,h));
ActionListener animate = new ActionListener()
@Override
public void actionPerformed(ActionEvent e)
animate();
imageLabel.repaint();
;
Timer timer = new Timer(50, animate);
timer.start();
JOptionPane.showMessageDialog(null, imageLabel);
timer.stop();
public void animate()
Graphics2D g = img.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLUE);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
x+=xDelta;
y+=yDelta;
int s = 15;
Area player = new Area(new Ellipse2D.Double(x, y, s, s));
// Acid test of edge collision;
if (doAreasCollide(player,walls))
if ( x+s>img.getWidth() || x<0 )
xDelta *= -1;
if(y+s>img.getHeight() || y<0 )
yDelta *= -1;
g.setColor(Color.ORANGE);
for (Area obstacle : obstacles)
if (doAreasCollide(obstacle, player))
g.setColor(Color.RED);
else
g.setColor(Color.GREEN);
g.fill(obstacle);
g.setColor(Color.YELLOW);
g.fill(player);
g.dispose();
public static void main(String[] args)
Runnable r = new Runnable()
@Override
public void run()
new ShapeCollision();
;
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
编辑
让它检测所有红色并将其设置为碰撞边界
在启动时,使用Smoothing a jagged path 问题中看到的源来获取红色像素的轮廓(请参阅getOutline(Color target, BufferedImage bi)
方法)。将 Area
存储为启动时的唯一障碍。
【讨论】:
+1 这是迄今为止我最喜欢的碰撞检测答案。很好的例子,安德鲁谢谢。以上是关于复杂形状的碰撞检测的主要内容,如果未能解决你的问题,请参考以下文章