Java中两个图像之间的碰撞检测

我在写作的游戏中显示了两个角色,即玩家和敌人。 定义如下:

public void player(Graphics g) { g.drawImage(plimg, x, y, this); } public void enemy(Graphics g) { g.drawImage(enemy, 200, 200, this); } 

然后叫:

 player(g); enemy(g); 

我可以用键盘移动播放器(),但在尝试检测两者之间的碰撞时我感到很茫然。 很多人都说使用矩形,但作为一个初学者,我看不出如何将它链接到我现有的代码中。 谁能为我提供一些建议?

我认为你的问题是你没有为你的玩家和敌人使用优秀的OO设计。 创建两个类:

 public class Player { int X; int Y; int Width; int Height; // Getters and Setters } public class Enemy { int X; int Y; int Width; int Height; // Getters and Setters } 

您的播放器应具有X,Y,Width和Height变量。

你的敌人也应该如此。

在你的游戏循环中,做这样的事情(C#):

 foreach (Enemy e in EnemyCollection) { Rectangle r = new Rectangle(eX,eY,e.Width,e.Height); Rectangle p = new Rectangle(player.X,player.Y,player.Width,player.Height); // Assuming there is an intersect method, otherwise just handcompare the values if (r.Intersects(p)) { // A Collision! // we know which enemy (e), so we can call e.DoCollision(); e.DoCollision(); } } 

为了加快速度,不要费心检查敌人的坐标是否在屏幕外。

首先,使用Jonathan Holland所描述的边界框来查找是否有碰撞。

从(多色)精灵,创建黑色和白色版本。 如果你的精灵是透明的,你可能已经有了这些(即有些地方在边界框内,但你仍然可以看到背景)。 这些是“面具”。

在蒙版上使用Image.getRGB()来获取像素。 对于每个不透明的像素,在整数数组中设置一个位(下面是playerArrayenemyArray )。 如果width <= 32像素,则数组的大小为height否则width <= 32 (width+31)/32*height 。 以下代码适用于width <= 32

如果边界框发生碰撞,请执行以下操作:

 // Find the first line where the two sprites might overlap int linePlayer, lineEnemy; if (player.y <= enemy.y) { linePlayer = enemy.y - player.y; lineEnemy = 0; } else { linePlayer = 0; lineEnemy = player.y - enemy.y; } int line = Math.max(linePlayer, lineEnemy); // Get the shift between the two x = player.x - enemy.x; int maxLines = Math.max(player.height, enemy.height); for ( line < maxLines; line ++) { // if width > 32, then you need a second loop here long playerMask = playerArray[linePlayer]; long enemyMask = enemyArray[lineEnemy]; // Reproduce the shift between the two sprites if (x < 0) playerMask << (-x); else enemyMask << x; // If the two masks have common bits, binary AND will return != 0 if ((playerMask & enemyMask) != 0) { // Contact! } } 

链接: JGame , 小型Java游戏框架

您不希望在绘制代码中包含碰撞检查代码。 这幅画需要很快。 碰撞可以进入游戏循环。 因此,您需要独立于其精灵的对象的内部表示。

这是我的碰撞检测程序的主要类。
您可以在以下url查看: http : //www.youtube.com/watch?v = JIXhCvXgjsQ

 /** * * @author Tyler Griffin */ import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.awt.GraphicsDevice.*; import java.util.ArrayList; import java.awt.Graphics; import java.awt.geom.Line2D; public class collision extends JFrame implements KeyListener, MouseMotionListener, MouseListener { ArrayList everything=new ArrayList(); int time=0, x, y, width, height, up=0, down=0, left=0, right=0, mouse1=0, mouse2=0; int mouseX, mouseY; GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice screen = environment.getDefaultScreenDevice(); DisplayMode displayMode = screen.getDisplayMode(); //private BufferStrategy strategy; JLayeredPane pane = new JLayeredPane(); tile Tile; circle Circle; rectangle Rectangle; textPane text; public collision() { setUndecorated(screen.isFullScreenSupported()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); setLayout(null); setResizable(false); screen.setFullScreenWindow(this); width=displayMode.getWidth(); height=displayMode.getHeight(); Circle=new circle(-(int)Math.round((double)height/7*2),-(int)Math.round((double)height/7*2),(int)Math.round((double)height/7*.85),this); Rectangle=new rectangle(-(int)Math.round((double)height/7*1.5),-(int)Math.round((double)height/7*1.5),(int)Math.round((double)height/7*1.5),(int)Math.round((double)height/7*1.5),this); Tile=Circle; Tile.move(mouseX-Tile.width/2, mouseY-Tile.height/2); text=new textPane(0,0,width,height,this); everything.add(new circle((int)Math.round((double)width/100*75),(int)Math.round((double)height/100*15),(int)Math.round((double)width/100*10),this)); everything.add(new rectangle((int)Math.round((double)width/100*70),(int)Math.round((double)height/100*60),(int)Math.round((double)width/100*20),(int)Math.round((double)height/100*20),this)); //everything.add(new line(750,250,750,750,this)); /*everything.add(new line(width/700*419,height/700*68,width/700*495,height/700*345,this)); everything.add(new line(width/700*495,height/700*345,width/700*749,height/700*350,this)); everything.add(new line(width/700*749,height/700*350,width/700*549,height/700*519,this)); everything.add(new line(width/700*549,height/700*519,width/700*624,height/700*800,this)); everything.add(new line(width/700*624,height/700*800,width/700*419,height/700*638,this)); everything.add(new line(width/700*419,height/700*638,width/700*203,height/700*800,this)); everything.add(new line(width/700*203,height/700*800,width/700*279,height/700*519,this)); everything.add(new line(width/700*279,height/700*519,width/700*76,height/700*350,this)); everything.add(new line(width/700*76,height/700*350,width/700*333,height/700*345,this)); everything.add(new line(width/700*333,height/700*345,width/700*419,height/700*68,this)); everything.add(new line(width/950*419,height/700*68,width/950*624,height/700*800,this)); everything.add(new line(width/950*419,height/700*68,width/950*203,height/700*800,this)); everything.add(new line(width/950*76,height/700*350,width/950*624,height/700*800,this)); everything.add(new line(width/950*203,height/700*800,width/950*749,height/700*350,this)); everything.add(new rectangle(width/950*76,height/700*350,width/950*673,1,this));*/ everything.add(new line((int)Math.round((double)width/1350*419),(int)Math.round((double)height/1000*68),(int)Math.round((double)width/1350*624),(int)Math.round((double)height/1000*800),this)); everything.add(new line((int)Math.round((double)width/1350*419),(int)Math.round((double)height/1000*68),(int)Math.round((double)width/1350*203),(int)Math.round((double)height/1000*800),this)); everything.add(new line((int)Math.round((double)width/1350*76),(int)Math.round((double)height/1000*350),(int)Math.round((double)width/1350*624),(int)Math.round((double)height/1000*800),this)); everything.add(new line((int)Math.round((double)width/1350*203),(int)Math.round((double)height/1000*800),(int)Math.round((double)width/1350*749),(int)Math.round((double)height/1000*350),this)); everything.add(new rectangle((int)Math.round((double)width/1350*76),(int)Math.round((double)height/1000*350),(int)Math.round((double)width/1350*673),1,this)); addKeyListener(this); addMouseMotionListener(this); addMouseListener(this); } public void keyReleased(KeyEvent e) { Object source=e.getSource(); int released=e.getKeyCode(); if (released==KeyEvent.VK_A){left=0;} if (released==KeyEvent.VK_W){up=0;} if (released==KeyEvent.VK_D){right=0;} if (released==KeyEvent.VK_S){down=0;} }//end keyReleased public void keyPressed(KeyEvent e) { Object source=e.getSource(); int pressed=e.getKeyCode(); if (pressed==KeyEvent.VK_A){left=1;} if (pressed==KeyEvent.VK_W){up=1;} if (pressed==KeyEvent.VK_D){right=1;} if (pressed==KeyEvent.VK_S){down=1;} if (pressed==KeyEvent.VK_PAUSE&&pressed==KeyEvent.VK_P) { //if (paused==0){paused=1;} //else paused=0; } }//end keyPressed public void keyTyped(KeyEvent e){} //*********************************************************************************************** public void mouseDragged(MouseEvent e) { mouseX=(e.getX()); mouseY=(e.getY()); //run(); } public void mouseMoved(MouseEvent e) { mouseX=(e.getX()); mouseY=(e.getY()); //run(); } //*********************************************************************************************** public void mousePressed(MouseEvent e) { if(e.getX()==0 && e.getY()==0){System.exit(0);} mouseX=(e.getX()+x); mouseY=(e.getY()+y); if(Tile instanceof circle) { Circle.move(0-Circle.width, 0-Circle.height); Circle.setBounds(Circle.x, Circle.y, Circle.width, Circle.height); Tile=Rectangle; } else { Rectangle.move(0-Rectangle.width, 0-Rectangle.height); Rectangle.setBounds(Rectangle.x, Rectangle.y, Rectangle.width, Rectangle.height); Tile=Circle; } Tile.move(mouseX-Tile.width/2, mouseY-Tile.height/2); } public void mouseReleased(MouseEvent e) { //run(); } public void mouseEntered(MouseEvent e){} public void mouseExited(MouseEvent e){} public void mouseClicked(MouseEvent e){} //*********************************************************************************************** public void run()//run collision detection { while (this == this) { Tile.move(Tile.x + ((mouseX - (Tile.x + (Tile.width / 2))) / 10), Tile.y + ((mouseY - (Tile.y + (Tile.height / 2))) / 10)); //Tile.move((mouseX - Tile.width / 2), mouseY - (Tile.height / 2)); for (int i = 0; i < everything.size(); i++) { tile Temp = (tile) everything.get(i); if (Temp.x < (Tile.x + Tile.width) && (Temp.x + Temp.width) > Tile.x && Temp.y < (Tile.y + Tile.height) && (Temp.y + Temp.height) > Tile.y)//rectangles collided { if (Temp instanceof rectangle) { if (Tile instanceof rectangle){rectangleRectangle(Temp);} else {circleRectangle(Temp);}//Tile instanceof circle } else { if (Temp instanceof circle) { if (Tile instanceof rectangle) {rectangleCircle(Temp);} else {circleCircle(Temp);} } else//line { if (Tile instanceof rectangle){rectangleLine(Temp);} else{circleLine(Temp);} } } }//end if }//end for try {Thread.sleep(16L);} catch (Exception e) {} Tile.setBounds(Tile.x, Tile.y, Tile.width, Tile.height); //Rectangle.setBounds(x, y, width, height); //Circle.setBounds(x, y, width, height); repaint(); text.out=" "; }//end while loop }//end run //***************************************special collision detection/handling functions************************************************ void rectangleRectangle(tile Temp) { int lapTop, lapBot, lapLeft, lapRight, small, scootX=0, scootY=0; lapTop=(Temp.y+Temp.height)-Tile.y; lapBot=(Tile.y+Tile.height)-Temp.y; lapLeft=(Temp.x+Temp.width)-Tile.x; lapRight=(Tile.x+Tile.width)-Temp.x; small=999999999; if (lapTop=Temp.x)||(Tile.y+Tile.height/2>=Temp.y && Tile.y+Tile.height/2<=Temp.y+Temp.height)) { rectangleRectangle(Temp); } else//push from nearest corner { int x,y; if(Tile.x+Tile.width/2>Temp.x+Temp.width && Tile.y+Tile.height/2Temp.x+Temp.width && Tile.y+Tile.height/2>Temp.y+Temp.height){x=Temp.x+Temp.width; y=Temp.y+Temp.height;} else {x=Temp.x; y=Temp.y+Temp.height;} double distance = Math.sqrt(Math.pow(Tile.x+(Tile.width/2) - x, 2) + Math.pow(Tile.y+(Tile.height/2) - y, 2)); if((int)Math.round(distance)=Tile.x)||(Temp.y+Temp.height/2>=Tile.y && Temp.y+Temp.height/2<=Tile.y+Tile.height)) { rectangleRectangle(Temp); } else//push from nearest corner { int x,y; if(Temp.x+Temp.width/2>Tile.x+Tile.width && Temp.y+Temp.height/2Tile.x+Tile.width && Temp.y+Temp.height/2>Tile.y+Tile.height){x=Tile.x+Tile.width; y=Tile.y+Tile.height;} else {x=Tile.x; y=Tile.y+Tile.height;} double distance = Math.sqrt(Math.pow(Temp.x+(Temp.width/2) - x, 2) + Math.pow(Temp.y+(Temp.height/2) - y, 2)); if((int)Math.round(distance)Tile.x+Tile.width && Temp.y+Temp.height/2Tile.x+Tile.width && Temp.y+Temp.height/2>Tile.y+Tile.height){Tile.move((Temp.x+Temp.width/2)-(int)Math.round(normX*((Temp.width/2)))-Tile.width,(Temp.y+Temp.height/2)-(int)Math.round(normY*((Temp.height/2)))-Tile.height);text.out="collision detected!";} else {Tile.move((Temp.x+Temp.width/2)-(int)Math.round(normX*((Temp.width/2))),(Temp.y+Temp.height/2)-(int)Math.round(normY*((Temp.height/2)))-Tile.height);text.out="collision detected!";} } } } void circleCircle(tile Temp) { double distance = Math.sqrt(Math.pow((Tile.x+(Tile.width/2)) - (Temp.x+(Temp.width/2)),2) + Math.pow((Tile.y+(Tile.height/2)) - (Temp.y+(Temp.height/2)), 2)); if((int)distance<(Tile.width/2+Temp.width/2)) { double normX = ((Tile.x+(Tile.width/2)) - (Temp.x+(Temp.width/2))) / distance; double normY = ((Tile.y+(Tile.height/2)) - (Temp.y+(Temp.height/2))) / distance; Tile.move((Temp.x+(Temp.width/2))+(int)Math.round(normX*(Tile.width/2+Temp.width/2))-(Tile.width/2) , (Temp.y+(Temp.height/2))+(int)Math.round(normY*(Tile.height/2+Temp.height/2))-(Tile.height/2));text.out="collision detected!"; } } void circleLine(tile Temp) { line Line=(line)Temp; if (Line.x1 < (Tile.x + Tile.width) && (Line.x1) > Tile.x && Line.y1 < (Tile.y + Tile.height) && Line.y1 > Tile.y)//circle may be hitting one of the end points { rectangle rec=new rectangle(Line.x1, Line.y1, 1, 1, this); circleRectangle(rec); remove(rec); } if (Line.x2 < (Tile.x + Tile.width) && (Line.x2) > Tile.x && Line.y2 < (Tile.y + Tile.height) && Line.y2 > Tile.y)//circle may be hitting one of the end points { rectangle rec=new rectangle(Line.x2, Line.y2, 1, 1, this); circleRectangle(rec); remove(rec); } int x1=0, y1=0, x2=Tile.x+(Tile.width/2), y2=Tile.y+(Tile.height/2); x1=Tile.x+(Tile.width/2)-Line.height;//(int)Math.round(Line.xNorm*1000); x2=Tile.x+(Tile.width/2)+Line.height; if(Line.posSlope) { y1=Tile.y+(Tile.height/2)-Line.width; y2=Tile.y+(Tile.height/2)+Line.width; } else { y1=Tile.y+(Tile.height/2)+Line.width; y2=Tile.y+(Tile.height/2)-Line.width; } Point point=intersection((double)x1,(double)y1,(double)x2,(double)y2,(double)Line.x1,(double)Line.y1,(double)Line.x2,(double)Line.y2);//find intersection if (point.x < (Line.x + Line.width) && point.x > Line.x && point.y < (Line.y + Line.height) && point.y > Line.y)//line intersects within line segment { //if(point!=null){System.out.println(point.x+","+point.y);} double distance = Math.sqrt(Math.pow((Tile.x+(Tile.width/2)) - point.x,2) + Math.pow((Tile.y+(Tile.width/2)) - point.y, 2)); if((int)distance Tile.x && Line.y1 < (Tile.y + Tile.height) && Line.y1 > Tile.y)//circle may be hitting one of the end points { rectangle rec=new rectangle(Line.x1, Line.y1, 1, 1, this); rectangleRectangle(rec); remove(rec); } if (Line.x2 < (Tile.x + Tile.width) && (Line.x2) > Tile.x && Line.y2 < (Tile.y + Tile.height) && Line.y2 > Tile.y)//circle may be hitting one of the end points { rectangle rec=new rectangle(Line.x2, Line.y2, 1, 1, this); rectangleRectangle(rec); remove(rec); } if(Line.posSlope)//positive sloped line { //first we'll do the top left corner int x1=Tile.x-Line.height; int x2=Tile.x+Line.height; int y1=Tile.y-Line.width; int y2=Tile.y+Line.width; Point topPoint=new Point(-99,-99), botPoint=new Point(-99,-99); double topDistance=0, botDistance=0; topPoint=intersection((double)x1,(double)y1,(double)x2,(double)y2,(double)Line.x1,(double)Line.y1,(double)Line.x2,(double)Line.y2);//find intersection topDistance = Math.sqrt(Math.pow(Tile.x - topPoint.x,2) + Math.pow(Tile.y - topPoint.y, 2)); //new let's do the bottom right corner x1=Tile.x+Tile.width-Line.height; x2=Tile.x+Tile.width+Line.height; y1=Tile.y+Tile.height-Line.width; y2=Tile.y+Tile.height+Line.width; botPoint=intersection((double)x1,(double)y1,(double)x2,(double)y2,(double)Line.x1,(double)Line.y1,(double)Line.x2,(double)Line.y2);//find intersection botDistance = Math.sqrt(Math.pow((Tile.x+Tile.width) - botPoint.x,2) + Math.pow((Tile.y+Tile.height) - botPoint.y, 2)); if(topDistance 

由于Java没有交叉函数(真的!?),您可以通过简单地对可能发生碰撞的每个对象的边界框(矩形)的X和Y,宽度和高度值进行碰撞来进行碰撞检测。

所以…在每个碰撞对象的基础对象中…即如果你的玩家和敌人有一个共同的基础,你可以放一个简单的Rectangle对象,叫做BoundingBox。 如果公共库是内置的Java类,那么您将需要创建一个类来扩展类中的构建,并让玩家和敌人对象扩展您的新类或者是该类的实例。

在创建(以及每个刻度或更新)时,您需要为玩家和敌人设置BoundingBox参数。 我没有在我面前的Rectangle类,但它最有可能是X,Y,Width和最终高度。 X和Y是游戏世界中的对象位置。 我认为宽度和高度是不言自明的。 尽管如此,他们很可能会从球员位置的右侧出来,如果X和Y在0处并且你的宽度和高度都是256,你就不会看到任何东西,因为角色会在左上角在屏幕之外。

无论如何……为了检测碰撞,你需要比较玩家和敌人的BoundingBoxes的属性。 所以像这样……

  if( Player.BoundingBox.X = Enemy.BoundingBox.X && If( Player.BoundingBox.Y = Enemy.BoundingBox.Y ) { //Oh noes! The enemy and player are on top of eachother. } 

逻辑可能会变得复杂,但您需要比较每个BoundingBox之间的距离并比较位置。

这是一个有用的开源游戏,它使用了很多冲突: http : //robocode.sourceforge.net/

您可以查看代码并与此处编写的答案互补。

有问题:

 Rectangle box1 = new Rectangle(100,100,100,100); Rectangle box2 = new Rectangle(200,200,100,100); // what this means is if any pixel in box2 enters (hits) box1 if (box1.contains(box2)) { // collision occurred } // your code for moving the boxes 

这也适用于圈子:

 Ellipse2D.Double ball1 = new Ellipse2D.Double(100,100,200,200); Ellipse2D.Double ball2 = new Ellipse2D.Double(400,100,200,200); // what this means is if any pixel on the circumference in ball2 touches (hits) // ball1 if (ball1.contains(ball2)) { // collision occurred } // your code for moving the balls 

要检查是否已触及屏幕边缘,您可以使用以下内容:

 Rectangle screenBounds = jpanel.getBounds(); Ellipse2D.Double ball = new Ellipse2D.Double(100,100,200,200); // diameter 200 Rectangle ballBounds = ball.getBounds(); if (!screenBounds.contains(ballBounds)) { // the ball touched the edge of the screen } 

使用矩形围绕每个玩家和敌人,矩形的高度和宽度应该与你周围的物体相对应,想象它只在一个大到足以容纳它的盒子里。

现在,您移动这些矩形的方式与对象相同,因此它们具有“边界框”

我不确定Java是否有这个,但它可能在矩形对象上有一个名为.intersects()的方法,所以你要做(rectangle1.intersectS(rectangle2)来检查一个对象是否与另一个对象发生碰撞)。

否则你可以得到方框的x和y坐标,并使用它们的高度/宽度来检测它们是否已经相交。

无论如何,您可以使用它来在交叉点上做一个事件(使一个爆炸,或其他)或阻止移动被绘制。 (回到以前的坐标)

编辑:我们走了

布尔

intersects(Rectangle r)确定此Rectangle和指定的Rectangle是否相交。

所以我会这样做(并且不要粘贴这段代码,它很可能不会工作,很长一段时间没用java,而且当我使用它时我没有做图形。)

 Rectangle rect1 = new Rectangle(player.x, player.y, player.width, player.height); Rectangle rect2 = new Rectangle(enemy.x, enemy.y, enemy.width, enemy.height); //detects when the two rectangles hit if(rect1.intersects(rect2)) { System.out.println("game over, g"); } 

显然你需要在某个地方适应它。

无需使用矩形…不断比较2个玩家的坐标。

喜欢if(x1===x&&y1==y) 记得在比较时增加x的范围。

如果你的矩形宽度是30, if (x1>x&&x2>x+30) .. likewise y