|
Antwort |
Registriert seit: 5. Dez 2006 459 Beiträge |
#1
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! |
Zitat |
Registriert seit: 6. Jun 2006 589 Beiträge Turbo Delphi für Win32 |
#2
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. |
Zitat |
Registriert seit: 5. Dez 2006 459 Beiträge |
#3
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!? |
Zitat |
Registriert seit: 6. Jun 2006 589 Beiträge Turbo Delphi für Win32 |
#4
Zitat von .chicken:
Kannst du deine ersten beiden Punkte vielleicht nochmal erlaeutern? Die versteh ich ehrlich gesagt nich so ganz ^^!
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 diesem Post beschreibt SirThornberry wie's geht.
Zitat von .chicken:
Dann zum dritten Punkt, das werd ich mir ma ansehn, wenn ich nich klar komme melde mich mich nomma, wenns recht is =)
Zitat von .chicken:
Du meinst also dass ich einfach bevor ich die Kugelposition erneuere die alte abfrage und dann einfach nur bis zum Rand "auffuelle"?!
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. |
Zitat |
Registriert seit: 5. Dez 2006 459 Beiträge |
#5
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! |
Zitat |
Registriert seit: 6. Jun 2006 589 Beiträge Turbo Delphi für Win32 |
#6
Zitat von .chicken:
Hm also zum ersten...is das einfach nur besserer Stil oder funktioniert das schneller oder was steckt dahinter?
Ich glaube es gibt noch viel mehr Vorteile, welche genau weiß ich auch nicht.
Zitat von .chicken:
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! |
Zitat |
Registriert seit: 5. Dez 2006 459 Beiträge |
#7
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 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 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 =) |
Zitat |
Registriert seit: 6. Jun 2006 589 Beiträge Turbo Delphi für Win32 |
#8
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 von .chicken:
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...
|
Zitat |
Registriert seit: 5. Dez 2006 459 Beiträge |
#9
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 |
Zitat |
Registriert seit: 6. Jun 2006 589 Beiträge Turbo Delphi für Win32 |
#10
Zitat von .chicken:
Denn nebenbei is der Ball ja rund, und ich habe aber ein eckiges Bild, dh die Abfrage ist nicht wirklich genau!
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); |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |