BreakOut - "Rekursive", fehlerfreie Kollisionen
Übersicht

PacManiBetreff: BreakOut - "Rekursive", fehlerfreie Kollisionen |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Hallo zusammen,
ich habe bereits nach Vorschlägen zu einer vernünftigen / fehlerfreien Breakout-Kollisionserkennung (2D) gesucht, aber keine fehlerfreien bzw. problemlosen Berechnungen gefunden. Es geht ja wie in jedem Breakout darum, mit einem Ball Steine wegzuhauen, wenn der Ball einen Stein berührt, wird er von diesem natürlich wieder zurückgelenkt. Allerdings kann es passieren, dass von einem Frame zum nächsten mehr als nur die Kollision mit _einem_ Stein stattfindet, besonders wenn der Ball schnell ist. So könnte er zum Beispiel genau in die Ecke von zwei anliegenden Steinen treffen und dadurch zwei Kollisionen haben. Oder aber er jagt gerade durch einen Tunnel unzerstörbarer Steine mit rasender Geschwindigkeit und kann so vermutlich über 8 Kollisionen haben - es muss also eine rekursive Lösung her, die so lange prüft, bis die Strecke des Balls abgerechnet wurde. Ich dachte dabei theoretisch an folgendes: - Alle Steine durchlaufen und die am nahesten liegende Kollision herausfinden - Den Ball dahin verschieben und die bis dahin bereits abgelaufene Streckenlänge abziehen - Erneute Ermittlung der nahesten Kollision mit der übriggebliebenen Strecke, die der Ball in diesem Frame noch zurücklegen muss - Ball wieder dahin verschieben und Streckenlänge abziehen - Wiederholen so oft, bis die noch zu berechnende Streckenlänge 0 ist. Es klingt bestimmt schlau, da mit Vektoren zu arbeiten. Nun liegt meine Schulzeit bezüglich Vektorenrechnung aber schon weit zurück und ich habe nur noch Grundgedanken davon im Kopf. Daher nun letztlich folgende Fragen: - Was haltet ihr von obigen Lösungsansatz? - Sollte man ihn mit Vektoren umsetzen oder geht dies auch leichter? - Könnt ihr mich Stichpunkte bzgl. benötigter Vektoren-Formeln (Kreuzprodukt? Punktprodukt? Ein Dschungel für mich, leider) nennen? - Umsetzung von Vektoren in Blitz? Mit Type? Vielleicht bin ich hier auch ein wenig denkfaul, aber ich recherchiere nun schon seit einigen Stunden und fand keine passenden Lösungsansätze ![]() Besten Dank für die Denkhilfen im Voraus, Gruß, Pac-Mani |
||
- Zuletzt bearbeitet von PacMani am So, Nov 13, 2011 17:33, insgesamt einmal bearbeitet
![]() |
ZEVS |
![]() Antworten mit Zitat ![]() |
---|---|---|
Ich weiß nicht, ob der Ball so schnell ist, dass er ganze Blöcke überspringt. Im Zweifelsfall mach einfach die FPS höher.
Die Kollision mit zwei Ecken brauchst du nicht miteinzubeziehen. Im nächsten Frame wird der Ball mit dem nächsten Block kollidieren (meinem empirischen Urteil nach finden sowieso nie zwei Kollisionen gleichzeitig statt. Versuch mal, einen Ball so gegen eine Wand zu werfen, dass er beide Wände gleichzeitig berührt). Die Vektorrechnung streng nach Newton's dritten: Zitat: Kräfte treten immer paarweise auf. Übt ein Körper A auf einen anderen Körper B eine Kraft aus (actio), so wirkt eine gleich große, aber entgegen gerichtete Kraft von Körper B auf Körper A (reactio)
http://de.wikipedia.org/wiki/N...hes_Gesetz Setze einfach für A den Ball und für B den Block ein und du hast es. ZEVS |
||
PacMani |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Aber was wenn der Ball in einem Frame zum nächsten Tatsächlich durch die Ablenkung zwei Kollisionen hat?
Ich klaue einfach mal dreist die Bilder einer anderen Seite hierzu: Ablenkung, wie man sie sich vorstellt: ![]() Aber nun befindet sich noch eine Wand (oder ein zweiter Stein) auf dem abgelenkten Weg des Balls. Würde man nicht erneut prüfen, würde der Ball hinter der Wand platziert werden: ![]() Korrekt wäre es natürlich nur so: ![]() |
||
![]() |
ZEVS |
![]() Antworten mit Zitat ![]() |
---|---|---|
Wenn du die FPS nur hoch genug hast, wird der Ball sich in einem Frame nicht schneller bewegen, als ein Block groß ist.
ZEVS |
||
PacMani |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Mein Spiel läuft bereits auf 60 FPS und kann sehr schnell werden. Genauer gesagt soll er theorethisch auch unbegrenzt schnell werden (natürlich nie unbegrenzt, aber eben schon nicht mehr verfolgbar).
Einfach die FPS hochzusetzen würde ich wie ein Rumoperieren am kranken Patienten empfinden. Vor allem, wenn der PC kurz ruckelt gibt es sofort fehlerhafte Berechnungen. Gerade weil mein Spiel auch auf etwas älteren Rechnern laufen soll die nur 30 FPS schaffen (der Ball bewegt sich dann trotzdem gleich schnell). Ein altes Breakout-Spiel von 1999 hat das auch geschafft, der mathematische Gedanke ist ja sicherlich auch nicht uninteressant ![]() |
||
![]() |
ZEVS |
![]() Antworten mit Zitat ![]() |
---|---|---|
OK, du willst also die harte, mathematische, spannende Tour.
1. Kollisionsberechnung: Du musst bei der Flugbahn den minimalen Abstand zu allen in Frage kommenden Blöcke berechnen, und schauen, ob dieser kleiner ist, als der Ball. Wenn ja, kommt es zu einer umfassenden Berechnung der Kollisionspunkte. Das Problem ist ja, dass der Bereich, in dem es zur Kollision kommt, ein Rechteck mit abgerundeten Ecken ist, also eine Form, die sich jeder ganzheitlichen mathematischen Berechnung entzieht. Für die Seiten ist der naheste Punkt zwangsläufig an einem der Enden, wenn es keine Linienkollision gibt, das geht relativ einfach. Bei festgestellter Kollision kannst du mit Strahlensätzen arbeiten, wenn du die Blockkante und Flugbahn als Strahlen/Geraden interpretierst und so die exakte Kollision anhand des Radius' bestimmmen. Für die Ecken musst du zur Kollisionsprüfung den Abstand Ecke-Flugbahn berechnen. Die Berechnung des exakten Kollisionspunktes wird wohl auf ein Gleichungssystem hinauslaufen, dass du allgemein löst und in das Programm eingibst. 2. Nach Berechnung der Kollisionspunkte getreu Newton's dritten den Ball ablenken. 3. Wenn dir das ganze zu langsam wird (besonders bei vielen Blöcken) kannst du überlegen, ob du evtl. einen räumlichen Index implementierst. Das ist eine Art Karte, wo zu jedem Bereich gespeichert ist, welche Blöcke drin sind. Damit kannst du alle Blöcke, die keinen Bereich mit dem Ball teilen, vorab als nicht kollidierend ignorieren. Du kannst auch mehr Frames berechnen, als zu anzeigst. Benötigt weniger Zeit, hat aber die selben Prinzipprobleme. ZEVS |
||
![]() |
BladeRunnerModerator |
![]() Antworten mit Zitat ![]() |
---|---|---|
Du hast eine Geschwindigkeit v (Pixel pro frame) und den EinheitsVektor Z der in die aktuelle Richtung deines Balles zeigt.
Nun prüfst Du ob zwischen deinem aktuellen Punkt und v*Z ein Block liegt, beginnend beim Startpunkt. Ermittelst Du eine Kollision, wird v und die Distanz zwischen Startpunkt und Koillision gekürzt. Nun berechnest Du Z neu (und gegebenenfalls die änderung von v) anhand der Kollisionswinkel. Dann wiederholst Du das Spiel bis v 0 erreicht. Dann wird für das nächste Frame V erneut auf die aktuelle Geschwindigkeit gesetzt. |
||
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3 Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64 B3D BMax MaxGUI Stolzer Gewinner des BAC#48, #52 & #92 |
PacMani |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Das klingt gut. Wie ermittle ich aber die Distanz zwischen Startpunkt und Kollision, um sie dann von v abzuziehen? Ist vermutlich eine banale Frage, sorry ![]() |
||
![]() |
ZEVS |
![]() Antworten mit Zitat ![]() |
---|---|---|
Code: [AUSKLAPPEN] Sqr((startX-KollisionX)^2+(startY-KollisionY)^2)
ZEVS |
||
PacMani |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Es lag mir auf der Zunge... hatte ich doch sogar schonmal benutzt in einem anderen Spiel.
Tut mir Leid ![]() Aber danke! |
||
PacMani |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Ich habe eben versucht, es umzusetzen.
Allerdings fiel mir dabei ein, dass ich beim Durchlaufen der einzelnen Steine auf jeden Fall die am nahesten liegende Kollision berechnen muss. Meine Funktion läuft die Steine von oben links nach unten rechts durch. Bewegt sich der Ball z.B. nach oben links und zwar schneller pro Frame als ein Stein groß ist, so würde er mit Steinen oben links kollidieren, obwohl unten rechts vor diesen Steinen auf dem Kollisionsweg noch weitere Steine liegen, mit dem der Ball eigentlich kollidieren müsste. Ich dachte mir also, ich ermittle die kürzeste Distanz zu jedem kollidierten Stein. Aber die Funktion ist grässlich aufwendig, groß und unübersichtlich geworden. Da fiel mir folgendes ein, was ich auch mal kurz illustrieren kann: Ich durchlaufe das Steinlevelarray einfach von der Position des Balls ausgehend (gelbe Pfeile) in die Richtung in die der Stein fliegt (grün). Dann sollte ich doch definitiv den am nahesten kollidierenden Stein finden, müsste nicht alle Steine durchlaufen (sondern nur einen Teil) und könnte bei der ersten ermittelten Kollision die For-Schleifen verlassen. Sehe ich das richtig oder fällt euch sofort etwas dagegensprechendes ein? ![]() |
||
![]() |
ZEVS |
![]() Antworten mit Zitat ![]() |
---|---|---|
Nein, wenn du die Wand-Kollision nicht aus den Augen verlierst.
Vielleicht solltest du dir auch alle Blöcke, die überhaupt erreicht werden können (also nicht eingebaut sind) extra speichern. Das sollte weitere Performancesteigerungen bringen. ZEVS |
||
PacMani |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Du meinst, Positionen, an denen sich keine Steine befinden, auslassen? Daran dachte ich auch schon, ist aufgrund der momentanen Implementierung aber leider zu aufwändig (leere Positionen haben einfach ID = 0).
So eine ähnliche Umstellung habe ich letztendlich auch bei einem Bomberman-Klon gemacht ![]() Die Wände berechne ich separat als erstes. |
||
![]() |
ZEVS |
![]() Antworten mit Zitat ![]() |
---|---|---|
Nein, nicht so ganz.
Du speicherst zu jedem Block, ob er irgendeine freie Fläche hat, mit der der Ball ihn erreichen könnte. Die Berechnung braucht zwar evtl. ihre Zeit, da aber bezogen auf alle Frames nur relativ selten ein Block zerstört wird, kannst du hier Performance sparen. Wenn du nämlich weißt, dass der Ball überhaupt nicht an den Block kommt, sparst du dir die Kollision. Des weiteren kannst du, wenn du die Flugbahn als lineare Gleichung betrachtest, viele Blöcke daran ausschließen, dass sie zu weit neben der Gerade liegen. ZEVS |
||
PacMani |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Ok, das wäre natürlich aufwendiger.
Aber die Idee mit nicht erreichenden Blöcken hat mich zu folgende Idee gebracht: Nur so weit wie die Flugbahn in Höhe und Breite reicht, die Steine zurückverfolgen: ![]() Klar, wenn der Ball sehr schnell wird, werden auch wieder (fast exponentiell ansteigend) mehr Blöcke berechnet werden müssen ![]() |
||
PacMani |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
So, nun habe ich zumindest den Durchlauf der Blöcke und die erste Kollisionsabfrage fertig. Jetzt muss ich es nur noch so umbasteln, dass die Kollisionsprüfung rekursiv mit der verbleibenden Distanz abgefragt wird (man sieht im Screenshot einen fälschlicherweise dezimierten Block, der eigentlich garnicht erreichbar war).
Hier der bisherige noch grauenhafte und nicht optimierte Code: BlitzBasic: [AUSKLAPPEN] Function UpdateBall(Instance.Ball) ![]() |
||
Übersicht


Powered by phpBB © 2001 - 2006, phpBB Group