![]() |
Pong - Problem bei der Ballkollision
Also ich weiss das Thema Pong wurde hier schon oft behandelt, aber ich habe zu meinem Problem nichts gefunden!
Hier erstmal mein Code:
Delphi-Quellcode:
Also erstmal: das ist natuerlich alles noch nicht fertig!!!
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls; const Breite = 600; Hoehe = 400; type TForm1 = class(TForm) Spielfeld: TPaintBox; Button1: TButton; Timer1: TTimer; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Spielfeld_bemalen; procedure Tastendruck(Sender: TObject; var Key: Word; Shift: TShiftState); procedure Timer1Timer(Sender: TObject); procedure Ball_berechnen; private { Private declarations } public { Public declarations } end; var Form1: TForm1; Spielerposition: Array [1..2] of TPoint; Ballposition, Ballrichtung: TPoint; Spieler, Ball: TBitmap; implementation {$R *.dfm} //---------------------------------- //FORM CREATE //---------------------------------- procedure TForm1.FormCreate(Sender: TObject); begin Form1.DoubleBuffered := True; Randomize; end; //---------------------------------- //---------------------------------- //FUNKTION ZUFALLSRICHTUNG //---------------------------------- function Zufallsrichtung (i: Integer; j: Integer): TPoint; var x, y: Integer; begin x := (Random(i - 1) + 1) * 5; y := Random(j) * 5; case Random(2) of 0: x := x; 1: x := -x; end; case Random(2) of 0: y := y; 1: y := -y; end; Result := Point(x, y); end; //---------------------------------- //---------------------------------- //SPIELFELD VORBEREITEN //---------------------------------- procedure Spielfeld_vorbereiten; begin //Bilder laden Spieler := TBitmap.Create; Spieler.LoadFromFile('Spieler.bmp'); Ball := TBitmap.Create; Ball.LoadFromFile('Ball.bmp'); Ball.TransparentColor := RGB(255, 255, 255); Ball.TransparentMode := tmFixed; Ball.Transparent := True; //Startpositionen festgelegt Spielerposition[1] := Point(10, (Hoehe div 2 - 30)); Spielerposition[2] := Point((Breite - 10 - Spieler.Width), (Hoehe div 2 - 30)); Ballposition := Point((Breite div 2 - (Ball.Width div 2)), (Hoehe div 2 - (Ball.Height div 2))); Ballrichtung := Zufallsrichtung(5, 5); end; //---------------------------------- //---------------------------------- //SPIELFELD BEMALEN //---------------------------------- procedure TForm1.Spielfeld_bemalen; var i, x: Integer; begin //Spielfeld wird gelöscht Spielfeld.Canvas.Brush.Color := clGray; Spielfeld.Canvas.FillRect(rect(0, 0, Breite, Hoehe)); //Mittellinie Spielfeld.Canvas.Brush.Color := clBlack; x := 0; for i := 0 to Hoehe do begin Spielfeld.Canvas.FillRect(rect((Breite div 2 - 1), x, (Breite div 2 + 1), (x+2))); x := x + 6; end; //Spieler und Ball werden gezeichnet Spielfeld.Canvas.Draw(Spielerposition[1].X, Spielerposition[1].Y, Spieler); Spielfeld.Canvas.Draw(Spielerposition[2].X, Spielerposition[2].Y, Spieler); Spielfeld.Canvas.Draw(Ballposition.X, Ballposition.Y, Ball); end; //---------------------------------- //---------------------------------- //BUTTONKLICK //---------------------------------- procedure TForm1.Button1Click(Sender: TObject); begin Spielfeld_vorbereiten; Timer1.Enabled := True; Button1.Enabled := False; end; //---------------------------------- //---------------------------------- //TASTENDRUCK //---------------------------------- procedure TForm1.Tastendruck(Sender: TObject; var Key: Word; Shift: TShiftState); begin case Key of VK_Up: if (Spielerposition[1].Y - 10) > 0 then Spielerposition[1].Y := Spielerposition[1].Y - 10; VK_Down: if (Spielerposition[1].Y + 10 + Spieler.Height) < Hoehe then Spielerposition[1].Y := Spielerposition[1].Y + 10; VK_Left: if (Spielerposition[2].Y - 10) > 0 then Spielerposition[2].Y := Spielerposition[2].Y - 10; VK_Right: if (Spielerposition[2].Y + 10 + Spieler.Height) < Hoehe then Spielerposition[2].Y := Spielerposition[2].Y + 10; end; end; //---------------------------------- //---------------------------------- //TIMER //---------------------------------- procedure TForm1.Timer1Timer(Sender: TObject); begin Ball_berechnen; Spielfeld_bemalen; end; //---------------------------------- //---------------------------------- //BALL BERECHNEN //---------------------------------- procedure TForm1.Ball_berechnen; begin //Wand angestoßen? if Ballposition.Y <= 0 then Ballrichtung.Y := -Ballrichtung.Y; if Ballposition.Y >= Hoehe then Ballrichtung.Y := -Ballrichtung.Y; //Spieler angestoßen? if ((Ballposition.X <= Spielerposition[1].X + Spieler.Width) AND (Ballposition.Y + Ball.Height >= Spielerposition[1].Y) AND (Ballposition.Y <= Spielerposition[1].Y + Spieler.Height)) OR ((Ballposition.X + Ball.Width >= Spielerposition[2].X) AND (Ballposition.Y + Ball.Height >= Spielerposition[2].Y) AND (Ballposition.Y <= Spielerposition[2].Y + Spieler.Height)) then Ballrichtung.X := -Ballrichtung.X; //Gepunktet? //Sp1 if Ballposition.X <= 0 then Timer1.Enabled := False; Button1.Enabled := True; //Sp2 if Ballposition.X + Ball.Width >= Breite then Timer1.Enabled := False; Button1.Enabled := True; Ballposition := Point(Ballposition.X + Ballrichtung.X, Ballposition.Y + Ballrichtung.Y); end; //---------------------------------- end. Aber bei mir ist nun das Problem aufgetreten, dass der Ball bevor er die Richtung aendert oft schon auf dem Balken oder halb außerhalb des Spielfeldes ist, weil ich ja Pro Timer den Ball um zwischen 1 und 5 verschiebe! Hoffe ihr versteht was ich meine...wie wuerdet ihr das lösen? Und was habt ihr fuer Verbesserungsvorschlaege zu meinem Quelltext?? Bin noch nich solange am Arbeiten mit Delpih, machen es jetzt in der Schule aber unsern Lehrer kann man in der Pfeife rauchen also bring ichs mir selber bei... Dazu danke dann erstmal an dieses (wirklich hilfreiche) Forum! MfG, chicken! |
Re: Pong - Problem bei der Ballkollision
Was mir spontan an deinem Quelltext auffällt:
-den Bildschirm lieber im OnPaint der PaintBox aktualisieren und dies per Invalidate aufrufen -Offscreen-Bitmaps(siehe Suchfunktion/machst du schon teilweise) -statt die Spielerpositionen im KeyDown zu verändern, lieber im Timer (benögitgte Funktion: GetAsyncKeyState) Zu deinem Problem: Du könntest die Kugel bei der Kollision mit dem Spielfeldrand um 2mal soviel nach innen verschieben, wie sie aus dem Bildschirm hinausragt. |
Re: Pong - Problem bei der Ballkollision
Also danke erstmal fuer deine Antwort!!!
Kannst du deine ersten beiden Punkte vielleicht nochmal erlaeutern? Die versteh ich ehrlich gesagt nich so ganz ^^! Dann zum dritten Punkt, das werd ich mir ma ansehn, wenn ich nich klar komme melde mich mich nomma, wenns recht is =) Und zu deiner letzten Antwort: Du meinst also dass ich einfach bevor ich die Kugelposition erneuere die alte abfrage und dann einfach nur bis zum Rand "auffuelle"?! MfG Edit: Zum Thema GetAsyncKey - Soll ich dass dann für jede benötigte Taste abfragen oder hab ich das falsch verstanden??? Bei vielen Tasten waer das ja bisschen umstaendlich, igibts doch sicher ne einfachere Moeglichkeit!? |
Re: Pong - Problem bei der Ballkollision
Zitat:
Statt die Zeichen-Prozedur Spielfeld_bemalen im Timer aufzurufen, schlage ich vor stattdessen die Prozedur "Spielfeld.Invalidate" aufzurufen. Dies bewirkt, dass die PaintBox neu gezeichnet wird. Dadurch wird das Ereignis "OnPaint" der PaintBox aufgerufen. In diesem sollten dann deine Zeichenvorgänge geschehen (PaintBox auf Form anklicken, im OI auf Ereignisse gehen, ins Feld neben OnPaint doppelklicken, am Cursor im Quelltext dann den Zeichnen-Code einfügen). Zu 2: Es ist schneller, nicht alle Zeichen-Vorgänge direkt auf die Komponente (PaintBox) zu übertragen, sondern erst alles auf das Canvas eines Bitmaps im Speicher zu zeichnen und dies dann auf das Canvas der PaintBox zu kopieren. In ![]() Zitat:
Zitat:
Von diesen Berechnungen soll der Anwender später natürlich nichts mitkriegen, deswegen darf zwischendurch nicht neugezeichnet werden. Aber das Neuzeichnen sollte sowieso nur im Timer über Invalidate (s.o.) erfolgen. |
Re: Pong - Problem bei der Ballkollision
Hm also zum ersten...is das einfach nur besserer Stil oder funktioniert das schneller oder was steckt dahinter?
Und dann zum letzten Punkt: Wie verhindere ich denn dass der Anwender das nicht mitkriegt? Habe ja jetzt im Timer dass er immer nachdem die Position neu berechnet wurde zeichnet, weswegen der Anwender ja auch sieht dass der Ball kollidiert! Danke fuer deine schnellen Antworten! |
Re: Pong - Problem bei der Ballkollision
Zitat:
Ich glaube es gibt noch viel mehr Vorteile, welche genau weiß ich auch nicht. Zitat:
|
Re: Pong - Problem bei der Ballkollision
OK, das mit onPaint und Invalidate hab ich soweit hinbekommen, flackert jezz sogar viel weniger, aber erst auf ein Bitmap zu zeichnen bekomme ich noch nicht wirklich hin >.<
Und dann zu der Sache mit der Kollision, meinste das so?
Delphi-Quellcode:
//----------------------------------
//BALL BERECHNEN //---------------------------------- procedure TForm1.Ball_berechnen; begin //Wand angestoßen? if Ballposition.Y <= 0 then Ballrichtung.Y := -Ballrichtung.Y; if Ballposition.Y >= Hoehe then Ballrichtung.Y := -Ballrichtung.Y; //Spieler angestoßen? if ((Ballposition.X <= Spielerposition[1].X + Spieler.Width) AND (Ballposition.Y + Ball.Height >= Spielerposition[1].Y) AND (Ballposition.Y <= Spielerposition[1].Y + Spieler.Height)) OR ((Ballposition.X + Ball.Width >= Spielerposition[2].X) AND (Ballposition.Y + Ball.Height >= Spielerposition[2].Y) AND (Ballposition.Y <= Spielerposition[2].Y + Spieler.Height)) then Ballrichtung.X := -Ballrichtung.X; //Gepunktet? //Sp1 if Ballposition.X <= 0 then begin Timer1.Enabled := False; Button1.Enabled := True; end; //Sp2 if Ballposition.X + Ball.Width >= Breite then begin Timer1.Enabled := False; Button1.Enabled := True; end; if Ballposition.X < Spielerposition[1].X + Spieler.Width then Ballposition.X := Spielerposition[1].X + Spieler.Width; if Ballposition.X + Ball.Width> Spielerposition[2].X then Ballposition.X := Spielerposition[2].X - Ball.Width; if Ballposition.Y < 0 then Ballposition.Y := 0; if Ballposition.Y + Ball.Height > Hoehe then Ballposition.Y := Hoehe - Ball.Height; Ballposition := Point(Ballposition.X + Ballrichtung.X, Ballposition.Y + Ballrichtung.Y); end; //---------------------------------- Sorry dass ich das net so schnell versteh :dp: Edit: Mir is grad aufgefallen dass da so noch einige Fehler drin sind, aber generell zu diesem Teil
Delphi-Quellcode:
So meinst du das?
if Ballposition.X < Spielerposition[1].X + Spieler.Width then
Ballposition.X := Spielerposition[1].X + Spieler.Width; if Ballposition.X + Ball.Width> Spielerposition[2].X then Ballposition.X := Spielerposition[2].X - Ball.Width; if Ballposition.Y < 0 then Ballposition.Y := 0; if Ballposition.Y + Ball.Height > Hoehe then Ballposition.Y := Hoehe - Ball.Height; Und dann noch was :D Kann man die Kollision nich irgendwie eleganter lösen??? Weil wenn ich hitnerher noch berechnen will, dass wenn der Ball weiter rechts aufprallt er beschleunigt/verlangsamt wird, dann verlier ich mich ja in endlosen IF-Bedigungen... Hab schon die SuFu dazu benutzt und bin öfter auf so Wurzel und Quadrat Funktionen gestoßen, hab aber echt keinen Plan was ich damit anstellen soll =) |
Re: Pong - Problem bei der Ballkollision
Mir ist noch ein Vorteil von OnPaint+Invalidate eingefallen: Das verhindert, dass man das Bild auf einer PaintBox nicht wegradieren kann, indem man andere Fenster drüberschiebt.
Wo liegt denn dein Problem mit dem Zeichnen auf ein Speicher-Bitmap? Wie hast du's denn versucht umzusetzen und was klappte dabei nicht? Zeig mal Code! In Z. 9 deines Codes ist noch ein Fehler: BallPosition.Y + Ball.Height müsste es heißen. Den Teil unten in deinem Code kann man im Grunde genommen so lösen. Was du generell noch an deinem Code verbessern könntest: Einige Bedingungen fragst du mehrmals ab. Du könntest an vielen Stellen mehrere Bedingungen zusammenfassen. In Zeile 7-10 passiert bei Zwei verschiedenen Bedingungen (von denen sowieso nur eine stimmen kann) das gleiche. Du könntest sie mit "or" verbinden. Dann braucht die zweite nämlich gar nicht mehr gelesen zu werden, wenn die erste zutrifft. Das brächte minimalen Zeitgewinn. Zitat:
|
Re: Pong - Problem bei der Ballkollision
Aso, dass hab ich falsch erklaert mit dem rechts und links, ich meinte wenn er weiter rechts auf dem schlaeger aufprallt (vom schlaeger aus gesehn) also weiter unten/oben...
Dass im Quelltext vieles noch nicht stimmte hatte ich ja auch erwaehnt, hab ihn nochmal überarbeitet aber das funzt trotzdem noch nich so ganz...
Delphi-Quellcode:
Kann man die Kollision denn eleganter abfragen? Denn nebenbei is der Ball ja rund, und ich habe aber ein eckiges Bild, dh die Abfrage ist nicht wirklich genau!
//----------------------------------
//BALL BERECHNEN //---------------------------------- procedure TForm1.Ball_berechnen; begin Ballposition := Point(Ballposition.X + Ballrichtung.X, Ballposition.Y + Ballrichtung.Y); Kollision; end; //---------------------------------- //---------------------------------- //KOLLISION //---------------------------------- procedure TForm1.Kollision; var i: Integer; begin //Wand angestoßen? if Ballposition.Y <= 0 then begin Ballposition.Y := 0; Ballrichtung.Y := -Ballrichtung.Y; end; if Ballposition.Y + Ball.Height >= Hoehe then begin Ballposition.Y := Hoehe - Ball.Height; Ballrichtung.Y := -Ballrichtung.Y; end; //Spieler1 angestoßen? //Vorne angestoßen? if (Ballposition.Y + Ball.Height >= Spielerposition[1].Y) AND (Ballposition.Y <= Spielerposition[1].Y + Spieler.Height) then if Ballposition.X <= Spielerposition[1].X + Spieler.Width then begin Ballposition.X := Spielerposition[1].X + Spieler.Width; Ballrichtung.X := -Ballrichtung.X; end else //Unten/Oben angestoßen? if (Ballposition.X <= Spielerposition[1].X + Spieler.Width) AND (Ballposition.X + Ball.Width >= Spielerposition[1].X) then begin if (Ballposition.Y + Ball.Height >= Spielerposition[1].Y) AND (Ballposition.Y + Ball.Height <= Spielerposition[1].Y + Spieler.Height div 2) then begin Ballposition.Y := Spielerposition[1].Y - Ball.Height; Ballrichtung.Y := -Ballrichtung.Y; end; if (Ballposition.Y <= Spielerposition[1].Y + Spieler.Height) AND (Ballposition.Y >= Spielerposition[1].Y + Spieler.Height div 2) then begin Ballposition.Y := Spielerposition[1].Y + Spieler.Height; Ballrichtung.Y := -Ballrichtung.Y; end; end; //Spieler2 angestoßen? //Vorne angestoßen? if (Ballposition.Y + Ball.Height >= Spielerposition[2].Y) AND (Ballposition.Y <= Spielerposition[2].Y + Spieler.Height) then if Ballposition.X + Ball.Width >= Spielerposition[2].X then begin Ballposition.X := Spielerposition[2].X - Spieler.Width; Ballrichtung.X := -Ballrichtung.X; end else //Unten/Oben angestoßen? if (Ballposition.X + Ball.Width >= Spielerposition[2].X) AND (Ballposition.X <= Spielerposition[1].X + Spieler.Width) then begin if (Ballposition.Y + Ball.Height >= Spielerposition[2].Y) AND (Ballposition.Y + Ball.Height <= Spielerposition[1].Y + Spieler.Height div 2) then begin Ballposition.Y := Spielerposition[2].Y - Ball.Height; Ballrichtung.Y := -Ballrichtung.Y; end; if (Ballposition.Y <= Spielerposition[2].Y + Spieler.Height) AND (Ballposition.Y >= Spielerposition[1].Y + Spieler.Height div 2) then begin Ballposition.Y := Spielerposition[2].Y + Spieler.Height; Ballrichtung.Y := -Ballrichtung.Y; end; end; //Gepunktet? //Sp1 if Ballposition.X <= 0 then begin Timer1.Enabled := False; Button1.Enabled := True; end; //Sp2 if Ballposition.X + Ball.Width >= Breite then begin Timer1.Enabled := False; Button1.Enabled := True; end; end; //---------------------------------- Für die Sache mit dem aufs Bitmap zeichnen: Ich hab den Code jetzt nichtmehr, weil ich ihn wieder gelöscht habe als es nicht funktioniert hat, damit ich das Programm weiter benutzen kann. Also immer wenn ich das Programm gestartet habe dann hat er einfach alles schwarz gemalt und is abgestuertzt. Ich hab das ungefaehr so versucht umzusetzen:
Delphi-Quellcode:
Denke mal da warn mehrere Fehlre drin!
Spielfeldpuffer := TBitmap.Create;
Spielfeldpuffer.Width := Breite; Spielfeldpuffer.Height := Hoehe; //dann in der onPaint Funktion die Spieler und den Ball mittels Draw auf den Canvas des Bilds gemalt //(das war sicher einer der Fehler) ungefaehr so Spielfeldpuffer.Canvas.Draw(Spielerposition[1].X, Spielerposition[1].Y, Spieler); //und dann in die Paintbox damit Spielfeld.Canvas.Draw(0, 0, Spielfeldpuffer); MfG Edit: (Kollision ueberarbeitet) Bei der Kollision is mir noch was aufgefallen...die beiden Prüfungen ob oben oder vorne am Schlaeger angestoßen wird ueberpruefen beide nahezu dasselbe, deswegen aendert der Ball abundzu plötzlich ganz komisch die Richtung...hab aber keine Ahnung iwe ich nun unterscheiden soll ob der Ball von oben auf den Schlaeger oder von der Seite da drauf prallt (hoffe ich versteht was ich meine) O - - - - - oder - - - O - - Hoffe das hilft etwas zum Verstaendnis :D |
Re: Pong - Problem bei der Ballkollision
Zitat:
So könntest du das Zeichen machen:
Delphi-Quellcode:
Du brauchst also gar kein weiteres Bitmap im Hintergrund erstellen. Um das löschen des zuvorgezeichneten brauchst du dich auch nicht kümmern, weil das durch Invalidate + OnPaint sowieso gelöscht wird. Das, was du sonst noch so auf dem Bildschirm darstellen wirst, bleibt ja unverändert. Du könntest es also auf einem Image im Hintergrund darstellen, an dem du später nichts ändern brauchst. Du lädst einfach das Bitmap über den Objektinspektor in dein Image. Damit das Image sich nicht über der PaintBox befindet und diese verdeckt, kannst du auf es rechtsklicken und nach hinten setzen auswählen.
BitBlt(Spielfeld.Canvas.Handle, Spielerposition[1].X, Spielerposition[1].Y,
Spieler.Width, Spieler.Height, Spieler.Canvas.Handle, 0, 0, SRCCOPY); BitBlt(Spielfeld.Canvas.Handle, Spielerposition[2].X, Spielerposition[2].Y, Spieler.Width, Spieler.Height, Spieler.Canvas.Handle, 0, 0, SRCCOPY); BitBlt(Spielfeld.Canvas.Handle, Ballposition.X, Ballposition.Y, Ball.Width, Ball.Height, Ball.Canvas.Handle, 0, 0, SRCCOPY); |
Re: Pong - Problem bei der Ballkollision
Ok, das klappt zwar soweit, aber dann is miene Transparent Color sichtbar... :/
|
Re: Pong - Problem bei der Ballkollision
Zitat:
Oder aber: Wie sehen die beiden Bitmaps Spieler und Ball denn aus? Wenn sie nicht allzu kompliziert sind, könntest du sie ja auch über die Canvas-Routinen selbst zeichnen. Dann erstellst du ein OffScreen-Bitmap, kopierst darauf zunächst den Bildschirmausschnitt des Images, auf den du später kopierst, zeichnest auf das OffScreen-Bitmap mit den Canvas-Routinen den Ball bzw. Spieler und kopierst das OffScreen-Bitmap anschließend auf die PaintBox. Musst du gucken, was schneller geht. Ich weiß nur, dass Tranzparenz im allgemeinen ziemlich ausbremst. |
Re: Pong - Problem bei der Ballkollision
Den Teil hab ich schon wieder nich verstanden :D
Also der Ball sieht imho noch nich so kompliziert aus aber will den spaeter vielleicht nochma aendern wenn alles funktioniert! Also das is ja auch erstma egal, laeuft so ja auch...nur an der Kollisionsabfrage haperts echt nochn bissl! Versuche das nun mit IntersectRect..das klappt aber irgendwie noch nich so ganz,daran arbeite ich nun erstmal weiter... Könntest du mir das mit dem "getAsyncKeyState" nochmal genauer erklaeren? Also ich rufe dass dann fuer jede Taste die ich benutzen will auf oder wie oder was? :wall: :mrgreen: |
Re: Pong - Problem bei der Ballkollision
Zitat:
Zitat:
Zitat:
|
Re: Pong - Problem bei der Ballkollision
Aso gut dann weiss ich jetzt wie, zu derm Kollisionsding:
Bei der "runden" Kollisionsabfrage bin ich noch garnicht... :D Ich versuch grad erstmal das so hinzukriegen, aber ich versuch erstmal weiter, mein Kopf raucht schon :D Danke fuer deine Hilfe |
Re: Pong - Problem bei der Ballkollision
Ich hab mir grad ueberlegt die Bewegung nich wie vorher zu berechnen sondern mittels der Steigung, dh ich rechne richtung.y / richtung.X und verschiebe y jedesmal wenn ich x um einen verschiebe um soviel...
was haltet ihr davon? sinnvoller? so könnte man die kollisionen genauer abfragen glaube ich...aber die umsetzung is glaub ich dochn bisschen kompliziert, waer ja dann mit extended nichmehr nur mit integer .... |
Re: Pong - Problem bei der Ballkollision
Ok, also habe nun nochmal komplett von vorne angefangen und erstmal nur mit einem Spielerbrett...
Der Stand der Dinge sieht so aus, dass der Ball von allen Waenden abprallt (bis auf die hinter ihm, dann is das Spiel vorbei) und der Ball einen Drall bekommt, jenachdem auf welcher Seite vom Schlaeger er aufprallt! Hier der Quelltext:
Delphi-Quellcode:
Wie immer bin ich offen fuer konstruktive Kritik und Vorschlaege =)
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, Math; const Breite = 300; Hoehe = 300; type TForm1 = class(TForm) PaintBox1: TPaintBox; Timer1: TTimer; Left: TImage; Right: TImage; Shape1: TShape; procedure Spielfeld_bemalen(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Spielstart; procedure Ball_berechnen; procedure Tastendruck(Sender: TObject; var Key: Word; Shift: TShiftState); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation type TSpieler = record X, Y: Integer; Bmp: TBitmap; end; type TBall = record X, Y: Integer; Bmp: TBitmap; Richtung: TPoint; end; var Spieler: TSpieler; Ball: TBall; {$R *.dfm} procedure TForm1.Spielfeld_bemalen(Sender: TObject); begin PaintBox1.Canvas.Brush.Color := clBlack; Paintbox1.Canvas.Pen.Color := clBlack; PaintBox1.Canvas.Rectangle(0, 0, Breite, Hoehe); PaintBox1.Canvas.Draw(Spieler.X, Spieler.Y, Spieler.Bmp); Paintbox1.Canvas.Draw(Ball.X, Ball.Y, Ball.Bmp); end; procedure TForm1.Timer1Timer(Sender: TObject); begin //Tastendrücke if GetAsyncKeyState(VK_Left) < 0 then if Spieler.X > 0 then Spieler.X := Spieler.X - 10; if GetAsyncKeyState(VK_Right) < 0 then if Spieler.X + Spieler.Bmp.Width < Breite then Spieler.X := Spieler.X + 10; PaintBox1.Invalidate; Ball_berechnen; end; procedure TForm1.FormCreate(Sender: TObject); begin Form1.DoubleBuffered := True; Randomize; Timer1.Enabled := False; end; procedure TForm1.Spielstart; begin Spieler.Bmp := TBitmap.Create; Spieler.Bmp.LoadfromFile('Spieler.bmp'); Spieler.X := Breite div 2 - (Spieler.Bmp.Width div 2); Spieler.Y := 0; Ball.Bmp := TBitmap.Create; Ball.Bmp.LoadFromFile('Ball.bmp'); Ball.Bmp.TransparentColor := RGB(255, 0, 216); Ball.Bmp.Transparent := True; Ball.X := Breite div 2 - Ball.Bmp.Width div 2; Ball.Y := Hoehe div 2 - Ball.Bmp.Height div 2; Ball.Richtung.X := 0; Ball.Richtung.Y := -(Random(9) + 3); end; procedure TForm1.Ball_berechnen; var R0, R1, R2: TRect; i: Integer; begin Ball.X := Ball.X + Ball.Richtung.X; Ball.Y := Ball.Y + Ball.Richtung.Y; R1 := Rect(Ball.X - 1, Ball.Y - 1, Ball.X + Ball.Bmp.Width + 1, Ball.Y + Ball.Bmp.Height + 1); R2 := Rect(Spieler.X, Spieler.Y, Spieler.X + Spieler.Bmp.Width, Spieler.Y + Spieler.Bmp.Height); //Schläger angestoßen oder gepunktet? if Ball.Y <= 0 + Spieler.Bmp.Height then //Schläger angestoßen if IntersectRect(R0, R1, R2) = True then begin Ball.Y := Spieler.Bmp.Height; i := (Ball.X + (Ball.Bmp.Width div 2)) - (Spieler.X + (Spieler.Bmp.Width div 2)); Ball.Richtung.X := Ball.Richtung.X + (i div 5); Ball.Richtung.Y := -Ball.Richtung.Y; end else //Gepunktet begin Timer1.Enabled := False; Showmessage('ITS OVER!'); end else //Wand angestoßen? if Ball.X <= 0 then begin Ball.X := 0; Ball.Richtung.X := -Ball.Richtung.X; end else if Ball.X + Ball.Bmp.Width >= Breite then begin Ball.X := Breite - Ball.Bmp.Width; Ball.Richtung.X := - Ball.Richtung.X; end else if Ball.Y + Ball.Bmp.Height >= Hoehe then begin Ball.Y := Hoehe - Ball.Bmp.Height; Ball.Richtung.Y := - Ball.Richtung.Y; end; end; procedure TForm1.Tastendruck(Sender: TObject; var Key: Word; Shift: TShiftState); begin case Key of VK_Return: begin Spielstart; Timer1.Enabled := True; end; end; end; end. Achja und hat noch wer ne Idee wie ich realisieren kann, dass der Ball auch an der rechten und linken Kante vom Schlaeger abprallen kann? Momentan ist es so, dass sobald er hinter der Vorderkante des Schlaegers landet das Spiel zuende ist, weil ich Probleme hatte zu prüfen ob der Ball nun auf die Kante oder Vorne aufgekommen ist (wie auch schon in einem meiner letzten Posts beschrieben). MfG |
Re: Pong - Problem bei der Ballkollision
Die ersten 3 Anweisungen in Spielfeld_bemalen kannst du dir sparen, wenn du einfach im Objektinspektor Form1.Color auf clBlack stellst. Das Canvas der PaintBox wird vorm OnPaint sowieso gelöscht.
Zitat:
|
Re: Pong - Problem bei der Ballkollision
Liste der Anhänge anzeigen (Anzahl: 1)
Ja verliern tut er trotzdem, aber wenn beim FIFA der Ball ins Tor geht sieht man das auch ;-)
Nein also natuerlich funktioniert das so auch aber irgendwann treten ja auch ma Situationen auf wo das nichtmehr so ist und ich will ja was aus dem Projekt lernen ;-) Danke für deine Antwort ich guck nochma im Forum! Edit: Also habe zu meinem speziellen Problem nichts gefunden im Forum ich haeng mal ne Grafik zur Verdeutlichung dran! |
Re: Pong - Problem bei der Ballkollision
Naja, wann gibt es eine Kollision mit der senkrechten Kante? Wenn der Mittelpunkt des Kreises oberhalb der unteren Kante des Schlägers ist und sich der linke (rechte) Rand des Kreises links (rechts) von der rechten (linken) Kante des Schlägers befindet.
|
Re: Pong - Problem bei der Ballkollision
Also entweder ich hab nur n totales Brett vorm Kopf...
Ne also mein Problem is ja, dass ich Pro Ball_berechnen Prozedur die ich aufrufe den Ball ja z.B. um X + 50 und Y + 70 oder sowas verschiebe, wenn danach der Ball schon mitten im Spielbrett ist, kann ich nichtmehr prüfen auf welcher Kante er denn aufgekommen waere...verstehste was ich meine? ^^ Kansnte vielleicht mal n Codebeispiel schreiben wie dus machen wuerdestr, wenns nich zuviel verlangt is? |
Re: Pong - Problem bei der Ballkollision
Ach so meinste das. Aber bevor das passiert wäre der Ball doch schon längst von der Ecke abgeprallt (es sei denn, du bewegst ihn wirklich um 70 und 50 Pixel auf einmal). Um die Kollision mit der Ecke wirst du also nicht drum rumkommen. Es gibt 3 mögliche Kollisionen:
1. auf waargerechte Kante (einfach) 2. auf senkrechte Kante (auch einfach) 3. auf Ecke (schwerer) Aber wenn du's realistisch machen willst, musst du auch diese 3. Kollision berücksichtigen. |
Re: Pong - Problem bei der Ballkollision
Ja aber mein Problem besteht darin rauszufinden welche der 3 Moeglichkeiten nun zutrifft, denn der Ball wird ja wie erwaehnt jedes mal um viele Pixel bewegt (damit er auch Geschwindigkeit erhaelt)!
Gibts ne Moeglichkeit die Richtung der Bewegung als Winkel anzugeben nich als X und Y-Koordinate? dann waer das sicher n bissl einfacher!!! Haste sonst ne Idee wie das geht oder irgendwer anders? Danke vielmals dass du dich so damit auseinandersetzt =) |
Re: Pong - Problem bei der Ballkollision
Delphi-Quellcode:
Puh. Jetzt bin ich aber mit meinem Latein am Ende. :roteyes:
if ObererKugelrand.Y < Schlägerunterkante.Y then
if EntfernungVomKugelmittelpunktZurEcke < Kugel.Radius then Eckenkollision else if (Kugel.Mittelpunkt.X < RechteKanteSchläger.X) and (Kugel.Mittelpunkt.X > LinkeKanteSchläger.X) then Unterkantenkollision else if (LinkerKugelrand.X < RechterSchlägerrand.X) then RechteKantenKollision else if (RechterKugelrand.X > LinkerSchlägerrand.X) then LinkeKantenKollision else GarKeineKollision; |
Re: Pong - Problem bei der Ballkollision
Delphi-Quellcode:
if ObererKugelrand.Y < Schlägerunterkante.Y then
if EntfernungVomKugelmittelpunktZurEcke < Kugel.Radius then //dazu hab ich nochmal ne grafik angehaengt, funktioniert so glaub ich auch nich ganz richtig, das is mir aber erstmal auch nich das wichtigste... Eckenkollision else if (Kugel.Mittelpunkt.X < RechteKanteSchläger.X) and (Kugel.Mittelpunkt.X > LinkeKanteSchläger.X) then Unterkantenkollision else if (LinkerKugelrand.X < RechterSchlägerrand.X) then RechteKantenKollision else if (RechterKugelrand.X > LinkerSchlägerrand.X) then LinkeKantenKollision else GarKeineKollision; D.h. bei deinem Beispiel duerfte ich den Ball nie schneller als der Radius bewegen oder??? Sonst waer nichtmehr gewaehrleistet dass das alles funktioniert glaub ich.. Da muss es doch auch omnch ne andere Moeglichkeit geben oder??? |
Re: Pong - Problem bei der Ballkollision
Ich seh gerade, dass die Anfangsgeschwindigkeit des Balles schon zwischen 3 und 11 :shock: Pixel pro Timer-Intervall liegt. Ich würde stattdessen lieber das Timer-Intervall runterstellen (ca. 20). Dann kannst du eine niedrigere Startgeschwindigkeit wählen. Wie hoch ist denn dein Timer-Intervall?
Eine andere Möglichkeit gäbe es auch: Wenn die Geschwindigkeit z.B. 10 beträgt, in Ball_berechnen ne Schleife von 1 bis 10 laufen lassen, und immer nur um 1 verschieben, Kollisionsabfrage, um 1 verschieben, Kollisionsabfrage usw. PS: Du hast den Anhang vergessen. |
Re: Pong - Problem bei der Ballkollision
Liste der Anhänge anzeigen (Anzahl: 1)
Mein Intervall is so schon auf 20 ^^
Aber das mit der Schleife probier ich mal...danke! Da seh ich nurnoch das Problem, dass ich ja 2 unteschiedliche x und y werte hab aber sie ja in einer Schleife unterbringen muss aber das krieg ich irgendwie hin.... Den Anhang haeng ich jetzt nochma dran! |
Re: Pong - Problem bei der Ballkollision
@Anhang:
Ja, dann musst du wohl die Schleife benutzen, damit die Kollision schon vorher abgefangen wird. |
Re: Pong - Problem bei der Ballkollision
Argh nichmal mit der Schleife krieg ich das hin, weil ich ja nur um ganze Werte verschieben kann...
Jetzt hab ich Richtung.X = 10 und Richtung.Y = 7 z.B...jetzt würd ich ne Schleife mit 10 Wiederholungen machen und jedesmal X um 1 und Y um Richtung.Y / Richtung.X aber das waer dann ja gerundet, d.h es wird ungenau und wenn Richtung.Y / Richtung.X z.B. 0,3 waer wuerde Richtung.Y ja zu null... Verstehste mein Problem? :D:D:D Boah is das kompliziert...wie wird sowas denn bei komplexeren Sachen gelöst??? Is die Bewegung mit z.B. DelphiX besser oder einfachrer zu realisieren? MfG |
Re: Pong - Problem bei der Ballkollision
Speicher doch die Position des Balles in einem reellen Typ.
Keine Ahnung, wie das mit DelphiX aussieht, ich hab bisher auch immer alles mit PaintBox und Co. gemacht. Glaub aber kaum, dass DelphiX dir diese Arbeit abnimmt. |
Re: Pong - Problem bei der Ballkollision
argh dann muss ich ja sauvoiel umschreiben oder?? und du meinst dan reellen typ dann immer wenn ich die pixel abfrage runden oder was???
|
Re: Pong - Problem bei der Ballkollision
Runden brauchst du eig. nur, wenn gezeichnet werden muss. Wieso viel umschreiben?
|
Re: Pong - Problem bei der Ballkollision
Aaaah das geht trotzdem nich ^^
Weil das mit der Schleife meeeeeeeeega kompliziert is....das muss doch auch einfacher gehn...ich guck mir mal nochn paar andere Pong Spiele an! |
Re: Pong - Problem bei der Ballkollision
Auch auf die Gefahr hin das ihr mich der Werbung bezichtigt.
es gibt da ein ![]() |
Re: Pong - Problem bei der Ballkollision
Ok danke, dass seh ich mir ma an, hab naemlich sonst unter der Suche kein Pong gefunden wo man auch die Seiten des Schlaegers anstoßen kann =)
Woah da steig ich nun garnichmehr durch :( |
Re: Pong - Problem bei der Ballkollision
Eigentlich ist es ganz Simpel.
Du must ja nur den Teil auserhalb der Class verstehn. |
Re: Pong - Problem bei der Ballkollision
Alles ausser Class is doch fast das ganze Projekt!? Oder meinste damit alles ausser der Elements.pas?
Da sind soviele Variablen, dass ich bei jeder Prozedur tausend mal nachgucken muss, was das war... :D Naja dann dauert des ganze halt ma zwei Stunden :P Ich setz mich heute Abend ma dran...
Delphi-Quellcode:
So also wenn mich nich alles taeuscht ist das doch das was ich suche oder??? Wie man berechnet wie der Ball abprallt wenn er auf ein Rechteck trifft....so und ich steig da mal garnicht durch!!!
Function EllipseRechteckcollision(E1, R1: Trect): boolean;
Type Tpunkt = Record X, Y: Extended; End; Var SN1, SN2, X, Alpha: extended; p1, p2: Tpoint; N1, N2: Tpunkt; radius1, radius2: integer; Begin result := false; If E1.left > E1.right Then tausche(E1.left, E1.right); If E1.Top > E1.bottom Then tausche(E1.top, E1.bottom); If r1.left > r1.right Then tausche(r1.left, r1.right); If r1.Top > r1.bottom Then tausche(r1.top, r1.bottom); p1.x := E1.left + ((E1.right - E1.left) Div 2); p1.y := E1.top + ((E1.Bottom - E1.top) Div 2); p2.x := r1.left + ((r1.right - r1.left) Div 2); p2.y := r1.top + ((r1.Bottom - r1.top) Div 2); Alpha := arcTangens(p1.x - p2.x, p1.y - p2.y); X := Hypot(p1.x - p2.x, p1.y - p2.y); Radius1 := p1.x - E1.left; Radius2 := p1.y - E1.top; // Radius 1 Horizontal, Radius2 Vertikal N1.X := cosinus(alpha) * radius1 + P1.X; N1.Y := sinus(Alpha) * radius2 + P1.Y; SN1 := Hypot(p1.x - n1.x, p1.y - n1.y); Case round(Alpha) Of 0..45: Begin n2.x := R1.right; n2.y := round(tangens(alpha) * ((r1.Bottom - p2.y) / 2)) + p2.y; End; 46..90: Begin n2.y := R1.top; n2.x := round(tangens(alpha - 45) * ((r1.right - p2.x) / 2)) + p2.x; End; 91..135: Begin n2.y := R1.top; n2.x := round(tangens(alpha - 90) * ((r1.right - p2.x) / 2)) + p2.x; End; 136..225: Begin n2.x := r1.left; n2.y := round(tangens(alpha) * ((r1.Bottom - p2.y) / 2)) + p2.y; End; 226..270: Begin n2.y := r1.bottom; n2.x := round(tangens(alpha - 225) * ((r1.right - p2.x) / 2)) + p2.x; End; 271..315: Begin n2.y := r1.bottom; n2.x := round(tangens(alpha - 270) * ((r1.right - p2.x) / 2)) + p2.x; End; 316..360: Begin n2.x := R1.right; n2.y := round(tangens(alpha) * ((r1.Bottom - p2.y) / 2)) + p2.y; End; End; SN2 := Hypot(p2.x - n2.x, p2.y - n2.y); If (x <= (sn1 + Sn2)) Then result := true; End; Ich hab null Plan davon was du da gemacht hast ^^
Delphi-Quellcode:
Speziell mit dem Teil habe ich Probleme also ich verstehs nun soweit, dass du zuerst die Steigung ziwschen den beiden Mittelpunkten ausrechnest, richtig?
Alpha := arcTangens(p1.x - p2.x, p1.y - p2.y);
X := Hypot(p1.x - p2.x, p1.y - p2.y); Radius1 := p1.x - E1.left; Radius2 := p1.y - E1.top; // Radius 1 Horizontal, Radius2 Vertikal N1.X := cosinus(alpha) * radius1 + P1.X; N1.Y := sinus(Alpha) * radius2 + P1.Y; SN1 := Hypot(p1.x - n1.x, p1.y - n1.y); Dann nimmst du von der Steigung den ArcTangenten...(ich versteh nur nich wozu :D) Und dann wofür diese Sinus und Cosinus Funktionen mit den Radien sind versteh ich auch nich ganz ^^ |
Re: Pong - Problem bei der Ballkollision
Liste der Anhänge anzeigen (Anzahl: 1)
Ho
Also ich will mal Versucehn Licht ins Helle zu bringen ;)
Delphi-Quellcode:
Berechnet wie der Name schon sagt ob ein Rechteck mit einer Ellipse Collidiert.
function EllipseRechteckcollision(..);
Wobei Rechteck genau wie Ellipse als Trect übergeben werden ( übrigens genau gleich würdest du sie mit einem Tcanvas Malen lassen ). dann wird eine Strecke zwischen den Mittelpunkten der beiden Objecte Gebildet und die Länge berechnet. Danach wird an dieser Lienie geschaut wann sie die schnittpunkte mit der Ellipse und dem Rechteck hat ( das ist das Sinus, Cosinus zeug ) anhand der Schnittpunkte kann dann berechnet werden wie Lang diese Strecken Relativ zur Gesamtstrecke der Mittelpunkte sind. je nach dem hat man dann eine Collision, oder eben nicht. Das ist es im Prinzip. Die Berechnung ist nicht 100% aber schon nah dran und immer noch recht schnell. Habe dir mal mein Collision Testprogramm mit dran gehängt. da kannst du Graphisch sehn was die Function macht. der Grund warum Sinus und Cosinus Functionen liegt an der Umrechnung Bogenmaß Gradmaß. Je nach schulbildung sagt dir das dann was, oder auch nicht ;) |
Re: Pong - Problem bei der Ballkollision
Naja..also bin iner 10.Klasse und wir hatten es bisher nur in Mathe Info (wo wir aber leider sonen dummen Lehrer haben, dass wenn man sich nich alles selber beibringt nix lernt).
Somit: es sagt mir recht wenig ^^ Also gut, dass hab ich alles fast verstanden aber der letzte Teil, da guckst du in welchen Winkel sie aufeinander treffen und siehst daran dann von wo der Ball gekommen sein muesste, dh auf welcher Seite vom Rechteck er aufprallt oder? (Boah das is ja mal viel komplizierter als ich dachte! Jetzt weiss ich zumindest, dass wenn wir das iner Schule durchnehmen ich aufpasse :D) BTW: Hab mir grad ma dein Spiel angeguckt, Balanced, MEGA GENIAL!! Respekt, nach wievielen Jahren Delphi haste das geschrieben und wie alt warste da?? |
Re: Pong - Problem bei der Ballkollision
thx für die Blumen.
![]() also mit 24. Eigentlich wollte ich ein Sample schreiben das mir mal klar macht wie man Mit OpenGl Programmiert. Weil ich bis dahin nur wuste das es sowas gibt. Nicht aber was es ist. Wie man An Balanced sehen kann ist OpenGl echt easy zu lernen. Das Game hat mich ungefähr 10- 20 Wochen programmieren gekostet. Dabei sind dann etwa 35000 Zeilen Code entstanden. Die Physik engine habe ich aber nicht komplett gemacht. Im Game sind 3 Physik engines die Hauptengine ist aber definitiv Newton.dll Aber bei ![]() Was deine Frage angeht : Du darfst nicht nur schaun aus welcher Richtung die Objecte zusammenstoßen, sondern du must die Objecte auch wieder auseinander rücken, bevor du sie dann Abprallen läst. Sonst bekommst du teilweise recht böse Fehler hin. Desweiteren ist es sehr wichtig das du immer Recht kleine Bewegungen machst ( deiner Kugel ) wenn du das nicht machst must du zusätzlich Prüfen ob du evtl eine Komplette wand "überspringst". 10. Klasse Mathe ist da leider natürlich nicht ganz die Optimale Grundlage. Ich empfehle dir Dringend Vektorrechnung !!! Frag das mal deinen Lehrer. oder schau dir die Tutorials an. mit Vektoren werden so 2D und 3D sacehn deutlich einfacher. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:14 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz