# Java Game verbesserte Kollisionserkennung



## BraunBerry (29. Jul 2016)

Hallo Community,
ich melde mich nach meinem Waypointproblem nochmal zurück. In meinem Spiel habe ich auch ein terrain mit einem Spieler. Also der Spieler befindet sich in einem Raum (mehrere Ecken, nicht rechteckig). Ich habe kein Problem damit, die Kollisionskanten parallel zur X bzw. zur Y Achse zu setzen. Deshalb habe ich auch zuerst die Standardtaktik angewandt: Wenn ein X oder Y Wert überschritten wird, wird er auf das Maximum zurück gesetzt z.B.:
if(X >= 100){X = 100;}
Alles schön und gut aber dennoch frage ich mich ob es nicht eine einfachere und vorallem zeitsparendere Lösung gibt. Ich habe mich gefragt, ob es nicht möglich wäre, passend zum entsprechenden Terrain noch eine Grafik anzufertigen, in dem der Raum, in dem sich der Spieler bewegen kann beispielsweise transparent ist und der Raum in den der Spieler nicht kann bzw. bis zu den Kollisionskanten beispielsweise pink ist. Diese grafik könnte man dann unsichtbar im Spiel hinterlegen und so die Kollision abfragen. So zummindest meine Vorstellung. WICHTIG: Es sollten keine Fehler entstehen, wenn ich meine DIMENSION auf die Grafik anwende, also ein float Wert, der mit der Fenstergröße und allen Bildgrößen multipliziert wird, um eine einheitliche Größe einstellen zu können. Wenn ihr eine Idee hättet wie man soetwas verwirklichen könnte oder ob es so ein Verfahren schon mal in einem Spiel gabwürde ich mich sehr freuen. 

lg BraunBerry


----------



## BRoll (1. Aug 2016)

hast du jetzt ein Problem mit der Haltung der Kollisionsinformation oder mit dem Kollisionsverfahren?
Egal ob du jetzt die Informationen in einem Bild, einer Textdatei, irgendeinem Binärformat oder CSV Datein hinterlegst,
die Abfrage wäre immer die gleiche (zb. dein Rücksetz-Verfahren). Das mit der Grafik als Speicherort für die Kollisionsinformation ist auf jeden Fall eine gute Lösung, ich würde sagen da ist es Geschmackssache und abhängig von der Komplexität der Karten welches Verfahren am sinnvollsten ist. In einer Bilddatei könntest du theorietisch mit jeder RGB (oder RGBA) Farbe eine bestimmte Aktion interpretieren (zb. Spawnpunkte oder eben Kollisionsflächen). Wenn man aber komplexe Metadaten zusätzlich zur Kollision hat, und nicht mit mehreren Files rumhantieren will, dann lohnt es sich über ein eigenes Format nachzudenken. Deshalb könnte ich dir jetzt keine Antwort geben welches das beste Verfahren für dein Projekt wäre.


----------



## BraunBerry (1. Aug 2016)

Mein Problem ist die Rücksetztung. In einem Rechteck wäre das gar kein Problem. Allerdings in komplexerem Raum ist das etwas koplizierter. Ich habe versucht die letzte Position des Spielers (also des Terrains, weil der Spieler immer zentriert ist) nach jeder Kollisionsabfrage in einer Variable zu speichern. Dann habe ich versucht die Intersection von Rectangels (playerHitbox und der Kollisionskante Bound001) abzufragen, so dass bei
if (playerHitbox.intersects(Bound001))
Die Koordinaten wieder auf den letzten Punkt zurückgesetzt werden. Allerdings bewegt sich dann garnichts mehr und ich glaube auch nicht dass das so funktionieren wird. Obwohl ich zwar alles selber schreiben wollte, frage ich mich ib es nicht irgendeine Library gibt, die die Kollisionsabfrage und Rücksetztung vereinfacht. 

Lg BraunBerry


----------



## InfectedBytes (1. Aug 2016)

Anstatt die Position neu zu setzen und bei kollision zurückzusetzen, kannst du auch einfach die position nur ändern, wenn dies zu keiner Kollision führt.
quasi:

```
target = position + delta;
if(!kollision(target)) {
  position = target;
  // ... restliche bewegung durchführen
}
```

Um dir mehr helfen zu können, wäre es gut wenn du mehr zum Spielfeld sagst. Denn je nach Aufbau gibt es unterschiedliche Lösungsmöglichkeiten. Nutzt du eine Tilemap? Beliebige Positionen? etc.

Ansonsten würde ich dir sowieso empfehlen das ganze mit sowas wie z.B. LibGDX zu machen, das ist ein Framework für Spiele.


----------



## BRoll (1. Aug 2016)

Du redest dauernd von einem komplexen Raum, der kein Rectangle ist, überprüfst aber mit Rectangles die Intersection? Kannst du mal ein Screenshot von so einem Raum hochladen?


----------



## BraunBerry (1. Aug 2016)

Im Bild habe ich versucht zu skizzieren, wie ich mir den Raum vorstelle.

InfectedBytes Idee, einfach die Bewegung zu deaktivieren wenn eine bestimmte Koordinate erreicht ist ist eigentlich sehr gut. Ich habe das mit den boolean Variablen canMoveRight und canMoveLeft in der Player-Klasse realisiert. Das Problem hierbei ist, dass wenn ich rechts am Maximum bin, der Spieler die ganze Zeit springt und wenn ich links am Maximum bin, das Bild immer hin und her springt. Hier nochmal mein Jump void:


```
public static void Jump(){
     if (isGrounded == true){
       isGrounded = false;
       isJumping = true;
       FALLVALUE = -FALLSPEED;
     }
   }

@Override
   public void actionPerformed(ActionEvent e) {
     if (e.getSource() == FallTimer){
       if (isGrounded == false){
         if (isJumping == true){
           JUMPHEIGHT += 1;
           MainTerrain.Y -= FALLVALUE;
           if (JUMPHEIGHT >= (MAXJUMPHEIGHT - JUMPSLOWDOWNINDICATOR)){
             JUMPHEIGHT = 0;
             FALLVALUE += 1;
           }
         }
       }
     }
   }
```

und meine KeyEvents:


```
switch(e.getKeyCode()){
       case KeyEvent.VK_A: if (Player.canMoveLeft == true){
                 MainTerrain.SPEEDX = Player.WALKSPEED;
                 Player.DIRECTION = "left";
                 Player.isWalking = true;
                 Player.initAniState();
                 break;}
     
       case KeyEvent.VK_D: if (Player.canMoveRight == true){
                 MainTerrain.SPEEDX = -Player.WALKSPEED;
                 Player.DIRECTION = "right";
                 Player.isWalking = true;
                 Player.initAniState();
                 break;}
     
       case KeyEvent.VK_SPACE: if (Player.isGrounded == true){Player.Jump();}
                  break;
       }
```

falls ihr damit etwas anfangen könnt.

Ich probiere derzeit immernoch verschiede Möglichkeiten von Kollision aus.

PS: Jeder Mensch hat Stärken. Themen in Foren verständlich schreiben gehört bei mir leider nocht nicht wirklich dazu. :/

lg BraunBerry


----------



## BRoll (2. Aug 2016)

Hmm was hat das jetzt alles plötzlich mit Springen zu tun?
Wenn das Bild hin und herspringt sieht das für mich eher aus als ob das Rücksetzen
an einer Stelle noch "kaputt" im Code ist. PS: Bei dem bisschen Code was du jetzt vom Springen gezeigt hast fehlt doch noch das zurücksetzen der isGrounded und isJumping wenn der Sprung fertig ist?


----------



## BraunBerry (2. Aug 2016)

```
public static void Update(){
     System.out.println(MainTerrain.X + " , " + MainTerrain.Y + " , " + isGrounded + " , " + JUMPHEIGHT + " , " + FALLVALUE + " , " + Application.WIDTH + " , " + Application.HEIGHT);  // ***TEMP***
     System.out.println(GUI.MOUSEX + " , " + GUI.MOUSEY);
     detectCollision();
   }
 
   public static void detectCollision(){
     if (MainTerrain.activeTerrain == "Scout001"){
     
       if ((MainTerrain.X <= 96) && (MainTerrain.X >= -270)){
         if (MainTerrain.Y <= 0){MainTerrain.Y = 0; isGrounded = true;}
       }
     }
   }
```

Hier ist nochmal meine bisherige Kollisionserkennung.
Ja die Kollision am liken Rand scheint irgendwie nicht richtig zu funktionieren (wenn ich a gedrückt halte). Und wenn ich am rechten Rand d gedrückt halte wird solange ich halte immer der Jump void ausgeführt. Ich vermute dass das Programm auf die nächste Aktion ausweicht (welche der Sprung ist) wenn eine Rechtsbewegung nicht mehr möglich ist.


----------



## BRoll (2. Aug 2016)

Hmm wo wird da jetzt in detectCollision das canMoveRight/canMoveLeft gesetzt/zurückgesetzt?
Und bei isGrounded= true müsste doch noch isJumping = false gesetzt werden ,wenn ich alles richtig verstanden habe.


----------



## InfectedBytes (2. Aug 2016)

und bitte nutz die code tags:
[code=java]hier kommt der code hin[/code]


----------



## InfectedBytes (2. Aug 2016)

Außerdem werden strings mit equals verglichen und nicht mit ==


----------



## BraunBerry (2. Aug 2016)

Ich hab das jetzt mal eingefügt:


```
public static void detectCollision(){
     if (MainTerrain.activeTerrain == "Scout001"){
     
       if ((MainTerrain.X <= 96) && (MainTerrain.X >= -270)){
         if (MainTerrain.Y <= 0){MainTerrain.Y = 0; isGrounded = true;}
       }
     
       if (MainTerrain.X <= -262){
         canMoveRight = false;
         MainTerrain.X = -262;
       }else{canMoveRight = true;}
     }
   }
```

Die Variable isJumping ist im Prinzip das umgekehrte Äquivalent zu isGrounded. Die Variable existiert nur schon, da ich für später Dublejumps, etc. geplant habe. Ich habe jetzt nur die Kollision für den rechten Rand eingefügt, da hier nur das Problem mit dem Springen bei d gedrückt halten auftritt.


----------



## BRoll (3. Aug 2016)

Naja wie InfectedBytes schon gesagt hat ist die oberste Bedingung niemals true, weshalb der ganze Code ansich unerreicht ist. Dh. du dürfest gar keinen Unterschied merken wenn du dort was änderst. Änder doch mal das == zu equals bei dem String Vergleich und schau wie es dann läuft. Oder wo ist jetzt noch das Problem?


----------



## BraunBerry (3. Aug 2016)

Dankeschön. Jetzt läuft es nach ein wenig Tüftelei. Kleinere Bugs sind noch drin aber die bekomme ich schon noch raus. Danke an euch beide


----------



## BraunBerry (6. Aug 2016)

Nach gründlicher Überarbeitung kann ich noch einen Lösungsvorschlag hinterlassen: Wenn eine Bewegung nicht möglich war ist der switch auf die nächstmögliche Aktion im Code ausgewichen. Jetzt habe ich für jede Bewegungsart (Links, Rechts, Sprung) einen extra 
	
	
	
	





```
switch(e.getKeyCode()){
```
 geschrieben und die Kollision verläuft reibungslos.

Hier des Rätsels Lösung:

```
switch(e.getKeyCode()){
       case KeyEvent.VK_A: if (Player.canMoveLeft == true){
                 MainTerrain.SPEEDX = Player.WALKSPEED;
                 Player.DIRECTION = "left";
                 Player.isWalking = true;
                 Player.initAniState();
                 break;}}
       
       switch(e.getKeyCode()){
       case KeyEvent.VK_D: if (Player.canMoveRight == true){
                 MainTerrain.SPEEDX = -Player.WALKSPEED;
                 Player.DIRECTION = "right";
                 Player.isWalking = true;
                 Player.initAniState();
                 break;}}
       
       switch(e.getKeyCode()){
       case KeyEvent.VK_SPACE: if (Player.isGrounded == true){Player.Jump();}
                  break;}
```


----------



## InfectedBytes (6. Aug 2016)

die vielen switch sind recht unnötig und gehen am ganzen Sinn von switch vorbei. Du musst nur das break außerhalb der if machen.
Besser:

```
switch(e.getKeyCode()) {
  case KeyEvent.VK_A:
    if(...) { ... }
    break;
  case KeyEvent.VK_D:
    if(...) { ... }
    break;
  // ...
}
```


----------



## BraunBerry (6. Aug 2016)

Okay der selbe Effekt aber etwas Codefreundlicher. Danke


----------



## InfectedBytes (6. Aug 2016)

Kein Problem.
Du musst dir halt deinen Code einfach mal genauer ansehen. Hier war eben das Problem dass du mit break eben nur aus dem Switch gesprungen bist wenn dein if erfüllt war. Wenn es aber nicht erfüllt war, bist du einfach fröhlich weiter durch das switch gegangen.

Ansonsten mach bitte dringen das == true weg!
Es ist zwar nicht falsch, aber dennoch recht unschön und vorallem unnötig, denn immerhin ist deine Variable schon ein boolean, da brauchst nicht auch noch prüfen ob dieser boolean gleich einem anderen boolean ist.
Einfach:

```
if(Player.isGrounded) { ... }
```
und fertig. Falls du wissen willst ob der boolean false ist, machst du einfach:

```
if(!Player.isGrounded) { ... }
```


----------



## BraunBerry (13. Aug 2016)

Okay mach ich. Ich hatte mir das nur zum besseren Verständnis so aufgeschrieben aber ich überarbeite den fertigen Code ja alle paar Tage


----------

