Problem bei pixelgenauer Kollisionsabfrage

JVTH

Mitglied
Hi Leute,
Ich habe ein Problem bei der pixelgenauen Kollisionsabfrage. Ich sitze schon seit ewigkeiten an diesem Problem.

Folgender Code:

Java:
public class CollisionHelper 
{
	
	/**
	 * returns the if the two entitys(represented by x, y and the image) are colliding
	 * @param x1
	 * @param y1
	 * @param img1
	 * @param x2
	 * @param y2
	 * @param img2
	 * @return
	 */
	public static boolean getAreColliding(int x1, int y1, int w1, int h1, BufferedImage img1, int x2, int y2, int w2, int h2, BufferedImage img2)
	{
		BufferedImage img11 = new BufferedImage(w1, h1, img1.getType());
		BufferedImage img22 = new BufferedImage(w2, h2, img2.getType());
		
		Graphics g1 = img11.getGraphics();
		g1.drawImage(img1, w1, h1, null);
		
		Graphics g2 = img22.getGraphics();
		g2.drawImage(img2, w2, h2, null);
		
		Rectangle r1 = new Rectangle(x1, y1, w1, h1);
		Rectangle r2 = new Rectangle(x2, y2, w2, h2);
		
		//check if both rectangles intersects
		if(r1.intersects(r2))
		{
			for(int x=0; x<w1; x++)
			{
				for(int y=0; y<h1; y++)//running through pixels of img 1
				{
					if(isOpaque(img11.getRGB(x, y)))//if pixel at x/y on img 1 isn`t transparent
					{
						if(isOpaque(img22.getRGB(x, y)))
						{
							return true;
						}
					}
				}
			}
		}
		return false;
	}
	
	protected static boolean isOpaque(int rgb) 
	{
		int alpha = (rgb >> 24) & 0xff;  
	    //red   = (rgb >> 16) & 0xff;  
	    //green = (rgb >>  8) & 0xff;  
	    //blue  = (rgb ) & 0xff;  
	    
	    if(alpha==0)
	    {
	      return false;
	    }
	    return true;
	}
	protected static boolean isOpaque2(int rgb) 
	{
		Color c = new Color(rgb, true);
		System.out.println(c.getAlpha());
		return c.getAlpha()!=0;
	}
	
}



Das Programm gibt mir bei isOpaque() immer false zurück.
Die Bilder erstelle ich selber mit folgendem Code:

Java:
@Override
public void loadImage() 
{
	img = new BufferedImage(30, 30, BufferedImage.TYPE_INT_ARGB);
	Graphics g = img.createGraphics();
		
	g.setColor(Color.black);
	g.fillOval(0, 0, 30, 30);
}


Das eine Bild ist 10*10 groß und das andere ist 100*100 groß.
 
Zuletzt bearbeitet von einem Moderator:
Java:
protected static boolean isOpaque(int rgb)
{ 
    System.out.println("Checking "+Integer.toHexString(rgb));

    int alpha = (rgb >> 24) & 0xff; 

    System.out.println("Alpha is "+Integer.toHexString(alpha));
...
Was wird denn da so ausgegeben? (Den rest des Codes schaue ich mir höchstens an, wenn du die Code-Tags korrigierst...)
 
Ach, bin ich doof!
@Marco13 Die exakte Ausgabe lautet dauerhaft :
Checking 0
Alpha is 0

Hier noch einmal richtig:
Java:
/**
	 * returns the if the two entitys(represented by x, y and the image) are colliding
	 * @param x1
	 * @param y1
	 * @param img1
	 * @param x2
	 * @param y2
	 * @param img2
	 * @return
	 */
	public static boolean getAreColliding(int x1, int y1, int w1, int h1, BufferedImage img1, int x2, int y2, int w2, int h2, BufferedImage img2)
	{
		BufferedImage img11 = new BufferedImage(w1, h1, img1.getType());
		BufferedImage img22 = new BufferedImage(w2, h2, img2.getType());
		
		Graphics g1 = img11.getGraphics();
		g1.drawImage(img1, w1, h1, null);
		
		Graphics g2 = img22.getGraphics();
		g2.drawImage(img2, w2, h2, null);
		
		Rectangle r1 = new Rectangle(x1, y1, w1, h1);
		Rectangle r2 = new Rectangle(x2, y2, w2, h2);
		
		//check if both rectangles intersects
		if(r1.intersects(r2))
		{
			for(int x=0; x<w1; x++)
			{
				for(int y=0; y<h1; y++)//running through pixels of img 1
				{
					if(isOpaque(img11.getRGB(x, y)))//if pixel at x/y on img 1 isn`t transparent
					{
						if(isOpaque(img22.getRGB(x, y)))
						{
							return true;
						}
					}
				}
			}
		}
		return false;
	}
	
	protected static boolean isOpaque(int rgb) 
	{
		int alpha = (rgb >> 24) & 0xff;  
	    //red   = (rgb >> 16) & 0xff;  
	    //green = (rgb >>  8) & 0xff;  
	    //blue  = (rgb ) & 0xff;  
	    
	    if(alpha==0)
	    {
	      return false;
	    }
	    return true;
	}

und die erstellung der Bilder

Java:
/**
	 * returns the if the two entitys(represented by x, y and the image) are colliding
	 * @param x1
	 * @param y1
	 * @param img1
	 * @param x2
	 * @param y2
	 * @param img2
	 * @return
	 */
	public static boolean getAreColliding(int x1, int y1, int w1, int h1, BufferedImage img1, int x2, int y2, int w2, int h2, BufferedImage img2)
	{
		BufferedImage img11 = new BufferedImage(w1, h1, img1.getType());
		BufferedImage img22 = new BufferedImage(w2, h2, img2.getType());
		
		Graphics g1 = img11.getGraphics();
		g1.drawImage(img1, w1, h1, null);
		
		Graphics g2 = img22.getGraphics();
		g2.drawImage(img2, w2, h2, null);
		
		Rectangle r1 = new Rectangle(x1, y1, w1, h1);
		Rectangle r2 = new Rectangle(x2, y2, w2, h2);
		
		//check if both rectangles intersects
		if(r1.intersects(r2))
		{
			for(int x=0; x<w1; x++)
			{
				for(int y=0; y<h1; y++)//running through pixels of img 1
				{
					if(isOpaque(img11.getRGB(x, y)))//if pixel at x/y on img 1 isn`t transparent
					{
						if(isOpaque(img22.getRGB(x, y)))
						{
							return true;
						}
					}
				}
			}
		}
		return false;
	}
	
	protected static boolean isOpaque(int rgb) 
	{
		int alpha = (rgb >> 24) & 0xff;  
	    //red   = (rgb >> 16) & 0xff;  
	    //green = (rgb >>  8) & 0xff;  
	    //blue  = (rgb ) & 0xff;  
	    
	    if(alpha==0)
	    {
	      return false;
	    }
	    return true;
	}
 
Bastel' das ggf. mal zu einem KSKB (Kleines, selbstständig compilierbares Beispiel) zusammen.

Sowas wie
Java:
        Graphics g1 = img11.getGraphics();
        g1.drawImage(img1, w1, h1, null);
        g1.dispose();

        hackilyCrappilyShowJustForTesting(img11);
...


private static void hackilyCrappilyShowJustForTesting(Image image)
{
    JFrame f = new JFrame();
    f.getContentPane().add(new JLabel(new ImageIcon(image)));
    f.setSize(300,300);
    f.setVisible(true);
}
würde das Bild anzeigen, ... es kann ja nicht so schwer sein, .... gerade bei einem 10x10-Bild wäre es ja noch überschaubar, sich alle Pixelfarben auf den Konsole auszugeben. Wenn er nach
Java:
BufferedImage img11 = new BufferedImage(w1, h1, img1.getType());
BufferedImage img22 = new BufferedImage(w2, h2, img2.getType());

Graphics g1 = img11.getGraphics();
g1.drawImage(img1, w1, h1, null);
g1.dispose();
        
Graphics g2 = img22.getGraphics();
g2.drawImage(img2, w2, h2, null);
g2.dispose();

// TEST
img11.setRGB(0,0, 0xFFFFFFFF);
img22.setRGB(0,0, 0xFFFFFFFF);
in isOpaque nicht mal was anderes ausgibt, ist dein Computer kaputt 😉
 
Das Problem liegt wohl beim neuerstellen der Bilder. Mit deinem Test ist nur der erste Pixel nicht transparent. Ohne den Test sind alle Transparent. Gezeichnet wird folglich auch nichts. Das img11 lässt sich hingegen einwandfrei zeichen. Der Fehler liegt bei dem Zeichnen des Bildes auf das andere Bild. Wenn ich
Code:
g1.fillRect(0,0,10,10);
mache, dann sind alle Pixel schwarz.
Die Ausgabe lautet bei der Zeichnung des Bildes lautet:

IsOpaque: false 0/0
IsOpaque: false 0/1
IsOpaque: false 0/2
...

Java:
public class Test 
{
	static BufferedImage img11;
	static BufferedImage img1;

	public static void main(String[] args) 
	{
		Test.erstelleImg1();
		Test.erstelleImg11AusImg1();
		Test.hackilyCrappilyShowJustForTesting(img11);
	}
	
	private static void hackilyCrappilyShowJustForTesting(Image image)
	{
	    ...
	}
		
	private static void erstelleImg1() 
	{
                //Bild aus der loadImage() Methode
		img1 = new BufferedImage(30, 30, BufferedImage.TYPE_INT_ARGB);
		Graphics g = img1.createGraphics();
		
		g.setColor(Color.black);
		g.fillOval(0, 0, 30, 30);
	}
	private static void erstelleImg11AusImg1()
	{
		img11 = new BufferedImage(10, 10, img1.getType());
		Graphics g1 = img11.getGraphics();
		g1.drawImage(img1, 10, 10, null);
		g1.dispose();
		        
		// TEST
		//img11.setRGB(0,0, 0xFFFFFFFF);
		
		for(int x=0; x<10; x++)
		{
			for(int y=0; y<10; y++)
			{
				System.out.println("IsOpaque: "+Test.isOpaque(img11.getRGB(x, y))+" "+x+"/"+y);
			}
		}
	}
	
	protected static boolean isOpaque(int rgb) 
	{
		...
	}

EDIT:

Eureka!! Ich habe es. Ich habe mir nochmal die API der drawImage(Image, int, int, ImageObserver) Methode angeguckt und die beiden ints sind nicht die Groesse des Bildes, sondern die x/y Koordinaten, jetzt geht es.
Trotzdem danke!
 
Zuletzt bearbeitet:
die beiden ints sind nicht die Groesse des Bildes, sondern die x/y Koordinaten

Oh je, gelesen hatte ich es, aber nicht so weit nachvollzogen dass es bis ins Großhirn vorgedrungen wäre 😳

Mir hat aber ohnehin nicht ganz eingeleuchtet, warum du dort neue Bilder erstellst. Das könnte schnell zum Performance-Killer werden. Du könntest/solltest aber überlegen, ob das so wirklich das geschickteste ist.

FALLS ich das richtig interpretiere: Es gibt große Bilder, wo immer mehrere "Sprites" drauf zusammengefasst sind, und in dieser Methode werden aus zwei solcher großer Bilder jeweils (durch die x/y/w/h) EIN Sprite "rausgepickt" und mit dem anderen auf Kollisionen geprüft.

Die Methode bekommt ja eigentlich alle relevanten Informationen übergeben, die man braucht, um das ganze auch ohne neu erstellte Bilder lösen zu können. Sowas wie das hier (ungetestet!!!) könnte doch gehen...?

Java:
package javaforum;

import java.awt.Rectangle;
import java.awt.image.BufferedImage;

public class PixelCollision
{
    public static boolean getAreColliding(
        int x1, int y1, int w1, int h1, BufferedImage img1, 
        int x2, int y2, int w2, int h2, BufferedImage img2)
    {
        Rectangle r1 = new Rectangle(x1, y1, w1, h1);
        Rectangle r2 = new Rectangle(x2, y2, w2, h2);
        
        if(r1.intersects(r2))
        {
            int w = Math.min(w1, w2);
            int h = Math.min(h1, h2);

            for(int x=0; x<w; x++)
            {
                for(int y=0; y<h; y++)
                {
                    int rgb1 = img1.getRGB(x1+x, y1+y);
                    if(isOpaque(rgb1))
                    {
                        int rgb2 = img2.getRGB(x2+x, y2+y);
                        if(isOpaque(rgb2))
                        {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }
    
    protected static boolean isOpaque(int rgb) 
    {
        int alpha = (rgb >> 24) & 0xff;  
        return alpha != 0;
    }
}

(Auch wenn mir wiederum die Sache mit den Koordinaten nicht ganz einleuchtet - müßte da nicht noch irgendwo die Information verwurstet werden, die besagt, an welcher Stelle sich der Sprite (auf dem Bildschirm!) befindet...?!)
 
Hi,
Du hast recht mit dem "Performance-Killer", aber der Grund wieso ich die Groesse mitgebe ist ein anderer. Jedes der Bilder ist ein einzelner Entity/Sprite, welcher aus seinem Bild besteht. Die Groesse habe ich mitgegeben, da die Groesse eines Entitys nicht durch die Groesse des Bildes bestimmt werden soll. Aber du hast schon Recht, vielleicht wäre es besser das Bild nur bei der Erstellung des Entitys zu skalieren. Der Grundgedanke war allerdings, das halt die Groesse des Entitys durch 2 ints(width/height) dargestellt wird.
 
Hmmmjaaa... so weit ja OK, aber ... Warum soll der Sprite eine andere Größe haben als das Bild?
Java:
class Sprite
{
    private int width = 100;
    private int height = 100;
    private BufferedImage image = new BufferedImage(123, 56, ...); // ???
]
(also, das kann man ja aus irgendeinem Grund "wollen", es erscheint mir aber auf den ersten Blick seltsam... ???:L )
 
Du hast recht, ich habe es geändert, und ich überpruefe jetzt nur noch die Überschneidungsfläche Hier einmal die fertige Klasse CollisionHelper:
Java:
public class CollisionHelper 
{
	
	/**
	 * returns the if the two entitys(represented by x, y and the image) are colliding
	 * @param x1
	 * @param y1
	 * @param img1
	 * @param x2
	 * @param y2
	 * @param img2
	 * @return
	 */
	public static boolean getAreColliding(int x1, int y1, BufferedImage img1, int x2, int y2, BufferedImage img2)
	{
		int xx1=x1-img1.getWidth()/2;
		int yy1=y1-img1.getHeight()/2;
		int xx2=x2-img2.getWidth()/2;
		int yy2=y2-img2.getHeight()/2;
		
		//are the rectangles intersecting
		if(xx1 + img1.getWidth() >= xx2 || xx2 + img2.getWidth() >= xx1 + img1.getWidth() || yy1 + img1.getHeight() >= yy2 || yy2 + img2.getHeight() >= xx1)
		{
			Rectangle r = CollisionHelper.getSubRect(img1, xx1, yy1, img2, xx2, yy2);
			
			for(int x = 0; x < r.width; x++)
			{
				for(int y = 0; y < r.height; y++)
				{
					if(isOpaque(img1.getRGB(r.x + x - xx1, r.y + y - yy1)) && isOpaque(img2.getRGB(r.x + x - xx2, r.y + y - yy2)))
					{
						return true;
					}
				}
			}
		}
		return false;
	}
	
	protected static boolean isOpaque(int rgb) 
	{
		//System.out.println("Checking "+Integer.toHexString(rgb));
		
		int alpha = (rgb >> 24) & 0xff;  
	    //red   = (rgb >> 16) & 0xff;  
	    //green = (rgb >>  8) & 0xff;  
	    //blue  = (rgb ) & 0xff;  
		
		//System.out.println("Alpha is "+Integer.toHexString(alpha));
	    
	    if(alpha==0)
	    {
	      return false;
	    }
	    return true;
	}
	
	protected static Rectangle getSubRect(BufferedImage img1, int x1, int y1, BufferedImage img2, int x2, int y2) 
	{
		//getting rightest x
		int startX=0;
		//is x2 more right than x1
		if(x1<x2)
		{
			startX=x2;
		}
		else
		{
			startX=x1;
		}
		
		//getting downst y
		int startY=0;
		//is y1 deeper than y2
		if(y1>y2)
		{
			startY=y1;
		}
		else
		{
			startY=y2;
		}
		
		//getting endX(width)
		int endX=0;
		int endX1=x1+img1.getWidth();
		int endX2=x2+img2.getWidth();
		//is endX1 the leftest
		if(endX1<endX2)
		{
			endX=endX1;
		}
		else
		{
			endX=endX2;
		}
		
		//getting endY
		int endY=0;
		int endY1=y1+img1.getHeight();
		int endY2=y2+img2.getHeight();
		//is endY1 the highest
		if(endY1<endY2)
		{
			endY=endY1;
		}
		else
		{
			endY=endY2;
		}
		
		//create Rectangle:
		Rectangle r = new Rectangle(startX, startY, endX-startX, endY-startY);
		
		return r;
	}
 

Zurück
Oben