/* * Engine Alpha ist eine anfängerorientierte 2D-Gaming Engine. * * Copyright (c) 2011 - 2014 Michael Andonie and contributors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package ea.internal.phy; import ea.*; import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; /** * Diese Klasse fungiert als Modul zum Behandeln von <b>Kollisionen</b> zwischen <i>mechanischen * <code>Raum</code>-Objekten</i>. Es arbeitet daher zusammen mit dem Mechanik-Client. * * @author Michael Andonie */ public class CollisionHandling { static { Manager.standard.anmelden(new CollisionRoutine(), ea.internal.gra.Zeichner.UPDATE_INTERVALL); new CollisionConsumer().start(); } private static ArrayList<Auftrag> liste = new ArrayList<>(); private static Queue<Collision> colQueue = new LinkedList<>(); /** * Meldet einen Client für ein Newton'sches Objekt an. Nach der Anmeldung sind * <i>Collisions-Checks</i> sowie </i>Collision-Handling</i> aktiv. * * @param c * der anzumeldende Client. */ public static void anmelden (MechanikClient c) { liste.add(new Auftrag(c)); } /** * Meldet einen Client direkt vom Handling ab. Er blockiert danach keine Rechenzeit und keinen * Speicherplatz mehr im Collision Handling.d * * @param c * Der abzumeldende Client. */ public static void abmelden (MechanikClient c) { Auftrag a = null; for (Auftrag auf : liste) { if (auf.parent == c) { a = auf; break; } } if (a != null) { // von kelunik // TODO ArrayList.remove braucht glaube ich kein synchronized. synchronized (liste) { liste.remove(a); } } } private static class CollisionConsumer extends Thread { private CollisionConsumer () { super("Collision Handling"); this.setDaemon(true); } @Override public void run () { while (!interrupted()) { synchronized (colQueue) { while (colQueue.isEmpty()) { // while, da ein notifyAll auch für andere // Bedinungen benutzt werden könnte. try { colQueue.wait(); } catch (InterruptedException e) { // don't care! } } colQueue.remove().abarbeiten(); } } } } @SuppressWarnings ("serial") private static class CollisionRoutine implements Ticker { @Override public void tick () { synchronized (liste) { int i = 0; for (Auftrag a : liste) { int j = 0; for (Auftrag a2 : liste) { if (j > i && a.probablehit(a2) && a.serioushit(a2)) { synchronized (colQueue) { colQueue.add(new Collision(new Auftrag[] {a, a2})); colQueue.notifyAll(); } } j++; } i++; } } } } /** * Diese Klasse speichert die wesentlichen Daten zu einem Auftrag: Collider für schnelle * Berechnung sowie der dahinterliegende Controller. * * @author Michael Andonie */ private static class Auftrag { final MechanikClient parent; Auftrag (MechanikClient p) { parent = p; } public boolean serioushit (Auftrag a) { return parent.ziel().schneidet(a.parent.ziel()); } public boolean probablehit (Auftrag a) { return parent.collider().schneidet(a.parent.collider()); } } /** * Diese Klasse repräsentiert eine Kollision zwischen zwei physikalischen Objekten. Sie wird von * einem Consumer-Thread abgearbeitet. * * @author Andonie */ private static class Collision { final Auftrag[] clients; public Collision (Auftrag[] clients) { this.clients = clients; } public void abarbeiten () { final MechanikClient c1 = clients[0].parent; final MechanikClient c2 = clients[1].parent; //Fenster.instanz.getCam().wurzel().add(c1.ziel().dimension().ausDiesem(),c2.ziel().dimension().ausDiesem()); //System.out.println("work: " + c1.ziel() + " - " + c2.ziel()); // First of all: Clients voneinander lösen if (c1.istBeeinflussbar() && c2.istBeeinflussbar()) { doppelaktivlogik(c1, c2); } if (c1.istBeeinflussbar() && !c2.istBeeinflussbar()) { ungleichlogik(c1, c2); } if (!c1.istBeeinflussbar() && c2.istBeeinflussbar()) { ungleichlogik(c2, c1); } } /** * Abarbeiten: 2 beeinflussbare Objekte prallen aufeinander. ->Impulsspaß * * @param c1 * Client 1 * @param c2 * Client 2 */ public static void doppelaktivlogik (MechanikClient c1, MechanikClient c2) { //Solve Collision do { c1.bewegen(c1.getVelocity().gegenrichtung().multiplizieren(MechanikClient.DELTA_T)); c2.bewegen(c2.getVelocity().gegenrichtung().multiplizieren(MechanikClient.DELTA_T)); } while (c1.ziel().schneidet(c2.ziel())); // Elastischer Stoß! -> Impulserhaltung //Parameter bestimmen, parallele und senkrechte Komponente bestimmen. Vektor v1 = c1.getVelocity(), v2 = c2.getVelocity(); Punkt s1 = c1.ziel().zentrum(), s2 = c2.ziel().zentrum(); //Die Massen der Objekte float m1 = c1.getMasse(), m2 = c2.getMasse(); Vektor normale = s1.nach(s2).normiert(); //Normale als Vektor von Zentrum 1 nach Zentrum 2, auf Länge 1 normiert. //Parallelkomponenten der Geschwindigkeiten (zur Normalen) Vektor v1p = normale.multiplizieren(v1.skalarprodukt(normale)), v2p = normale.multiplizieren(v2.skalarprodukt(normale)); //Senkrechtkomponenten der Geschwindigkeiten (zur Normalen) Vektor v1s = v1.differenz(v1p), v2s = v2.differenz(v2p); //Parallelkomponenten -> EINDIMENSIONALES Problem, Standard. Vektor v1pNeu = v1p.multiplizieren(m1).summe(v2p.multiplizieren(2).differenz(v1p).multiplizieren(m2)).teilen(m1 + m2), v2pNeu = v2p.multiplizieren(m2).summe(v1p.multiplizieren(2).differenz(v2p).multiplizieren(m1)).teilen(m1 + m2); //Neue Geschwindigkeiten: jeweils neue Parallelkomponente + alte Senkrechtkomponente (unberührt!) c1.geschwindigkeitSetzen(v1pNeu.summe(v1s)); c2.geschwindigkeitSetzen(v2pNeu.summe(v2s)); } /** * Abarbeiten: Beeinflussbar auf unbeeinflussbar. * * @param beeinflussbar * Das beeinflussbare Element. * @param unbeeinflussbar * Das unbeeinflussbare Element. */ public static void ungleichlogik (MechanikClient beeinflussbar, MechanikClient unbeeinflussbar) { do { beeinflussbar.bewegen(beeinflussbar.getVelocity().gegenrichtung().multiplizieren(MechanikClient.DELTA_T)); } while (beeinflussbar.ziel().schneidet(unbeeinflussbar.ziel())); //stupid logic: nur parallel zum Fenster. Punkt zmov = beeinflussbar.ziel().zentrum(); BoundingRechteck bounds = unbeeinflussbar.ziel().dimension(); Vektor vneu = beeinflussbar.getVelocity().multiplizieren(unbeeinflussbar.getElastizitaet()); if (zmov.realX() <= bounds.x + bounds.breite / 2) { //Aktiv LINKS von Passiv if (zmov.realX() > bounds.x) { //Apprall oben / unten vneu = new Vektor(vneu.x, -vneu.y); } else { //Abprall rechts vneu = new Vektor(-vneu.x, vneu.y); } } else { //Aktiv RECHTS von Passiv if (zmov.realX() < bounds.x + bounds.breite) { //Abprall oben / unten vneu = new Vektor(vneu.x, -vneu.y); } else { //Abprall links vneu = new Vektor(-vneu.x, vneu.y); } } beeinflussbar.geschwindigkeitSetzen(vneu); } } }