Kollisionsproblem
-
Na schön, du willst das also mit LinienSegmentKollisionsAbfragen realisieren, ja? Dann mach das doch auch mit Vektoren, und nicht mit linearen Funktionen, das sind nämlich Geraden, was du brauchst sind Strecken, mach das also lieber mit Vektoren, glaub mir. Du hast den Vektor, der die Position deines Balles speichert und den Vektor in der sich bewegt. Damit weißt du, auf welchem Strahl sich dein Ball in diesem Frame bewegt. Wenn dieser Strahl ein Hindernis schneidet, dann kollidiert auch deine Kugel damit. Allerdings tut er das auch, wenn der Strahl weniger als der Radius des Balles von einer Ecke entfernt ist. Dann sollte auch die Berechnung des resultierenden Winkels problematisch werden, denn spätestens dort machts ohne Vektorrechnung keinen Spaß mehr... Allerdings kann man diese ganze Rechnerei einsparen, wenn man den Ball nicht zu schnell macht, und die Steine nicht zu klein. Dann muss man allerdings Kompromisse finden. Also bleib bei deiner Idee mit der SegmentKollision, aber verzettel dich nicht, denn deine if-else-Konstrukte sind weder sehr übersichtlich, noch funktional. Das geht viel schöner, dann findet man auch die Fehler leichter...
mfG D1B
PS: Ich will dir wirklich helfen...
Edit: Was zum Beispiel sollen die TimeVariablen, die braucht man gar nicht. Und warum setzt du die bools links, rechts etc. am Ende wieder auf false, die sind doch nur lokal?
[ Dieser Beitrag wurde am 23.02.2003 um 15:29 Uhr von D1BAKEL editiert. ]
-
das mit den bools ist wirklich überflüssig.
Aber was mich jetzt noch interessieren würde ist, wie ich Vektoren erstelle. Ich habe nämlich bisher immer mit dieser Variante der x und y zerlegung programmiert.
Ich hoffe du kannst mir helfen.
Und das mit den Timern das mache ich, da es ja auch sein kann, dass der Ball zwei Geraden während eines Frames trifft. und dann überprüfe ich welche er früher trifft.
Außerdem will ich, nachdem die Kollisionserkennung funktioniert, auch noch perfekte Physik ins Spiel bringen, und dazu benötige ich die Zeit.
Denn wenn der Ball z.B. zum Zeitpunkt 0.2 Trifft dann muss er ja noch 0.8*Bewegung in die neue Richtung bewegt werden.Vielen Dank
-
Das mit der perfekten Physik ist ein nahezu unmöglicher Ansatz, besonders wenn mann ihn von Null auf Hundert startet, denn stell dir nur folgendes vor: Der Ball ist so schnell, dass er ein einem Frame 12 Blöcke trifft, dann musst du als Programmierer folgendes machen: Du musst rauskriegen, mit welchem Block er kollidiert, dann die Richtug entsprechend ändern und dann kommt die nächste Kollision, dann wieder Richtung usw., ist es das, was du willst? Warum sollte er denn so schnell sein? Wenn du nen einigermaßen vernünftigen Rechner hast, brauchst du das alles gar nicht. Dann reicht es (bis zu einer bestimmten Geschwindigkeit) einfach zu bewegen, kontrollieren und korrigieren. Jeder Programmierer träumt davon (oder hat es schonmal), eine perfekte PhysikEngine zu schreiben, aber früher oder später kommst du zu der Einsicht, dass es nicht unsere Aufgabe ist, die Realität abzubilden, sondern sie zu simulieren. Je nach gewünschter Genauigkeit ist damit auch Aufwand verbunden, aber von einer perfekten PhysikEngine wagt hier wohl keiner zu reden. Man vereinfacht immer, schon in der Schule wird es einem vereinfacht dargestellt. Uns wird erzählt, dass bei einer Kugel (/Licht), die gegen eine Wand prallt, Einfallswinkel gleich Reflexionswinkel ist, dass da aber Elastizität im Spiel ist, ist uns egal, das brauchen wir nicht. Es ist unsere Aufgabe, diese Dinge auf ein Minimum zu reduzieren, wo wären wir heute, wenn wir alles realistisch haben wollten. Dann gäbe es keine vorbeleuchteten Texturen oder Kollisionsmaps, nein, es gäbe nicht die Spiele, von denen wir träumen. Vergiss also die perfekte Engine und strebe lieber der nach, die realistisch aussieht und trotzdem noch schnell ist. Auch brauch man keine unendlich schnellen Bälle, denn wenn sie sich pro Frame so weit bewegen, dass unser Gehirn seinen Weg gar ncith mehr erahnen kann, sinkt die Aunschauligkeit und damit auch die Attraktivität deines Programmes. Denk nochmal drüber nach, ich werd mir was für dein Kollisionsproblem überlegen...
bis dann
D1B
-
Thx an D1BAKEL
Aber du hast noch etwas vergessen: Die Reibungskraft.
Vielen Dank dass du dir die Mühe machst und mir hilfst.
-
Kein Problem, jetzt kommt auch was konstruktives (die Funktionen SetX() und SetY() müssen noch initialisiert werden und Ball_Radius gibts auch noch nichts):
void Block::Collision( Sprite* Ball, PointManager* PM, DirectXAudio* DXAudio, Sprite* Funken ) { for( int i = 0; i < BlocksAnzahl; ++i ) { if( !daweg[i] ) continue; if( Blocks[i].RX() - Ball_Radius > Ball->RX() && Blocks[i].RX() - Ball_Radius > Ball->RX() + Ball->StepX ) continue; if( Blocks[i].RY() - Ball_Radius > Ball->RY() && Blocks[i].RY() - Ball_Radius > Ball->RY() + Ball->StepY ) continue; if( Blocks[i].RX() + Ball_Radius < Ball->RX() - Block_Width && Blocks[i].RX() + Ball_Radius < Ball->RX() - Block_Width + Ball->StepX) continue; if( Blocks[i].RY() + Ball_Radius < Ball->RY() - Block_Height && Blocks[i].RY() + Ball_Radius < Ball->RY() - Block_Height + Ball->StepY) continue; daweg[i] = false; double yD, x; if( Ball->StepY ) yD = -Ball->RY() - Ball_Radius + Blocks[i].RY(); else yD = Ball->RY() - Ball_Radius - Blocks[i].RY() - Block_Height; x = yD * Ball->StepX / Ball->StepY + Ball->RX(); if( x < Blocks[i].RX() ) { Ball->SetX( 2 * x - Ball->RX() - Ball->StepX; Ball->StepX *= -1; } else if( x > Blocks[i].RX() + Block_Width ) { Ball->SetX( 2 * x - Ball->RX() - Ball->StepX + 2 * Block_Width ); Ball->StepX *= -1; } else { Ball->SetY( 2 * yD + Ball->RY() - Ball->StepY ); Ball->StepY *= -1; } } }
Ich habs nicht getestet (wie auch), aber ich hab n gutes Gefühl, probiers aus, und sag obs funktioniert, bzw. wos Fehler gibt. Mach am besten nur einen einzigen Block und sieh dann ganz genau hin, obs ungenau ist. Diese Variante könnte weitere Fehler bergen, wenn der Ball zu schnell wird, probiers einfach aus...
mfG D1B
-
Ok, zuerst einmal danke für den riesen Aufwand.
Also es funzt leider nicht:
Die Kollision wird richtig erkannt.
Aber:
So bald der Ball einen Block trifft, wird er an die falsche Stelle gesetzt.
Die Richtung wird allerdings richtig verändert.
Ich dachte mir jetzt dass es nur an dem yD liegen kann.
Aber was ich nocht nicht verstehe ist, für was das yD überhaupt steht.
Ich denke auch, dass du davon ausgehst, dass ich die Koordinaten des Mittelpunkts des Balles speichere. Das tue ich allerdings nicht.
Ich komme darauf, da du bei der ersten Abfrage gleich von der X-Koordinate den Radius abziehst und dann mit der X-Koordinate des Balles vergleichst.
Jetzt wüsste ich noch gerne, was du genau bei yD berechnest.
Und warum du überhaupt
if(Ball->StepY)
abfrägst, denn der Ball kann sich, zumindest in meinem Programm nicht wagrecht bewegen, d.h. dass Ball->StepY nie gleich 0 sein kann.Nochmals vielen vielen Dank für den Aufwand.
Ich würde micht freuen wenn du mir das mit dem yD noch erklären würdes.
Thx
-
Also yD ist jeweils die Entfernung von der Position des Balles bis zum Objekt. Wenn der Ball über dem Objekt ist, ist es die Entfernung senkrecht zur oberen Kante, wenn er darunter ist zur unteren Kante. Wenn dur mir noch sagt, wie du den Ball gespeichert hast, kann ich dir die Funktion noch verfeinern. yD ist von daher wichtig, dass ich den Schnittpunkt über den zweiten Strahlensatz berechne, das bietet sich an, da die Blöcke alle in einem kartesischen Raster angeordnet sind. Hast du in deiner Ball-Struktur die untere linke Ecke gespeichert, oder welchen Punkt? Dann versteh ich auch, warum du mit dem Durchmesser gearbeitet hast, hätte mir eigentlich gleich auffallen sollen
Du hast gesagt, der Ball wird an die falsche Stelle gesetzt, ist diese Stelle weit von der richtigen Entfernt, oder liegt der Fehler nur in meiner falschen Auffassung von deinem Ball, ist die falsche Position als nur um einen Radius zur Seite und einen auf der Vertikalen verschoben, oder manchmal auch auf der ganz anderen Seite. If(Ball->StepY) ist dasselbe, wie if(Ball->StepY > 0), wenns also negativ ist, wird der elseZweig ausgeführt. Ich teste damit also, ob der Ball sich nach oben oder nach unten bewegt.
Schreib mir also, wie du den Ball speicherst, und erkläre, was du mit "an der falschen Stelle" meinst, dann werde ich mir die Funktion nochmal angucken...
mfG D1B
-
Also der Ball wird, wenn ziemlich genau in der Mitte des Blocks trifft richtig weiter geleitet, da ist es auch egal auf welcher Seite er trifft. Allerdings habe ich es bisher noch nicht geschafft, dass er von der unteren Seite richtig abprallt. Also geht es von jeder außer der unteren Seite, wenn er in der Mitte trifft. wenn er allerdings nicht in der Mitte trifft, dann hat er nach dem Treffer eine total falsche Position (kann bis zu 100 Pixel falsch sein, schätze ich mal).
Ich speichere die Koordinaten der linken oberen Ecke meines Balles.
Und das mit dem
if(Ball->StepY)
gut, dass ich das jetzt auch richtig weiß, denn so etwas kann man öfters gebrauchen.Vielen Dank nochmal
-
Gehe ich recht in der Annahme, dass dein KoordinatenUrsprung in der linken oberen Ecke ist? Ich bin nämlich von der unteren ausgegangen, werds nochma überdenken und überarbeiten...
-
Okay, dann versuchs mal hiermit (hab den Ball deinen Vorstellungen angeglichen und dafür gesorgt, dass er auch unten korrekt abprallt, hoffe ich jedenfalls...):
void Block::Collision( Sprite* Ball, PointManager* PM, DirectXAudio* DXAudio, Sprite* Funken ) { for( int i = 0; i < BlocksAnzahl; ++i ) { if( !daweg[i] ) continue; if( Blocks[i].RX() > Ball->RX() && Blocks[i].RX() > Ball->RX() + Ball->StepX ) continue; if( Blocks[i].RY() > Ball->RY() && Blocks[i].RY() > Ball->RY() + Ball->StepY ) continue; if( Blocks[i].RX() < Ball->RX() - Block_Width + Ball_Durchmesser && Blocks[i].RX() < Ball->RX() - Block_Width + Ball_Durchmesser + Ball->StepX) continue; if( Blocks[i].RY() < Ball->RY() - Block_Height + Ball_Durchmesser && Blocks[i].RY() < Ball->RY() - Block_Height + Ball_Durchmesser + Ball->StepY) continue; daweg[i] = false; double yD, x; if( Ball->StepY ) yD = -Ball->RY() + Blocks[i].RY(); else yD = Ball->RY() - Blocks[i].RY() - Block_Height; x = yD * Ball->StepX / Ball->StepY + Ball->RX() + Ball_Durchmesser / 2; if( x < Blocks[i].RX() ) { Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser ); Ball->StepX *= -1; } else if( x > Blocks[i].RX() + Block_Width ) { Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser + 2 * Block_Width ); Ball->StepX *= -1; } else { Ball->SetY( ( Ball->StepX ? 2 : -2 ) * yD + Ball->RY() - Ball->StepY + Ball_Durchmesser ); Ball->StepY *= -1; } } }
Zwei Sachen noch: Zum ersten würde ich mir echt überlegen, den Ball nicht doch im Mittelpunkt zu speichern, evtl. auch die Blöcke, und von dort aus dann die halbe Länge, der beiden Achsen, mir gefällt das jedenfalls besser, da sich dann nur Vorzeichen ändern und man leichter den Überblich behält... Naja, deine Entscheidung. Und dann wundere ich mich, warum die Funktion Collision() eine MemberFunktion von deiner Struktur Block ist! Ich fände sie fiel einleuchtender, wenn die Funktion Member von Ball wäre, dann bräuchtest du den schon mal nicht als Parameter übergeben. Statt dessen kannst du dann lieber einen Pointer auf dein Array mit den Blöcken übergeben, wäre sehr viel Stilvoller. Wie rufst du diese Funktion denn auf? Gibt es da einen Block, den du favorisierst, der immer die Kollision übernehmen darf, oder lässt du das jeden Block in jedem Frame machen??? :p
mfG D1B
-
Hallo,
Als erstes einmal ich bin jetzt auch angemeldet, nur damit du weißt, dass es immer noch ich bin.
Ok also dieser Code setzt jetzt die Koordinaten richtig, allerdings kommt es jetzt häufig vor, dass der Ball durch einen Block hindurchfliegt. Dabei liegt meine Geschwindigkeit nur bei 8.0 Pixel pro Frame.
Die Richtungen berechne ich dan mit sin und cos.
Und die Blockhöhe ist 20 Pixel und die Breite 40.
Das mit der Kollisionserkennung kommt folgendermaßen zustande:
Ich habe eine Klasse für alle Sprites als Manager geschrieben. und deshalb habe ich auch die Kollisionserkennung in der Blocksklasse.
Die Kollision mit dem Brett habe ich z.B. in der Brettklasse.
Ich hoffe du kannst mir noch weiter helfen mit meinem Problem.Auf jeden Fall noch einmal Großes Lob an dich.
Vielen Dank.
-
Hallo,
Also ich habe da gerade einen Fehler in deinen ersten if-Abfragen entdeckt:- bei der ersten frägst du nach der linken Seite ab, da muss der Ball_Durchmesser dazu
- bei der zweiten frägst du nach der oberen Seite ab, da muss der Ball_Durchmesser ebenfalls dazu
- bei der dritten frägst du nach der rechten Seite ab, da muss der Ball_Durchmesser nicht dazu
- bei der vierten frägst du nach der unteren Seite ab, da muss der Ball_Durchmesser nicht dazu.Dies ist so, da ich ja die Koordinaten der linken oberen Ecke des Balles speichere.
Das ist aber leider immer noch nicht die Lösung des Problems.
Nochmal Vielen Dank
Ich hoffe ich habe das richtig verstanden.
[ Dieser Beitrag wurde am 24.02.2003 um 17:48 Uhr von Zero0Cool editiert. ]
-
Okay, dann hier mein nächster Vorschlag (Ball_Radius wird nicht mehr genutzt, kann also verworfen werden):
void Block::Collision( Sprite* Ball, PointManager* PM, DirectXAudio* DXAudio, Sprite* Funken ) { for( int i = 0; i < BlocksAnzahl; ++i ) { if( !daweg[i] ) continue; if( Blocks[i].RX() < Ball->RX() - Block_Width && Blocks[i].RX() < Ball->RX() - Block_Width + Ball->StepX ) continue; if( Blocks[i].RY() < Ball->RY() - Block_Height && Blcoks[i].RY() < Ball->RY() - Block_Height + Ball->StepY ) continue; if( Blocks[i].RX() > Ball->RX() + Ball_Durchmesser && Blocks[i].RX() > Ball->RX() + Ball_Durchmesser + Ball->StepX ) continue; if( Blocks[i].RY() > Ball->RY() + Ball_Durchmesser && Blocks[i].RY() > Ball->RY() + Ball_Durchmesser + Ball->StepY ) continue; daweg[i] = false; double yD, x; if( Ball->StepY ) yD = -Ball->RY() + Blocks[i].RY(); else yD = Ball->RY() - Blocks[i].RY() - Block_Height; x = yD * Ball->StepX / Ball->StepY + Ball->RX() + Ball_Durchmesser / 2; if( x < Blocks[i].RX() ) { Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser ); Ball->StepX *= -1; } else if( x > Blocks[i].RX() + Block_Width ) { Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser + 2 * Block_Width ); Ball->StepX *= -1; } else { Ball->SetY( ( Ball->StepX ? 2 : -2 ) * yD + Ball->RY() - Ball->StepY + Ball_Durchmesser ); Ball->StepY *= -1; } } }
Ich hoffe, dass es jetzt klappt...
mfG D1B
-
Ok vielen Dank.
Aber es gibt leider immer noch das Problem mit dem SetX() und SetY(),
denn wenn ein Block getroffen wird, dann wird der Ball an die falsche stelle gesetzt.
Ich weiß allerdings nicht an was das liegen könnte. Denn wenn ich es nachrechne stimmt alles.
Außer dem SetX() bzw. SetY() dort bekomme ich noch das falsche Ergebnis raus.
Ich weiß allerdings noch nicht warum.
Ich werde das dann noch einmal überprüfen.Vielen Dank nochmals für den Code.
-
Rekaptitulieren wir noch einmal: Die Funktionen RX() und RY() von Ball und Block liefern jeweils die linke obere Ecke. Probier doch mal folgendes: Setze den Ball mit SetX(0); SetY(0) auf den Koordinatenursprung. Dann sollten der linke und der obere Bildschirm(Viewport-)Rand Tangentem an den Ball sein, er ist also so weit in der Ecke, wie (physikalisch) möglich. Wenn das klappt, scheinen die Set-Funktionen zu funktionieren. Über das Ergebnis dieses Versuches solltest du mich umgehend informieren... Setze für diesen Versuch ruhig mal StepX und StepY auf Null, um genau gucken zu können.
Gibt es denn noch Kollisionen, die er nicht erkennt, oder auf die er die Richtung in falscher Weise ändert? Wenn nicht ist das Problem fast gelöst...
mfG D1B
-
Ok ich habe jetzt einmal die letzten Abfragen ein wenig umgeschrieben, da es passiert ist, dass der Ball von links oben gekommen ist, dann rechts oben den Block getroffen hat und dann nach links unten abgeprallt ist. Und das kann ja wohl nicht Sinn der Sache sein, oder?
Ok hier jetzt einmal mein Verbesserungsvorschlag:if( x <= Blocks[i].RX() && Ball->StepX >= 0 ) { Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser ); Ball->StepX *= -1; } else if( x >= Blocks[i].RX() + Block_Width && Ball->StepX <= 0 ) { Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser + 2 * Block_Width ); Ball->StepX *= -1; } else { Ball->SetY( ( Ball->StepY ? 2 : -2 ) * yD + Ball->RY() - Ball->StepY + Ball_Durchmesser); Ball->StepY *= -1; }
Ok viel hat sich nicht geändert ist aber eben auch wichtig.
Das mit dem SetX() und SetY() bekomme ich einfach nicht hin.
Bitte hilf mir.Thx
Edit: Wenn ich bei SetX() und SetY() jeweils 0 angebe, dann befindet sich der Ball in der linken oberen Ecke des Bildschirms. und zwar so weit wie möglich.
[ Dieser Beitrag wurde am 24.02.2003 um 19:48 Uhr von Zero0Cool editiert. ]
-
Poste doch mal bitte die Struktur namens Sprite, das würde uns sehr weiterhelfen...
-
> von links oben gekommen ist, dann rechts oben den Block getroffen hat und dann nach links unten abgeprallt
Wie kannst du denn sehen, wo der Ball den Block trifft, dass passiert doch zwischen 2 Frames...
Nochwas: Weißt du eigentlich, was x ist?
-
Na ja also das mit dem Treffer da ich mir ja nur einen Block gemacht habe, kann ich schon ungefähr vorraus sagen, wo der Ball den Block treffen wird.
Und da habe ich eben gesehen, dass der Ball den Block rechts oben treffen muss.
Und trotzdem ist er nach links unten abgeprallt. Aber das ist jetzt nicht mehr.Das mit dem x ich denke, dass das x für die X-Koordinate steht, an der der Ball den Block treffen wird???
Wenn es nicht so ist, dann kläre mich bitte auf...
Ich weiß echt nicht mehr weiter...
Thx@D1BAKEL
-
Oh Mann, ich glaube, jetzt haben wirs. Es gab da noch zwei kleine Fehler: Der erste ist passiert, als ich die ganzen Radien entfernt hab, da hab ich ein Vorzeichen übersehen und es weggenommen, statt nen Durchmesser draus zu machen. Und der zweite Fehler ist mir ja so richtig peinlich, ein simpler Tippfehler: In der letzten Abfage darfs natürlich nicht Ball->StepX heißen, sondern muss Ball->StepY heißen, man will ja die vertikale Richtung wissen...
void Block::Collision( Sprite* Ball, PointManager* PM, DirectXAudio* DXAudio, Sprite* Funken ) { for( int i = 0; i < BlocksAnzahl; ++i ) { if( !daweg[i] ) continue; if( Blocks[i].RX() < Ball->RX() - Block_Width && Blocks[i].RX() < Ball->RX() - Block_Width + Ball->StepX ) continue; if( Blocks[i].RY() < Ball->RY() - Block_Height && Blcoks[i].RY() < Ball->RY() - Block_Height + Ball->StepY ) continue; if( Blocks[i].RX() > Ball->RX() + Ball_Durchmesser && Blocks[i].RX() > Ball->RX() + Ball_Durchmesser + Ball->StepX ) continue; if( Blocks[i].RY() > Ball->RY() + Ball_Durchmesser && Blocks[i].RY() > Ball->RY() + Ball_Durchmesser + Ball->StepY ) continue; daweg[i] = false; double yD, x; if( Ball->StepY ) yD = -Ball->RY() + Blocks[i].RY() - Ball_Durchmesser; else yD = Ball->RY() - Blocks[i].RY() - Block_Height; x = yD * Ball->StepX / Ball->StepY + Ball->RX() + Ball_Durchmesser / 2; if( x < Blocks[i].RX() ) { Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser ); Ball->StepX *= -1; } else if( x > Blocks[i].RX() + Block_Width ) { Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser + 2 * Block_Width ); Ball->StepX *= -1; } else { Ball->SetY( ( Ball->StepY ? 2 : -2 ) * yD + Ball->RY() - Ball->StepY + Ball_Durchmesser ); Ball->StepY *= -1; } } }
Ich möchte dich bitte, das gleich ma auszuprobieren, und mir zu sagen, obs jetzt endlich funktioniert, wenn nicht mit entsprechender Fehleranalyse...
mfG D1B
[ Dieser Beitrag wurde am 24.02.2003 um 20:19 Uhr von D1BAKEL editiert. ]