# Sidescroller mit Hindernissen (Qualxi tut)



## sophismo (23. Aug 2012)

Hi!

Ich hab mal angefangen Qualxis Tutorial, recht eigen abgeändert, nachzubauen.

Sehr sehr coole Sache - danke!!

Das funkt recht gut, man hat Beschleunigung statt Geschwindigkeit und "Luftwiderstand" etc. pp.
Ich hab auch die zum Spiel gehörigen Funktionen und Variablen in eine Klasse "Engine" ausgelagert. Genau wie eine Library für geladene Bilder (die von der Engine im Konstruktor erzeugt wird, und dort werden Bilder und fps geladen).

Jedenfalls is alles soweit gut bis zur Pixelgetreuen Kollision:

Ich lasse stille raketen erzeugen, fliege mit 1 Pixel pro Knopfdruck hin und schaue nach.
Von schräg unten klappt das manchmal, von hinten aber gar nicht: es die OpaquCollisionsCheck gibt immer aus, dass alles passt - obwohl ich knapp in der Bombe (hinten) drin bin (mit meiner Seite).
Bei etwa 2,3 Pixel überlappen kommt:

[Java]
sub_me: breite und höhe: 1.0015870000000575 10.03
sub_him: breite und höhe: 1.0015870000000575 10.0

Exception in thread "Thread-4" java.awt.image.RasterFormatException: (y + height) is outside of Raster
	at sun.awt.image.ByteInterleavedRaster.createWritableChild(ByteInterleavedRaster.java:1273)
	at java.awt.image.BufferedImage.getSubimage(BufferedImage.java:1166)
	at Sprite.notOpaqueCollision(Sprite.java:120)
	at Rocket.collidedWith(Rocket.java:102)
	at Engine.doLogic(Engine.java:53)
	at Engine.update(Engine.java:23)
	at GamePanel.run(GamePanel.java:66)
	at java.lang.Thread.run(Thread.java:680)[/code]

Es schaut für mich auch etwas komisch aus, dass da steht:
if( isOpaque(rgb1) && isOpaque(rgb2) ){return true;}
Das sagt doch, dass er aufhören soll zu suchen, wenn irgend welche Pixel alphakanäle haben?!


[Java]
	public boolean notOpaqueCollision(Sprite s){
		Rectangle2D.Double cut = (Double) this.createIntersection(s);
		System.out.println("Intersection created!");
		if ((cut.width < 1) || (cut.height < 1)){
			return false;
		}

		//Rectangle relative to images
		Rectangle2D.Double sub_me = getSubRec(this, cut);
		Rectangle2D.Double sub_him = getSubRec(s, cut);

		System.out.println("I: breite ud höhe: " + sub_me.width +" "+ sub_me.height +s.currentPic);
		System.out.println("ER:breite ud höhe: " + sub_him.width +" "+ sub_him.height);

		BufferedImage img_me = pics[currentPic].getSubimage((int)sub_me.x, (int)sub_me.y,
				(int)sub_me.width, (int)sub_me.height);
		BufferedImage img_him = pics[s.currentPic].getSubimage((int)sub_him.x, (int)sub_him.y,
				(int)sub_him.width, (int)sub_him.height);

		for (int i = 0; i < img_me.getWidth(); i++){
			for(int k = 0; k < img_him.getHeight(); k++){

				int rgb1 = img_me.getRGB(i, k);
				int rgb2 = img_him.getRGB(i, k);

				System.out.println(rgb1 + "-1 / 2- " + rgb2);
				if( isOpaque(rgb1) && isOpaque(rgb2) ){
					return true;
				}
			}
		}
		return false; //wegen return missing eingefügt...

	}
[/code]

und 
	
	
	
	





```
protected Rectangle2D.Double getSubRec 
	(Rectangle2D.Double source, Rectangle2D.Double part){
		
		Rectangle2D.Double sub = new Rectangle2D.Double();
		
		if (source.x > part.x){
			sub.x = 0;
		}else{
			sub.x = part.x - source.x;
		}
		
		if (source.y > part.y){
			sub.y = 0;
		}else{
			sub.y = part.y - source.y;
		}
		
		sub.width = part.width;
		sub.height = part.height;
		
		return sub;		
	}

	protected boolean isOpaque(int rgb){
		// Bits 24-31 = alpha, below are red, green, blue
		int alpha = (rgb >> 24) & 0xff;
		if(alpha == 0){
			return false;
		}else{ return true; }
}
```

Und die Einbindung in der Rakete:


```
@Override
	public boolean collidedWith(Sprite s) {
		if(remove){return false;}
		if(this.intersects(s) && (s instanceof Player | s instanceof Rocket)){
			System.out.println("Rakete Kollidierte!");
			
			if(!notOpaqueCollision(s)){System.out.println("noch nicht");return false;}
			
			remove = true;
			s.remove = true;

			eng.createExplosion((int) getX(),(int) getY());
			eng.createExplosion((int) s.getX(),(int) s.getY());
		
			return true;
		}else{
			return false;
		}
	}
```


----------



## Marco13 (23. Aug 2012)

Ich weiß nicht, welche Teile davon direkt aus dem Tutorial stammen, aber... würde es schon helfen, wenn du

```
System.out.println("About to take "+sub_me+" from "+pics[currentPic]);
BufferedImage img_me = pics[currentPic].getSubimage((int)sub_me.x, (int)sub_me.y,
                (int)sub_me.width, (int)sub_me.height);
```
ausgibst was da für Werte verwendet werden?


----------



## sophismo (24. Aug 2012)

Die 3 Methoden stammen komplett aus dem Tutorial! Meine Änderungen sind im Aufbau: ich habe zB eine Library für Bilder, statt Methoden im GamePanel,.. aber das funkt alles wirklich besser, als erhofft!
Einzige klitzekleine Änderung hier könnte das Einbinden in die Kollisionserkennung der Rakete sein - aber nur, weil ich nicht genau gewusst habe, wo das hingehört. (Laut tut: wenn eine "ungenaue" Kollision erkannt wird, dann prüft man noch "genau" nach -> das hab ich so verstanden, dass der Code in die colidedWith() Methode kommt, und nochmals nachprüft.)

Und die ganzen Konsolenausdrücke sind natürlich nur zum Debuggen, da ja ein Fehler aufgetreten ist. (also die sind von mir zum Test eigenfügt.)


Nun mit deinem Ausdruck eingefügt:

Hier hats funktioniert - allerdings etwas zu früh. Das war die Kollision von schräg unten (Rakete schaut nach links, Heli ist auf Bild 3 (rotor nach rechts) stillgehalten:
[Java]
About to take java.awt.geom.Rectangle2D$Double[x=26.81306400000028,y=7.010999000000012,w=3.1869359999997187,h=2.9890009999999876] from BufferedImage@50269997: type = 13 IndexColorModel: #pixelBits = 8 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@162db19d transparency = 2 transIndex   = 255 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 30 height = 10 #numDataElements 1 dataOff[0] = 0
-16777216-1 / 2- 16777215
-16744320-1 / 2- 16777215
-16744320-1 / 2- 16777215
-16744320-1 / 2- 16777215
-16777216-1 / 2- 16777215
-16744320-1 / 2- 16777215
noch nicht
Rakete Kollidierte!
Intersection created!
About to take java.awt.geom.Rectangle2D$Double[x=26.81306400000028,y=6.993508000000006,w=3.1869359999997187,h=3.0064919999999944] from BufferedImage@50269997: type = 13 IndexColorModel: #pixelBits = 8 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@162db19d transparency = 2 transIndex   = 255 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 30 height = 10 #numDataElements 1 dataOff[0] = 0
-16776961-1 / 2- 16777215
-16777216-1 / 2- 16777215
-16744320-1 / 2- 16777215
-16777216-1 / 2- 16777215
-16744320-1 / 2- 16777215
-16744320-1 / 2- 16777215
16777215-1 / 2- 16777215
-16777216-1 / 2- 16777215
-16744320-1 / 2- -16777216[/Java]

Und hier, wieder Spieler längst in der Rakete drinnen:
[Java]Rakete Kollidierte!
Intersection created!
About to take java.awt.geom.Rectangle2D$Double[x=28.992985000000402,y=0.0,w=1.0070149999995976,h=10.0] from BufferedImage@35cf7491: type = 13 IndexColorModel: #pixelBits = 8 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@46edf730 transparency = 2 transIndex   = 255 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 30 height = 10 #numDataElements 1 dataOff[0] = 0
Exception in thread "Thread-4" java.awt.image.RasterFormatException: (y + height) is outside of Raster
	at sun.awt.image.ByteInterleavedRaster.createWritableChild(ByteInterleavedRaster.java:1273)
	at java.awt.image.BufferedImage.getSubimage(BufferedImage.java:1166)
	at Sprite.notOpaqueCollision(Sprite.java:122)
	at Rocket.collidedWith(Rocket.java:102)
	at Engine.doLogic(Engine.java:53)
	at Engine.update(Engine.java:23)
	at GamePanel.run(GamePanel.java:66)
	at java.lang.Thread.run(Thread.java:680)
[/Java]

Mir sagt der Output leider nicht wirklich was... 
Danke jedenfalls schonmal für dein Interesse und Mitwirken!


----------



## Marco13 (24. Aug 2012)

Die gleiche Ausgabe sollte natürlich auch für die Erstellung des anderen subImages verwendet werden. Und vermutlich wäre es noch übersichtlicher, wenn man die Koordinaten des Rectangles direkt nach 'int' gecastet ausgeben würde. Es geht ja nur darum, zu sehen, welche Werte (y+height) haben, und welche Höhe das Bild hat, um den Fehler zurückverfolgen zu können...


----------



## sophismo (24. Aug 2012)

:lol: Tschuldige, hab dadurch, dass ich nicht genau verstanden habe, worauf du hinaus wolltest nicht bemerkt, dass ein Output sinnlos ist.

So jetzt hat sich aber gottseidank etwas getan:

Ich hab den zuvor genannten Fehler nämlich entdeckt!
Beim Erstellen des BufferedImg img_him habe ich die Farben/das aktuelle Animationsbild der Rakete ein zweites Mal benutzt, statt das Bild des Spielers zu nehmen. 1 Buchstabe ist manchmal doch sehr entscheidend - es hätte lauten müssen:
	
	
	
	





```
s.pics[s.currentPic]. ..
```

Nun der Auszug nach erfolgter Kollision (von hinten, was zuvor zum Fehler geführt hat):


```
About to take ME (Rocket): java.awt.geom.Rectangle2D$Double[x=28.990741000000014,y=0.0,w=1.009258999999986,h=10.0] from BufferedImage@30ff94b1: type = 13 IndexColorModel: #pixelBits = 8 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@5b7b0998 transparency = 2 transIndex   = 255 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 30 height = 10 #numDataElements 1 dataOff[0] = 0
About to take HIM (Player): java.awt.geom.Rectangle2D$Double[x=0.0,y=9.302811999999946,w=1.009258999999986,h=10.0] from BufferedImage@6ab30913: type = 13 IndexColorModel: #pixelBits = 8 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@5b7b0998 transparency = 2 transIndex   = 255 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 30 height = 30 #numDataElements 1 dataOff[0] = 90
```

Mein bis jetzt einzig ungelöster Fehler ist, dass eine zu große Überlappung sein muss, bevor die Kollision wirklich zugelassen wird von der opaqueCollision();

Es scheint, als würde genau der äußerste Bereich - also der Schwarze Rand nicht zählen oder so.


----------



## sophismo (24. Aug 2012)

Nehme alles zurück und behaupte glatt das Gegenteil.

Nachdem ich jetzt auf dem Bild nachgemessen habe, sehe ich erst, dass bei den ausgegebenen Koordinaten im Bild tatsächlich nur 1 Pixel steht. Also Gottseidank läuft das Ding! 
Danke nochmal für die Hilfe.

Ich habe noch eine kleine Frage, was den Aufbau eines solchen Spiels angeht. Wie erwähnt läuft bei mir am GamePanel nur die Tastaturabfrage, ein Started Boolean (der die Spielschleife am Laufen hält) , ein Vektor mit meinen Actors (der wird übergeben) und eine "Engine".

So ist es für mich einfach übersichtlicher - wahrscheinlich codemäßig nicht gerade schöner, nehme ich an..

Die Engine wiederum ruft jegliche Logik auf, bewegt die Acteure und lädt für sich wieder Bibliotheken für Sound und Bildmaterial.

Meine Frage ist jetzt die: Ich will mit dem Spiel noch weiter hinaus (mehr gegner, lvls etc.) und zum Beispiel hätt ich gern ein Menü (zweitrangig).

Am wichtigsten erstmal ist: wie mach ich am besten "Welten"? Wobei es eigentlich nur darum geht, dass diese Welt (wo im Code soll das sein?) verschiedene Gegner aufruft. Ich hab mir das so gedacht, dass immer bei Logik oder so die Zeit an das Lvl Objekt übergeben wird, und dann nach x Sekunden der und der Gegner spawnt. Aber dann weiß ich nicht genau, wie ich eine unterscheidung treffe, welche lvl jetzt gespielt wird.. Also dass ich einen Wert mit übergeben kann ja, aber soll ich dann für jede Lvl eine Klasse schreiben? ???:L

Und Später (ws. in 100 Jahren ^^) ein Menü: Wie wird das "elegant" gelöst (oder wie macht Ihr das), dass man von einem "Fenster" sag ich jetzt mal auf die SpielFläche mit dem eigentlichen Lvl darauf kommt?


Bin da sehr am Anfang - ist mein erstes grafisches Spiel und ich suche keine Bibel, sondern Vorschläge, oder Code, wies "schön" programmiert ist. Also wofür man eine Klasse aufmacht und was durchaus auch mit anderen Dingen in einen Topf geschmissen werden kann.

Danke :toll:


----------



## Marco13 (24. Aug 2012)

Für die neuen Fragen, zu den Leveln und dem eigentlichen Aufbau, wäre ein neuer Thread vielleicht nicht verkehrt - mit genauerer Fragestellung und ggf. (Pseudo) Codeschnipseln, wie du dir die Verwaltung und Abläufe vorstellst. Vielleicht sagen dann auch mehr Leute was dazu.


----------



## sophismo (24. Aug 2012)

Vorschlag angenommen, Thema abgeschlossen, danke nochmal!


----------

