Autor |
Beitrag |
felix00186
Hält's aus hier
Beiträge: 1
|
Verfasst: Sa 06.06.15 17:53
Hallo!
Ich möchte ein Spiel programmieren. Bei diesem Spiel gibt es auf dem Bildschirm einen Smiley. Der bewegt sich immer in eine Richtung, die man durch das Drücken von w (nach oben), a (nach links), s (nach unten), d (nach rechts) verändern kann. Sobald sich der Smiley außerhalb des Bildschirms befindet, soll das Spiel zu Ende sein. Gleichzeitig kommt es alle 1,5 bis 6 Sekunden (zufällig) dazu, dass an einer zufälligen Stelle auf dem Bildschirm die Schrift "Leertaste drücken" erscheint. Die Schrift verschwindet nach 1 Sekunde wieder und wenn man in der Zeit die Leertaste nicht gedrückt hat, wird der Smiley schneller. Wenn man die Leertaste drückt, ohne dass man soll, wird der Smiley auch schneller. Das hier habe ich bis jetzt programmiert, aber ich weiß nicht, wie ich es gleichzeitig ablaufen lassen soll. Ich bin noch Anfänger.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); var i:integer; begin if key=#13 then begin Label1.visible:=FALSE; with Form1.Canvas do begin pen.color:=clLime; for i:=0 to clientwidth do begin MoveTo(0,0); LineTo(i,clientheight); MoveTo(clientwidth,0); LineTo(clientwidth-i,clientheight); if i mod 5 =0 then delay(1); end; Button1.visible:=TRUE; end; end; end; |
Das ist der Anfang. Sobald man Enter drückt, zieht sich ein Vorhang über den Bildschirm und dann erscheint ein "Starten"-Button.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| procedure TForm1.Button1Click(Sender: TObject); begin Button1.visible:=FALSE; leeren; x:=clientwidth div 2; y:=clientheight div 2; smiley(x,y); Label2.visible:=TRUE; repeat leeren; y:=y-2; smiley(x,y); delay(50); until a=1; end; |
Das passiert, wenn man den Button anklickt. Ein Smiley entsteht in der Mitte des Bildschirms und bewegt sich nach oben.
Smiley ist ein Objekt, das ich oben unter procedure benannt habe. Der Smiley wird auf die Form1.Canvas gezeichnet. x und y sind die Koordinaten des Mittelpunkts.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82:
| procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if key=87 then begin a:=1; leeren; a:=0; repeat if (x+15<clientwidth) and (x-15>0) and (y+15<clientheight) and (y-15>0) then begin smileyweg; y:=y-g; smiley(x,y); delay(50); end else begin gameover; Application.MainForm.Close; end; until (a=1) or (f=1); end;
if key=83 then begin a:=1; leeren; a:=0; repeat if (x+15<clientwidth) and (x-15>0) and (y+15<clientheight) and (y-15>0) then begin smileyweg; y:=y+g; smiley(x,y); delay(50); end else begin gameover; Application.MainForm.Close; end; until (a=1) or (f=1); end;
if key=65 then begin a:=1; leeren; a:=0; repeat if (x+15<clientwidth) and (x-15>0) and (y+15<clientheight) and (y-15>0) then begin smileyweg; x:=x-g; smiley(x,y); delay(50); end else begin gameover; Application.MainForm.Close; end; until (a=1) or (f=1); end;
if key=68 then begin a:=1; leeren; a:=0; repeat if (x+15<clientwidth) and (x-15>0) and (y+15<clientheight) and (y-15>0) then begin smileyweg; x:=x+g; smiley(x,y); delay(50); end else begin gameover; Application.MainForm.Close; end; until (a=1) or (f=1); end; |
So funktioniert die Steuerung. Die Variable a besagt, dass eine andere Taste gedrückt wurde und sorgt dafür, dass sich der Smiley nicht mehr in die ursprüngliche Richtung bewegt.
"Leeren" und "Smileyweg" sind 2 Objekte, die ich als procedure beschrieben habe.
Leeren zeichnet ein Rechteck in der Hintergrundfarbe über den Bildschirm, Smileyweg zeichnet ein Rechteck in der Hintergrundfarbe nur über den Smiley.
Gameover; ist ein Objekt, das ich am Anfang mit einer Procedure benannt habe.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
| procedure Starten; var t:integer; begin repeat t:=Random(6000-1500+1)+1500; delay(t); xx:=Random(Form1.clientwidth-100); yy:=Random(Form1.clientheight-50); with Form1.Canvas do begin font.color:=clBlack; font.height:=30; brush.color:=clBtnFace; Textout(xx,yy,'Drücke die Leertaste!'); end; p:=1; delay(1000); p:=0; with Form1.Canvas do begin pen.color:=clBtnFace; brush.color:=clBtnFace; rectangle(xx,yy,xx+100,yy+50); end; if k<>1 then begin Form1.Canvas.Font.height:=20; Form1.Canvas.Font.color:=clRed; Form1.Canvas.brush.color:=clBtnFace; Form1.Canvas.Textout(xx,yy,'Verpasst! Geschwindigkeit +1'); g:=g+1; end; k:=0; until f=1; end; |
Das ist die procedure, die dafür sorgen soll, dass immer wieder zufällig die Leertaste gedrückt werden soll. Zusätzlich wird das noch in OnKeyDown überprüft:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| if key=32 then begin if p=1 then begin k:=1; Form1.Canvas.rectangle(xx,yy,xx+100,yy+50); end else begin g:=g+1; Form1.Canvas.font.height:=20; Form1.Canvas.font.color:=clRed; Form1.Canvas.brush.color:=clBtnFace; Form1.Canvas.TextOut(0,0,'Geschwindigkeit +1'); end; end; |
Ich hoffe, man kann mit den Variablen p und k einigermaßen durchsehen.
Als letztes zeige ich noch, was genau die procedure Gameover ist:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| procedure Gameover; begin f:=1; with Form1.Canvas do begin pen.color:=clRed; brush.color:=clRed; rectangle(0,0,Form1.clientwidth,Form1.clientheight); end; smiley(x,y); Form1.Canvas.brush.style:=bsClear; Form1.Canvas.font.color:=clWhite; Form1.Canvas.font.height:=100; Form1.Canvas.TextOut(100,50,'GAMEOVER!'); delay(3000); end; |
Ich kann es mir nicht erklären, aber es funktioniert nicht mal, dass das Programm sofort erkennt, wenn der Smiley außerhalb des Bildschirms ist. Erst wenn man dann die Richtung ändert, erkennt es das. Dazu kommt noch, dass das Programm 3 Sekunden nach Gameover nicht geschlossen wird, sondern nur der Bildschirm leer wird und dazu nicht mal mit dem Kreuz rechts oben zu schließen ist.
Es wäre wirklich sehr, sehr nett, wenn jemand die ganzen Teile so zusammensetzen kann, dass das Spiel funktioniert. VIELEN DANK!
Ich hoffe, man sieht einigermaßen durch mit den ganzen Variablen:
Delphi-Quelltext 1: 2: 3:
| var Form1: TForm1; x,y,a,g,p,k,xx,yy,f:integer; |
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 06.06.15 20:24
Ich habe leider gerade nicht viel Zeit, deshalb kann ich nur etwas allgemeinere Tipps geben.
Was du machst ist aktuell, dass du eine Schleife hast und dort mit Verzögerungen Schritt für Schritt eine Aktion ablaufen lässt: Quelltext 1: 2: 3: 4: 5:
| Aktion A Delay Aktion B Delay ... | Das Problem ist nur, dass du so dazwischen nichts anderes machen kannst. Deshalb ist es besser, wenn du nur eine Schleife benutzt und darin jeweils einen Schritt aller Aktionen durchführst und dann die Verzögerung durchführst. Sprich: Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| Aktion A Aktion B Aktion C Delay Aktion A Aktion B Aktion C Delay ... |
felix00186 hat folgendes geschrieben : | Ich hoffe, man sieht einigermaßen durch mit den ganzen Variablen:
Delphi-Quelltext 1: 2: 3:
| var Form1: TForm1; x,y,a,g,p,k,xx,yy,f:integer; | |
Bei solchen Variablennamen fällt das schwer. Ist dir nicht aufgefallen, dass bei Delphi selbst z.B. der Befehl Form1.Canvas.Rectangle ausgeschrieben mehr Sinn macht als (nach deiner Schreibweise) F1.C.R? Meinst du du würdest mit F1.C.R etwas anfangen können ohne nachzuschauen was der Befehl macht?
Deshalb sollten Variablennamen nicht einfach Buchstaben aus dem Alphabet sein sondern "sprechend" sein, sprich der Name sollte etwas darüber sagen was in der Variablen drin ist. (Von Schleifenvariablen wie for i := ... mal abgesehen)
Zum Beispiel PosLeft und PosTop statt xx und yy. Wie du sie konkret benennst, ist nicht so wichtig, Hauptsache der Name hat einen Sinn. 
|
|
GuaAck
      
Beiträge: 378
Erhaltene Danke: 32
Windows 8.1
Delphi 10.4 Comm. Edition
|
Verfasst: So 07.06.15 00:14
Hallo Felix,
Jaenicke liegt natürlich in allen Punkten richtig.
Hier aber etwas zum Hintergrund: Bei einem üblichen Delphi-Programm läuft alles sequentiell ab. Wenn man will, dass das Betriebssytem Teilaufgaben auf die Kerne der CPU verteilt, dann muss man die Teilaufgaben selbst festlegen (Threads). Bei Darstellungen auf dem Bildschirm geht das aber nicht so einfach, es können ja nicht alle gleichzeitig auf dem Bildschirm malen.
Du meinst mit "gleichzeitig" etwas im Zeitbereich von einigen Millisekunden, so dass der Mensch am Bildschrim es nicht merken würde, wenn ein schneller Rechner die Aufgaben nacheinander erledigt. Genau da setzen die Betriebssystema an. Z.B. programmierst Du etwas, was 10 s Zeit braucht. Während der Berechnung kannst Du das Fenster auf dem Bildscheirm weder schließen, noch verschieben.
Du hast vielleicht eine Button "Abbruch" programmiert, aber das geht auch nicht. Der Click auf Deinen Button (oder Fenster bewegen use.) wird vom Betriebsystem in eine Message an Dein Programm umgesetzt. Dein Programm rechnet aber und hat keine Zeit, den Befehl "Abbruch" zu bearbeiten.
Mit dem Aufruf "processmessages" kannst Du Windows an geeigneten Stellen erlauben, andere Teile Deines Progroamm, z. B. die Routine "OnAbbuchClick" zu bearbeiten.
Viel Erfolg,
Gruß GuaAck
|
|
Xion
      

Beiträge: 1952
Erhaltene Danke: 128
Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
|
Verfasst: So 07.06.15 21:08
Delphi ist prinzipiell ereignisorientiert. Das heißt: Dein Quelltext wird immer dann ausgeführt, wenn "irgendwas" passiert ist. So wird etwa der Code von OnKeyDown ausgeführt, wenn eine Taste gedrückt wird.
Dann ist, wie schon erwähnt wurde, ein Programm immer eine Folge von Befehlen. Es wird also immer eine Zeile Quelltext zu einem Zeitpunkt ausgeführt. Da deine Starten-Funktion blockierend ist, das heißt von sich aus nie mehr beendet wird, kann eine andere Funktion (z.B. Gameover) nicht "gleichzeitig" ausgeführt werden und diese unterbrechen.
Theoretisch geht das auch (mit Threads), aber glaube mir, das willst du auf keinen Fall, denn dann kann es sehr unschöne Effekte geben, wenn man nicht ganz genau weiß, was man tut.
Deine Starten-Funktion enthält eine Endlosschleife, da du möchtest, dass das Programm endlos läuft (bzw. bis du Gameover aufrufst). Diese repeat/until Schleife solltest du durch einen Timer ersetzen. Die Komponente TTimer kannst du auf dein Formular ziehen und per Doppelklick darauf ein OnTimer-Ereignis erzeugen. Diese Funktion wird dann in regelmäßigen Abständen als Ereignis gestartet (vom Betriebssystem). Dort schreibst du rein, was jetzt in Starten steht, aber OHNE die repeat/until Schleife (sonst hängt das Programm im Timer).
Dann kannst du sowohl auf einen Knopf drücken, als auch deine Leerzeichen-Schrift periodisch anzeigen. Das geschieht aber nicht gleichzeitig, denn die Ereignisse, wie eben Timer oder KeyDown, werden stets nacheinander ausgeführt. Allerdings geht das so schnell, dass man es nicht merkt. Wenn du aber deine Endlosschleife in den Timer schreibst, dann wird der Timer nie fertig und entsprechend auch "nachher" nichts mehr ausgeführt. Sleep oder Delay ist eigentlich ein klares Zeichen für einen ungeschickten Ansatz, da es den Programmfluss stört und gleichzeitig aber auch nichts anderes ausgeführt werden kann.
Übrigens: Auch das Betriebssystem kann immer nur eine Befehlsfolge ausführen, inklusive deinem Programm (auf einem Ein-Kern-Prozessor zumindest). Allerdings ist das Betriebssystem schlau genug, dass es dein Programm trotzdem stoppen kann, wenn du eine Endlosschleife programmiert hast. Andernfalls würde sich dein komplettes System aufhängen. Wenn auf meinem Notebook hier 5 Programme gleichzeitig laufen, dann ist es in Wirklichkeit so, dass sie nacheinander laufen. Gleichzeitig kennt der 1.6GHz Single-Core nämlich nicht. 
_________________ a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
|
|
|