Kollision genau erkennen mit intersects

Javaman91

Bekanntes Mitglied
Hallo,

bei meinem Programm wird zurzeit nur erkannt ob eine Kollision stattgefunden hat oder nicht.
Im moment ist es einfach so, das der Spieler wenn er in der Luft ist in richtung Boden bewegt wird und nicht durch den Boden fallen kann, da einfach wenn eine Kollision erkannt wird ein boolean auf false gesetzt wird.

Nun möchte ich aber unterscheiden, wo eine Kollision stattgefunden hat.
Also ob eine Hitbox von rechts, links, oben oder unten berührt wurde.

Hier die Klasse, in der eine kollision ermittelt wird:

Java:
package map2;

import java.io.IOException;

//import java.io.IOException;

import org.newdawn.slick.*;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.SpriteSheet;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Shape;

public class Hauptfenster extends BasicGame {

   private Editor editor;
   private Input in;
   private int ypos = 200;
   private int xpos = 40;
   private SpriteSheet spriteSpieler;
   private Animation animationSpieler;
   private SpriteSheet spriteSpieler1;
   private Animation animationSpieler1;
   private int animieren;
   protected Shape spielerHitbox;
   private boolean kolision = true;
   
   public Hauptfenster() {
     
     super("Game");
   }

   @Override
   public void init(GameContainer gc) throws SlickException {
     
     
     editor = new Editor ();
   
     try {
       editor.load("Level.txt");
     } catch (IOException e) {
       e.printStackTrace();
     }
     
    in = gc.getInput();
     
    spriteSpieler = new SpriteSheet("spieler.png", 40, 40);
    animationSpieler = new Animation(spriteSpieler, 100);
     
    spriteSpieler1 = new SpriteSheet("Rauchen.png", 40, 40);
    animationSpieler1 = new Animation(spriteSpieler1, 1);
   }

   @Override
   public void update(GameContainer gc, int arg1) throws SlickException {
     
     if(in.isKeyDown(in.KEY_W)){
       ypos -= 2;
       animieren = 1;
     }
     else if(in.isKeyDown(in.KEY_S)){
       ypos += 1;
       animieren = 1;
     }
     else if(in.isKeyDown(in.KEY_D)){
       xpos += 1;
       animieren = 1;
     }
     else if(in.isKeyDown(in.KEY_A)){
       xpos -= 1;
       animieren = 1;
     }
     else
       animieren = 0;
     
     for(int i=0; i<20; i++){
     for(int j=0; j<20; j++){
     
       if(editor.getarrayHitboxen()[i][j] != null){   
       if(editor.getarrayHitboxen()[i][j].intersects (spielerHitbox)){
         kolision=false;
       }
      }
    }
  }

     if(kolision == true){
       ypos++;
     }
     else{
       kolision = true;
     }
   }   
   
   @Override
   public void render(GameContainer gc, Graphics g) throws SlickException {
   
     editor.render(g);
     spielerHitbox = new Rectangle (xpos, ypos, 25, 40);
     g.setColor(Color.red);
     g.draw(spielerHitbox);
     
     if(animieren == 1){
     animationSpieler.draw(xpos, ypos);
     }
     
     if(animieren == 0){
     animationSpieler1.draw(xpos, ypos);
     }
   }

   public static void main(String[] args) throws SlickException {
     
     AppGameContainer container = new AppGameContainer(new Hauptfenster());
     container.setDisplayMode(500, 500, false);
     container.setShowFPS(true);
     container.setVSync(true);
     container.start();
   }
}

Wie mache ich das?
 
Danke für den Link!

Ich fasse mal kurz zusammen, wie ich das Verstanden habe.
Slick2D bietet eine Klasse an mit der man Vektoren erzeugen kann. Die Klasse lautet: „Vector2f“.
Erzeugt werden kann ein Vektor mit:
Java:
Vector2f vektor = new Vector2f(x, y);
x & y geben die Position des Objektes an in meinen Fall die linke obere Ecke der Hitbox (Rechteck).

Hier eine kleine Grafik als Beispiel für zwei Vektoren:
(Siehe: Bild1)

Als nächstes subtrahiert man die beiden Vektoren mit:
Java:
vektor1.sub(vektor2);
Nun meine erste Frage, was bekommt er nach der Subtraktion heraus, einen neuen Vektor mit einer neuen Position??

War meine Erklärung bis hier hin Richtig?

MfG und DANKE für eure Hilfe!!
 

Anhänge

  • Bild1.png
    Bild1.png
    38,3 KB · Aufrufe: 26
Ich verstehe das ganze leider nicht so wirklich.
Mir ist klar, das nach der minus Rechnung ein neuer Vektor herauskommt.
Aber durch eine einfache subtraktion von zwei Vektoren kann ich doch nicht ermitteln, ob nun eine Kollision mit der rechten oder linken Seite einer Hitbox stattgefunden hat.

Darf ich euch um ein einfaches und gut erklärtes Beispiel bitten??

Mfg
 
Achso, ich glaube jetzt habe ich es Verstanden:

a & b sind die beiden Vektoren.
(x|y) -> x & y sind die Koordinaten für die Punkte des Vektors (Pfeil).

In deinem Beispiel beginnen beide Vektoren bei: (0|0) -> Ursprung der Vektoren.


a hat für den Endpunkt des Vektors die Koordinaten: (1|2)
b hat (2|5)

Nun rechnest Du:

c = a-b = (-1|0)

Das Ergebnis lautet (-1|0).
Das bedeutet, das b um -1 nach links wandert.


Hier eine Zeichnung von mir:

(Siehe Bild: Beispiel)

Stimmt das nun so?
 

Anhänge

  • Beispiel.png
    Beispiel.png
    122,5 KB · Aufrufe: 40
Wenn du guckst, wann sie intersecen und dann die Subtraktion durchführst siehst du, welche Seite den Zusammenstoß hatte. ( im Beispiel eine Kollision mit der linken Seite des Players, da -1;0) Du kannst so also herausfinden auf welcher Seite der Spieler, oder das Objekt, ist - und im Umkehrschluss, von welcher Seite er/es kam.
 
Jetzt habe ich verstanden was Du meinst.

1. Ich warte ab, bis die Kollision stattgefunden hat.
2. Dann füre ich die Berechnung durch mit: "VektorA - VektorB" in Java sieht das dann im Sourcecode so aus:
Java:
vektor1.sub(vektor2);
PS: sub = subtraktion

3.Nach der Berechnung kann ich mit den Ergebnis feststellen, von welcher Seite mein Block getroffen wurde.
Wenn wie in Deinem Beispiel das Ergebnis (-1|0) liefert dann heißt das, dass sich der Player auf der x-Achse nach links bewegt hat, da ja: x=-1 (x entspicht der x-Achse) und y=0 (y enspricht der y-Achse).
Wäre x = 1, dann hätte eine Kollision von der rechten Seite stattgefunden, da ja ein positives x nach rechts geht.
Wäre y = -1, so hätte die Kollision von oben auf den Block stattgefunden. Bei y=1 wär das eine Kollision von unten.

Bitte bestätigen wenn korrekt??
 
Ja, so könnte man es benutzen. Aber dazu muss gesagt sein, dass es sich um vector2f handelt, was wahrscheinlich für float steht. D.h. die Werte sind nicht so schön. Dazu kommt, dass du das Ganze auf dein Koordinatensystem anpassen musst, wenn der Block z.B. 50 Breit ist, so hättest du statt der -1 eine -50. Und wenn die beiden Objekte nicht gleich groß sind, musst du passende Prüfungen machen, um links/rechts/oben/unten zu bestimmen (ähnliches Problem: der Player steht oben, jedoch nicht Kante an Kante).
 
Ich habe einen besser Möglichkeit gefunden um Kollisionen abzufragen.

Link: http://www.basteldroid.de/kapitel-3-lektion-4-kollisionserkennung-grundlagen/

Nun bin ich gerade dabei diese Zeile zu verstehen:
"if(MegaMan.right + MegaMan.vX > brick.left)"

Meine if sieht so aus:

Java:
if(editor.getarrayHitboxen()[i][j].intersects(spielerHitbox)){

Was muss ich anstelle von "right" schreiben, damit ich die x Koordinate der rechten Seite der Hitbox erhalte?

Normalerweise müsste ich doch einfach schreiben:

Java:
if(editor.getarrayHitboxen()[i][j].x+25 > spielerHitbox.x){

x ist normalerweise die position von der linken Seite der Hitbox.
Mit "+25" sollte ich doch eigentlich die rechte Seite der Hitbox erreichen, da eine Hitbox 25*25 Pixel groß ist.
Das ">" sagt ja nur, solange die x Position der linken Seite größer ist alls die der Rechten, so ist die Bedingung erfüllt. Wenn sich die Hitboxen berühren, dann ist ja die x Koordinate bei beiden Hitboxen gleich groß und die if ist nicht mehr erfüllt.

So hab ich mir das gedacht, sollte doch eigentlich funktionieren?
 
Ja, so sollte es gehen. Ich hatte hier letztens auch einen Thread aufgemacht, wo ich ein ähnliches Problem hatte. Ich habe es auch so gelöst.
 
Java:
if(collision){
    ply.setVelY(0);
    ply.setVelX(0);
   if(intersect.getBoundsInLocal().getHeight()> intersect.getBoundsInLocal().getWidth()){
       //left/right
       if(ply.getPlayer().getBoundsInLocal().getMinX()< s.getBoundsInLocal().getMaxX()&& ply.getPlayer().getBoundsInLocal().getMaxX()> s.getBoundsInLocal().getMaxX()){
           //player hits the left side of a block
            ply.setX(ply.getPlayer().getX()+intersect.getBoundsInLocal().getWidth());
           [URL='http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+system']System[/URL].out.println("left");
       }else{
           //player hits the right side of a block
           [URL='http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+system']System[/URL].out.println("right");
            ply.setX(ply.getPlayer().getX()-intersect.getBoundsInLocal().getWidth());
       }
   }elseif(intersect.getBoundsInLocal().getWidth()> intersect.getBoundsInLocal().getHeight()){
       //top/bottom
       if(ply.getPlayer().getBoundsInLocal().getMinY()< s.getBoundsInLocal().getMinY()&& ply.getPlayer().getBoundsInLocal().getMaxY()> s.getBoundsInLocal().getMinY()){
           //player is on the top
            ply.setY(ply.getPlayer().getY()-intersect.getBoundsInLocal().getHeight());
            ply.setJumping(false);
       }else{
           //player hits the bottom of a block
            ply.setY(ply.getPlayer().getY()+intersect.getBoundsInLocal().getHeight());
       }
   }
}
Max x entspricht dem "normalen" (linken) x + die Breite. Und y genau das selbe mit der Höhe.
Die Reaktion auf die jeweilige Seite ist natürlich meine individuelle, vielleicht nicht die passende für dich.
 
Hallo,

war für ein paar Tage im Urlaub.

Habe nun etwas rumprobiert, komme aber bei einem Problem nicht weiter:
Java:
editor.getarrayHitboxen()[i][j].x+25 > spielerHitbox.x
Hier wird "x" rot unterstrichen.
Als Lösung schlägt er mir folgendes vor: "Replacce x with getter".
Das heißt er ersetzt "x" mit "getX()", dies funktioniert auch nicht.
"x" soll ja eigentlich nur die x-Position vom Rechteck sein.
Daraufhin dachte ich mir, ich könnte mir ja von meiner Editor-Klasse die Position aus folgender Codezeile herausholen:
Java:
hitbox = new Rectangle(25*j, 25*i, 25, 25);
Das heißt: "25*j" entspricht ja eigentlich meiner x-Position.
Mit einer getter-Methode könnte ich dann diesen Wert übergeben.
Java:
public int getJ(){
      return j*25;
}
Für "spielerHitbox" müsste ich natürlich einen anderen Wert nehmen.
Leider funktioniert das so nicht:
Java:
getarrayHitboxen()[i][j].getJ()+25

Was könnte ich da machen?
 
Klasse1:
Java:
package map2;

import java.io.IOException;
import org.newdawn.slick.*;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.SpriteSheet;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.geom.Vector2f;

public class Hauptfenster extends BasicGame {

   private Editor editor;
   private Input in;
   private int ypos = 200;
   private int xpos = 40;
   private SpriteSheet spriteSpieler;
   private Animation animationSpieler;
   private SpriteSheet spriteSpieler1;
   private Animation animationSpieler1;
   private int animieren;
   protected Shape spielerHitbox;
   private boolean kolision = true;
   private Vector2f vektor;
   private Vector2f vektor1;
   private boolean linkeKollision = true;
   private Shape hitbox;
  
   public Hauptfenster() {
    
     super("Game");
   }

   @Override
   public void init(GameContainer gc) throws SlickException {
    
     editor = new Editor ();
  
     try {
       editor.load("Level.txt");
     } catch (IOException e) {
       e.printStackTrace();
     }
    
    in = gc.getInput();
    
    spriteSpieler = new SpriteSheet("spieler.png", 40, 40);
    animationSpieler = new Animation(spriteSpieler, 100);
    
    spriteSpieler1 = new SpriteSheet("Rauchen.png", 40, 40);
    animationSpieler1 = new Animation(spriteSpieler1, 1);
   }

   @Override
   public void update(GameContainer gc, int arg1) throws SlickException {
    
     if(in.isKeyDown(in.KEY_W)){
       ypos -= 2;
       animieren = 1;
     }
     else if(in.isKeyDown(in.KEY_S)){
       ypos += 1;
       animieren = 1;
     }
     else if(in.isKeyDown(in.KEY_D)){
       xpos += 1;
       animieren = 1;
     }
     else if(in.isKeyDown(in.KEY_A)){
       xpos -= 1;
       animieren = 1;
     }
     else
       animieren = 0;
        
     for(int i=0; i<20; i++){
      for(int j=0; j<20; j++){
    
       if(editor.getarrayHitboxen()[i][j] != null){  
         if(editor.getarrayHitboxen()[i][j].getXPos() > spielerHitbox.xpos){
           System.out.println("Kollision erkannt!");
         }
        }
       }
      }
     }  
   
   @Override
   public void render(GameContainer gc, Graphics g) throws SlickException {
  
     editor.render(g);
     spielerHitbox = new Rectangle (xpos, ypos, 25, 40);
     g.setColor(Color.red);
     g.draw(spielerHitbox);
    
     if(animieren == 1){
     animationSpieler.draw(xpos, ypos);
     }
    
     if(animieren == 0){
     animationSpieler1.draw(xpos, ypos);
     }
   }

   public static void main(String[] args) throws SlickException {
    
     AppGameContainer container = new AppGameContainer(new Hauptfenster());
     container.setDisplayMode(500, 500, false);
     container.setShowFPS(true);
     container.setVSync(true);
     container.start();
   }
}

Klasse2:
Java:
package map2;

import org.newdawn.slick.*;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Shape;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import javax.imageio.ImageIO;

public class Editor {
  
   private FileReader filereader;
   private BufferedReader bufferreader;
   private char array[][] = new char[20][20];
  
   Shape arrayHitboxen[][] = new Shape[20][20];
   Shape hitbox;
   int i, j;
  
   public Editor(){
   }
  
   public void load(String file) throws IOException{
    
    String temp;
    filereader = new FileReader(file);
    bufferreader = new BufferedReader(filereader);
  
    for(int i=0; i<20; i++){
  
    temp = bufferreader.readLine();
    char tempArray[] = temp.toCharArray();
  
    for(int j =0; j<20; j++){
    
    array[i][j] = tempArray[j];
    }
    }
    }
  
    public void render(Graphics g) throws SlickException {
      
      Image bild = new Image("Block.png");
            
    for(i=0; i<20; i++){
    for(j=0; j<20; j++){
    
    if(array[i][j] == 'h'){ //Himmel
    g.setBackground(Color.pink);
    g.fillRect(25*j, 25*i, 25, 25);
    hitbox = new Rectangle(25*j, 25*i, 25, 25);
    arrayHitboxen[i][j] = hitbox;
    g.draw(arrayHitboxen[i][j]);
    }
    else if (array[i][j] == 'g'){ //Gras
    g.setColor(Color.green);
    g.fillRect(25*j, 25*i, 25, 25);
    hitbox = new Rectangle(25*j, 25*i, 25, 25);
    arrayHitboxen[i][j] = hitbox;
    g.draw(arrayHitboxen[i][j]);
    }
    else if (array[i][j] == 'n'){ //Nichts
    g.setColor(Color.yellow);
    g.fillRect(25*j, 25*i, 25, 25);
    arrayHitboxen[i][j] = null;
    }
    else if (array[i][j] == 'w'){ //Wasser
    g.setColor(Color.blue);
    g.fillRect(25*j, 25*i, 25, 25);
    hitbox = new Rectangle(25*j, 25*i, 25, 25);
    arrayHitboxen[i][j] = hitbox;
    g.draw(arrayHitboxen[i][j]);
    }
    else if (array[i][j] == 'e'){ //Erde
    g.setColor(Color.blue);
    g.drawImage(bild, 25*j, 25*i);
    hitbox = new Rectangle(25*j, 25*i, 25, 25);
    arrayHitboxen[i][j] = hitbox;
    g.draw(arrayHitboxen[i][j]);
    }
    }
    }
    }
    
    public Shape[][] getarrayHitboxen(){
      return arrayHitboxen;
    }
  
    public int getXPos(){
      return j*25;
    }
   }
 
Du kannst doch von dem Rectangle-Objekt die x-Werte abfragen (hier:
if(editor.getarrayHitboxen()[i][j]
).
Statt deiner Funktion nimmst du einfach die, die schon bei Shape (parent class von Rectangle) dabei sind.

minX
The left most point of this shape.

maxX
The right most point of this shape

Also je nachdem, welche Seite du willst
Java:
editor.getarrayHitboxen()[i][j].maxX()
oder
Java:
editor.getarrayHitboxen()[i][j].minX()
 
Danke, das mit min & max funktioniert schon mal.

Wenn ich nun folgendes schreibe:
Java:
if(editor.getarrayHitboxen().getMaxX() > spielerHitbox.getMinX() && editor.getarrayHitboxen().getMinX() < spielerHitbox.getMaxX() && editor.getarrayHitboxen().getMaxY() > spielerHitbox.getMinY() 
&& editor.getarrayHitboxen().getMinY() < spielerHitbox.getMaxY()){
System.out.println("Kollision erkannt!");
}
dann habe ich das gleiche programmiert wie, wenn ich "intersects" verwende.
Wenn ich aber nur eine Seite abfragen möchte (z.B. rechte Seite), dann mache ich das so:
Java:
if(editor.getarrayHitboxen().getMaxX() > spielerHitbox.getMinX()){
}

Funktioniert soweit auch, bis auf eine Kleinigkeit.
Die Kollision findet auch statt, wenn ich mich nicht auf der höhe des Blockes befinde
(Siehe Bild).

MfG und DANKE für die Hilfe!!
 

Anhänge

  • Bild.png
    Bild.png
    72 KB · Aufrufe: 37
Habe es heute nochmal versucht, es geht auch mit "getX()".
Weiß nicht, was ich beim ersten mal Falsch gemacht habe, das es nicht ging.
Leider habe bei "getX()" & "getMaxX()" das selbe Probleme wie in meinen Beitrag #25 beschrieben.

Aber warum macht der das?
 
Weil die Bedingung auch zutrifft, wenn er unter/über dem Block ist. Du musst sie so spezifizieren, dass es nur von der Seite, und auf gleicher Bodenhöhe, so ist.
 

Neue Themen


Zurück
Oben