Java Kollisionsabfrage

JavaGamer

Bekanntes Mitglied
Hallo,

ich hätte da ein "kleines" Problem. Dieses besteht darin, dass ich momentan ein 2D Game programmiere, jedoch funktioniert die Kollisionsabfrage nicht so wie ich es will, bzw. garnicht.

Also hier ist der src:

Java:
	/**
	 * Controls the physics for the object. If it's true you will get physics, if false, no physics are enalbed, but
	 * the collision is always active!
	 */
	protected boolean physics = true;
	
	/**
	 * The gravity
	 */
	public float gravity = 1.0F;
	
	/** The maximum of speed */
	public float speed = 159.0F / 60;
	
	public Rectangle rect;

	/**
	 * Override this method to load stuff for the game object.
	 * 
	 * Don't forget to call this method in the init-method of your main class.
	 */
	public abstract void init();
	
	/**
	 * Override this method to update the game object.
	 * Don't forget to call this method in the update-method of your main class.
	 */
	public abstract void update();
	
	/**
	 * Override this method to render the game object.
	 * Don't forget to call this method in the render-method of your main class.
	 * 
	 * @param graphics The java.awt.Graphics class. This is a must, that you can render stuff in this method.
	 */
	public abstract void render(Graphics g);
	
	public boolean collide(GameObject tempObject)
	{
		int bx = (int) (x + velX);
		int by = (int) (y + velY);
		
		rect.setLocation((int)bx, (int)by);
		
		if(!this.physics)
			return false;
		
		if(rect.intersects(tempObject.rect))
			return true;
		
		return false;
	}
	
	public void fall(GameObject tempObject)
	{
		if(!this.physics)
			return;
		
		if(!collide(tempObject))
		{
			//System.out.println("?");
			this.velY += this.gravity;
			
			if(this.velY > this.speed)
				this.velY = this.speed;
		}
		else if(collide(tempObject))
		{
			//System.out.println("?---------");
			this.velY = 0;
		}
	}

Und hier der Code der Player-Klasse:
Java:
	@Override
	public void update() 
	{
		this.x += this.velX;
		this.y += this.velY;
		
		for(int i = 0; i < this.handler.objects.size(); i++)
		{
			GameObject tempObject = this.handler.objects.get(i);
			
			this.fall(tempObject);
		}
	}

So, nun besteht das Problem darin, dass der Spieler einfach durch andere Objekte hindurch fallen kann, anstatt bei der Kollsision mit anderen Objekten zu stoppen. Hätte da vielleicht irgendjemand eine Idee?
Die x,y, velX und velY Variablen sind übrigens alles doubles. 😉

Ich hoffe mal, das mir hier geholfen werden kann. 😉
JavaGamer
 
EDIT :

Ich sehe gerade bei dir in Zeile 47 in der Collide Funktion:
Code:
if(!this.physics)
   return false;
 
if(rect.intersects(tempObject.rect))
   return true;

Du gibts false zurück, wenn keine Physics angegeben sind, bevor du eine Collision prüfst. Wird wohl der Fehler sein.
 
Zuletzt bearbeitet:
Ist zwar nicht auszuschließen, aber this.physics steht auf true und somit wird die Abfrage ignoriert. Ich hätte auch an der Stelle angesetzt ...

Java:
if(rect.intersects(tempObject.rect))
{
    System.out.println("X");
    return true;
}

... du könntest den Fehler eingrenzen indem du guckst, ob dein fallendes Objekt überhaupt den Boden schneidet, wenn er es sollte. Muss nicht auf den Pixel genau sein, sondern nur grob. Dann weist du zumindest, dass deine Logik zum 0-setzen der Geschwindigkeit falsch ist und nicht die Kollisionsabfrage.

PS: Das else-if ist unnötig und erschwert den Code zu lesen.

Java:
if(!collide(tempObject))
else if(collide(tempObject))

Besser:
Java:
if(collide(tempObject))
else
 
Zuletzt bearbeitet:
Ok, habe dies nun getestet. Hier der geänderte Code, da ich noch einen kleinen Fehler gefunden hatte (der aber leider nicht die Lösung des Problems war).

(Code von der collide-Methode)
Java:
		if(rect.intersects(tempObject.rect) && this.id != tempObject.id)
		{
			System.out.println("X");
			return true;
		}

Also der gibt nun die ganze Zeit "X" aus wenn dieser mit einem Objekt kollidiert, sobald ich allerdings ein else einfüge, damit dieser z.B. eine bestimmte andere Aktion ausführt wenn er nicht kollidiert, gibt er egal was passiert nur noch "-" aus (was im else block steht) anstatt auch "X" auszugeben wenn er kollidiert wie er es ohne else-Block getan hat.

Danach habe ich mal den Code für einen kleinen Test weiter umgeschrieben. Die fall-Methode tut nun garnichts mehr außer die collide-Methode aufzurufen.
Java:
	public boolean collide(GameObject tempObject)
	{
		velY = 2;
		int bx = (int) (x + velX);
		int by = (int) (y + velY);
		
		rect.setLocation((int)bx, (int)by);
		
		if(!this.physics)
			return false;
		
		if(rect.intersects(tempObject.rect) && this.id != tempObject.id)
		{
			velY = 0;
			System.out.println("X");
			return true;
		}
		
		return false;
	}
Soweit so gut ("X" wird immer noch ausgegeben bei Kollision mit anderen Objekten), allerdings geschieht bei der Kollision mit anderen Objekten garnichts -> der Spieler fällt immer noch

Danach habe ich jetzt die Methode noch weiter abgeändert:
Java:
	public boolean collide(GameObject tempObject)
	{
		velY = 2;
		int bx = (int) (x + velX);
		int by = (int) (y + velY);
		
		rect.setLocation((int)bx, (int)by);
		
		if(!this.physics)
			return false;
		
		if(rect.intersects(tempObject.rect) && this.id != tempObject.id)
		{
			velY = 0;
			System.err.println("1.: " + velY);
			return true;
		}
		
		System.out.println("2.: " + velY);
		
		return false;
	}
--> nur 2.: wird ausgegeben (immer)!

Java:
	public boolean collide(GameObject tempObject)
	{
		velY = 2;
		int bx = (int) (x + velX);
		int by = (int) (y + velY);
		
		rect.setLocation((int)bx, (int)by);
		
		if(!this.physics)
			return false;
		
		if(rect.intersects(tempObject.rect) && this.id != tempObject.id)
		{
			velY = 0;
			System.err.println("1.: " + velY);
			return true;
		}
		
		return false;
	}
--> nur 1.: wird ausgegeben bei Kollision!

Java:
	public boolean collide(GameObject tempObject)
	{
		velY = 2;
		int bx = (int) (x + velX);
		int by = (int) (y + velY);
		
		rect.setLocation((int)bx, (int)by);
		
		if(!this.physics)
			return false;
		
		if(rect.intersects(tempObject.rect) && this.id != tempObject.id)
		{
			velY = 0;
			return true;
		}
		
		System.out.println("2.: " + velY);
		
		return false;
	}
--> nur 2.: wird ausgegeben (immer)!

Ich hoffe dies hilft beim finden des Problems.
 
Zuletzt bearbeitet:
Du hast da jetzt "velY = 2;" hinzugefügt. Dadurch wird dein Objekt mit Sicherheit nie stoppen. Die letzen beiden Code-Snippets sind sehr verwirrend / unnötig.

Testen musst du nur deinen collide-Code mit den Werten, die du schon hattest (von den velY =2 abgesehen):

Java:
     public boolean collide(GameObject tempObject)
    {
    int bx = (int) (x + velX);
    int by = (int) (y + velY);
     
    rect.setLocation((int)bx, (int)by);
     
    if(!this.physics)
    return false;
     
    if(rect.intersects(tempObject.rect) && this.id != tempObject.id)
    {
    velY = 0;
    System.err.println("1.: " + velY);
    return true;
    }
     
    System.out.println("2.: " + velY);
     
    return false;
    }

Wenn "1" bei einer Kollision und "2" beim Fallen ausgegeben wird, ist deine Methode OK. Dass velY bei einer Kollison 0 ist, glaube ich dir ... steht eine Zeile vorher. Der Fehler liegt dann in der Methode fall. Du solltest auch bedenken, dass du velX nicht beachtest. Eigentlich müsste die horizontale Fall-Geschwindigkeit beim Aufprall auch 0 sein.

Also der gibt nun die ganze Zeit "X" aus wenn dieser mit einem Objekt kollidiert, sobald ich allerdings ein else einfüge, damit dieser z.B. eine bestimmte andere Aktion ausführt wenn er nicht kollidiert, gibt er egal was passiert nur noch "-" aus (was im else block steht) anstatt auch "X" auszugeben wenn er kollidiert wie er es ohne else-Block getan hat.

Das ist definitv nicht der Fall, wenn du das gescheit programmierst. Ohne Code-Beispiel kann ich den Fehler nicht zeigen, wobei du wahrscheinlich Klammern vergessen hast ...
 
Zuletzt bearbeitet:
Also das mit der horizontalen Fall-Geschwindigkeit stelle ich mir gerade ein wenig kompliziert vor, da die Varialbe velX auch die Richtung bestimmt, in der der Spieler läuft. Aber egal, wirklich ne gute und realistische Physik habe ich eh net im Game, auch wenn ich's am liebsten machen würde, aber ich habe keine Ahnung wie man die Physik aus der Schule in Java umsetzten soll. Zudem bezweifle ich, dass das was wir im Physik-Unterricht besprochen haben überhaupt für die Umsetzung einer wirklichen Physik in Java reichen würde.

Aber egal, habe jetzt auf jeden Fall die collide-Methode am laufen, nur die fall-Methode halt net. Nach momentanigen Änderungen sieht diese so aus:
Java:
	public boolean collide(GameObject tempObject, float x, float y)
	{
		rect.setLocation((int)x, (int)y);
		
		if(!this.physics)
			return false;
		
		if(rect.intersects(tempObject.rect) && this.id != tempObject.id)
			return true;
		
		return false;
	}

	public void fall(GameObject tempObject)
	{
		if(!this.physics)
			return;
		
		if(collide(tempObject))
		{
			velY = 0;
			return;
		}
		else
		{
			System.out.println("just a test!");
			/*velY += gravity;
			
			if(this.velY > this.MAX_SPEED) // speed wurde in MAX_SPEED umbenannt
				this.velY = this.MAX_SPEED;*/
		}
	}

Und das mit velY wird nun beim init vom Player gesetzt, also wenn das Player-Objekt erstellt wird (aufgrund von Testzwecken). Zudem wird, so wie ich es sehe anscheinend auch das "just a test!" dauerhaft ausgegeben, egal was passiert.

Also jetzt meine Frage, wie schaffe ich es das die fall-Methode auch funktioniert und vorallem, dass der Spieler automatisch runterfällt wenn nichts unter diesem ist und sobald ein Objekt unter diesem ist, dass er dann nicht mehr fällt?
 
Der Fehler liegt in der for-Schleife, wo fall() aufgerufen wird. Befindet sich das Kollisionsobjekt irgendwo zwischen den 100 Objekten, dann wird zwar die Kollision erkannt und deine Geschwindigkeit auf 0 gesetzt, aber im darauf folgenden Schritt wieder hochgesetzt, da dein Objekt die anderen Objekte nicht berührt. Eigentlich müsstete deine Logik folgendermaßen aussehen:

Java:
boolean isFalling = true;
for(GameObject obj = handler.objects) {
    if(collide(obj)) {
        isFalling = false;
        break;
    }
}
if(isFalling) {
    doFall();
}

public void doFall() {
velY += gravity;
 
if(this.velY > this.MAX_SPEED) // speed wurde in MAX_SPEED umbenannt
this.velY = this.MAX_SPEED
}
 
Zuletzt bearbeitet:
Ok, nur das mit der for-Schleife funktioniert noch nicht so ganz, da "handler.objects" eine LinkedList ist, darum habe ich es mal mit der alten for-Schleife getestet, allerdings fällt dort der Spieler immer noch durch Objekte hindurch.
 
Dann würde ich ganz plump behaupten : Dein Spieler ist schneller als dein Objekt hoch. Sodass es erst gar nicht zu einer Kollision kommen kann. Pack mal ein paar sysouts in den Code und schaue, ob dein Spieler überhaupt mit deinem Objekt Kollidiert.
 
Also ich habe dies mal ganz einfach getestet indem ich einfach mal die velY Variable gleich der Max-speed variable gesetzt habe und die Kollision hat immer noch funktioniert, nur sobald ich etwas in die fall Methode, dort in den else Block reinschreibe, funktioniert es nicht mehr.

Also sobad die Varialbe velY im else-Block verändert wird, funktioniert die Kollisionsabfrage nicht mehr.

Player (extends GameObject)
Java:
	@Override
	public void update() 
	{
		this.x += this.velX;
		this.y += this.velY;
		
		/*for(GameObject obj = handler.objects)
		{
			if(collide(obj))
			{
				isFalling = false;
				break;
			}

		}*/
		
		//if(isFalling)
			//doFall();
		
		for(int i = 0; i < this.handler.objects.size(); i++)
		{
			GameObject tempObject = this.handler.objects.get(i);
			
			//if(this.rect.getBounds2D().intersects(tempObject.rect.getBounds2D()))
				//System.out.println("AH!");
			//else
				//System.out.println("ELSE");
			
			this.fall(tempObject);
		}
	}

GameObject
Java:
	public boolean collide(GameObject tempObject, float x, float y)
	{
		rect.setLocation((int)x, (int)y);
		
		if(!this.physics)
			return false;
		
		if(rect.intersects(tempObject.rect) && this.id != tempObject.id)
			return true;
		
		return false;
	}

	public void fall(GameObject tempObject)
	{
		if(!this.physics)
			return;
		
		if(collide(tempObject))
		{
			velY = 0;
			return;
		}
		else
		{
			//System.out.println("just a test!");      sobald hier die velY Varialbe geändert wird funktioniert es nicht mehr
			/*velY += gravity; 
			
			if(this.velY > this.MAX_SPEED)
				this.velY = this.MAX_SPEED;*/
		}
	}
 
Zuletzt bearbeitet:
1. Wie gesagt, deine for-Schleife ist falsch und kann nicht funktionieren:

for(int i = 0; i < this.handler.objects.size(); i++) { ... }

2. Das was Bananabert gesagt hat. Deine Geschwindigkeit nimmt irrsinnig stark zu. Versuchs mal mit "velY = 8" anstatt "velY += gravity".
 
Wie müsste ich den die for-Schleife verändern? Da diese for-Schleife geht ja auch nicht, da handler.objects eine LinkedList ist.
Java:
		for(GameObject obj = handler.objects)
		{
			if(collide(obj))
			{
				isFalling = false;
				break;
			}

		}

Und mit velY = 8 funktioniert dies auch nicht.
 
Bist du dir denn sicher, dass dein Objekt kollidieren sollte?
Setz mal deine maximalen Updates auf 2 oder 3 pro Sekunde.

Was ich auch nicht verstehe, deine "collide"-Methode erwartet float x, float y. In deinen Beispielen gibst du diese nie an. Hast du deine Methode überladen und übergibts dann 0,0 an diese? Weil du als erste in der "collide"-Methode rec.setLocation(x,y) setzt. Könnte ebenfalls eine Fehlerquelle sein.
 
Ich habe zwei collide-Methoden. Die eine ruft die andere nur auf, dabei wird dann für x und y die aktuelle Position des Spielers genommen (schon überprüft, funktioniert, ist nicht 0,0).

Problem ist ja, sobald dies in der fall-Methode steht, geht es nicht mehr:
Java:
		else
		{
			//System.out.println("just a test!");
			velY += 8;
			
			if(this.velY > this.MAX_SPEED)
				this.velY = this.MAX_SPEED;
		}

Da geplant ist es halt so, wenn der Spieler kollididert soll velY auf 0 gesetzt werden, wenn dieser nicht kollidiert soll automatisch velY erhöht werden, darum die else-Schleife.

Also momentan läuft das Game mit 247 FPS und zwischen 13 und 15 Updates pro Sekunde.

Mit der for-each Schleife von Ruzmanz läuft das ganze sogar bei 55 Updates pro Sekunde.
 
Zuletzt bearbeitet:
Ja, sagtest du bereits mehrfach. Aber :
Bist du dir denn sicher, dass dein Objekt kollidieren sollte?
Setz mal deine maximalen Updates auf 2 oder 3 pro Sekunde.
Oder gar am Besten mit 1 Update pro Sekunde.
Mach dazu noch ein paar Ausgaben im Code. Z.B. Position vom Spieler und Objekten ausgeben lassen.

Alles andere ist nur gerate, da wir den kompletten Code nicht haben.
 
Es fehlt noch ein velY = 0, sofern du meine Methode nimmst.

Java:
for(GameObject obj : handler.objects)
{
    if(collide(obj))
    {
        isFalling = false;
        break;
    }
}
 
if(isFalling)
{
    velY = 8;
}
else
{
    velY = 0;
}
 
Ok, habe jetzt deine Methode genommen und funktioniert alles ohne Probleme.
Danke nochmal für die Hilfe! 😀

Das Game läuft jetzt sogar mit 55 Updates pro Sekunde bei 1004 FPS, bin ja mal gespannt wie es aussieht wenn das Game fertig ist, da momentan nur 1 Objekt und der Spieler vorhanden sind. 😉

Jetzt bleibt nur noch eine Frage, wie lasse ich jetzt den Spieler runterfallen sobald kein Objekt mehr unter ihm ist und wenn er nicht springt und der Sprung halt noch nicht den höchsten Punkt herreicht hat?
 
Zuletzt bearbeitet:
Du brauchst noch einen zweiten Zustand.

Java:
if(isJumping) {
	velY = -8;
}
else if(isFalling)
{
	velY = 8;
}
else
{
	velY = 0;
}
 
Ok, dies funktioniert jetzt so einigermaßen....
Da momentan kann ich seitlich noch in andere Objekte hineinspringen und sogar durch Objekte durchspringen bzw. in diese hineinspringen. Dann wäre da noch das Problem, dass ich in der Luft rumlaufen kann, da ich halt nicht automatisch runterfalle, sondern erst falle wenn ich springe oder manuell die Varialbe "isFalling" auf true setzte.

Player
Java:
	@Override
	public void update() 
	{
		this.x += this.velX;
		this.y += this.velY;
		
		for(GameObject obj : handler.objects)
		{
			if(collide(obj))
			{
				isFalling = false;
				break;
			}
		}
		
		if(isFalling || isJumping)
			doFall();
		else
			velY = 0;
	}

GameObject
Java:
	public boolean collide(GameObject tempObject, float x, float y)
	{
		rect.setLocation((int)x, (int)y);
		
		if(!this.physics)
			return false;
		
		if((rect.intersects(tempObject.rect) || (rect.contains(tempObject.rect))) && this.id != tempObject.id)
			return true;
		
		return false;
	}
	
	public void doFall()
	{
		if(!this.physics)
			return;
		
		if(isJumping)
		{
			this.setVelY(-10);
			
			this.isJumping = false;
			this.isFalling = true;
		}
		else if(isFalling)
		{
			velY += gravity;
			
			if(this.velY > this.MAX_SPEED)
				this.velY = this.MAX_SPEED;
		}
		else
			velY = 0;
	}

KeyInput
Java:
	private Handler handler;
	
	private boolean[] keys = new boolean[120];
	public boolean up, down, left, right, f3;
	
	public KeyInput(Handler handler)
	{
		this.handler = handler;
	}
	
	public void update()
	{
		this.up = keys[KeyEvent.VK_UP] || keys[KeyEvent.VK_W];
		this.down = keys[KeyEvent.VK_DOWN] || keys[KeyEvent.VK_S];
		this.left = keys[KeyEvent.VK_LEFT] || keys[KeyEvent.VK_A];
		this.right = keys[KeyEvent.VK_RIGHT] || keys[KeyEvent.VK_D];
		this.f3 = keys[KeyEvent.VK_F3];
		
		for(GameObject obj : handler.objects)
		{
			if(obj.getId() == 1 && obj instanceof Player)
			{
				if(up && !obj.isJumping()) obj.setJumping(true);
				if(left && !right && !obj.isFalling()) obj.setVelX(-5);
				if(right && !left && !obj.isFalling()) obj.setVelX(5);
				
				if(!right && !left)
					obj.setVelX(0);
				
				if(!up && !obj.isJumping()) obj.setJumping(false);
			}
		}
	}
	
    /**
     * Invoked when a key has been pressed.
     */
	@Override
	public void keyPressed(KeyEvent event)
	{
		keys[event.getKeyCode()] = true;
		
		if(event.getKeyCode() == KeyEvent.VK_ESCAPE && GameState.getState() == GameState.GAME)
			GameState.setState(0x1);
	}
	
    /**
     * Invoked when a key has been released.
     */
	@Override
	public void keyReleased(KeyEvent event)
	{
		keys[event.getKeyCode()] = false;
	}

Zudem, wie kann ich es programmieren, dass man während man fällt nicht laufen kann, allerdings wenn man springt halt nach rechts und links springen kann, aber diese Bewegung nach rechts und links nicht einfach stopt sobald man fällt.

Und dann hätte ich da noch das Problem, dass ich auch springen kann wenn ich runterfalle und sogar wenn ich springe.

Irgendeine Idee?
 
Dass du durch Objekte durchspringen oder links/rechts durchlaufen kannst, liegt wohl daran, dass du nur eine Kollision für den Boden hast. Und du brichst deine Kollisionsabfragen gegen weitere Objekte ab, sobald eine Kollision statt fand.

Zudem, wie kann ich es programmieren, dass man während man fällt nicht laufen kann, allerdings wenn man springt halt nach rechts und links springen kann, aber diese Bewegung nach rechts und links nicht einfach stopt sobald man fällt.

Und dann hätte ich da noch das Problem, dass ich auch springen kann wenn ich runterfalle und sogar wenn ich springe.

Irgendeine Idee?

Zu dem Links und Rechts Springen : In dem du die Richtung des Sprunges speicherst und weitere Eingaben während des Sprunges ignorierst.

Und das Springen beim Fallen ebenfalls. So lange du springst, ignorierst du die Eingaben.
 
Zuletzt bearbeitet:

Zurück
Oben