Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Software-Projekte der Mitglieder (https://www.delphipraxis.net/26-software-projekte-der-mitglieder/)
-   -   Tipps und Ratschläge für Spiel & für effizientes Programmieren (https://www.delphipraxis.net/193665-tipps-und-ratschlaege-fuer-spiel-fuer-effizientes-programmieren.html)

Danny92 27. Aug 2017 16:29


Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Liste der Anhänge anzeigen (Anzahl: 1)
Jetzt habe ich das Schiffe versenken soweit fertig gestellt und stelle es euch hier zur Verfügung. Ich bin für jeden Tipp, Verbesserungsvorschlag, konstruktive Kritik oder neuen Einfall jederzeit dankbar. Schlussendlich geht es mir neben der Kritik nun nur noch darum, etwas an Performance rauszuholen, denn bei aktivem Spielen mit der KI läuft meine - wenn auch nicht gerade schnellste - CPU immerhin schon bei ca. 33%. Ist das eurer Meinung nach normal bei einem Programm diesem Formats? Oder ist das schlichtweg "schlecht" programmiert? Wie sieht eure CPU-Auslastung aus? Denn bis jetzt konnte ich nicht erkennen woran das liegt, da ich mit effizientem Programmieren noch nicht so vertraut bin bzw. es selten das Problem war. Das Neuzeichnen der StringGrids oder das Vorausberechnen des nächsten Schusses der KI scheint es schon mal nicht zu sein.
Vielen Dank für eure kompetente Hilfe!

PS: Da das Programm wegen der Resource zu groß ist, muss es hier verlinkt werden. Minen sind noch nicht implementiert. :lol:

Glados 27. Aug 2017 16:37

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Da fehlt wohl die DPR-Datei :P
Und die Exe kannst du ruhig auf "Release" stellen. 60MB.. kommt mir stark vor wie Debug.

Aviator 27. Aug 2017 16:39

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Also ich würde es mir anschauen, aber die Seite auf die mich FileUpload beim Klick auf Download weiterleitet wird bei mir als unsicher angezeigt, da das Zertifikat nicht mehr gültig ist. Was genau ist denn in dem Programm so groß? Hast du da eine kompilierte Version drin? Wenn ja, dann lösche die einfach mal raus.

Danny92 27. Aug 2017 17:02

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Liste der Anhänge anzeigen (Anzahl: 1)
Okay hier ist die vom Sound entschlackte Projektdatei. Lediglich deshalb war sie über 50 MB groß (das hier keine falschen Eindrücke entstehen :wink: , bei mir wurde der Link auch unsicher gemeldet :wink:) Deswegen hier noch einmal.

HolgerX 27. Aug 2017 19:42

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Hmm..

OK, dann fange ich hier mal an:

Nur mal Unit1 überflogen...

Delphi-Quellcode:
var
  Form1: TForm1;
  feldgroesse: byte = 10;
  blinkcount: byte = 0;
  a,b,schiffsindex,KIschiffindex,schussfeld: TPoint;
  shipplayer,shipki: TFlotte;
  spielstatus: byte = 0;
  letterissetted: boolean = false;
  firstpointissetted: boolean = false;
  completekoordinate: boolean = false;
  spielbeendetundverloren: boolean = false;
  schwierigkeitsgrad: byte = 1;
  playerfirst: boolean = true;
  vabwechselnd: boolean = false;
  vkischusssichtbar: boolean = false;
  playerIsPlaying: boolean = false;
  playerhasshot: boolean = false;
  mitGitter: boolean = true;
  BASS_Init: TBASS_Init;
  BASS_StreamCreateFile: TBASS_StreamCreateFile;
  BASS_ChannelIsActive: TBASS_ChannelIsActive;
  BASS_ChannelPlay: TBASS_ChannelPlay;
  BASS_ChannelStop: TBASS_ChannelStop;
  BASS_Free: TBASS_Free;
  expindex: integer = 0;
  backgroundmusic,strs: HStream;
  flottengroesse: array[1..5] of integer;
  wasserindex: integer = 0;
  demoisactive: boolean = false;
  demoabort: boolean = false;

implementation
-> Vermeide 'Globale' Variablen soweit wie möglich, eigentlich sollte hier nur
Delphi-Quellcode:
var
  Form1: TForm1;
stehen und sonst keine weiteren..
-> Die Meisten Variablen werden 'nur' in diesem Formular verwendet und sollten somit (mit 'F' davor) in den private-Bereich der Form.
-> Die ganzen BASS-Sachen packst Du in eine eigene Unit, der Übersicht wegen, wobei, wofür ist denn die 'bass.pas' dar? ;)

So.. Nur mal den Anfang gemacht.. ;)

Danny92 27. Aug 2017 20:06

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Okay danke das ist doch schon mal was. Ich werd' da weiter dran arbeiten und mich um effizieteren Gebrauch der Variablen kümmern. Womöglich hab ich dann halt mehr Parameter, und den Rest schieb ich in den private-Bereich. Aber ich fürchte das wird nicht das Problem sein für die hohe CPU-Auslastung oder?
Die Bass.pas hab ich nur am Anfang gebraucht, wie ich die Bass.dll in der Ressource noch nicht reingepackt hab, bzw. sie noch nicht zu verwenden gewusst hatte. :roll: Damit gebe ich eben Hintergrundmusik und einige Soundeffekte wieder, die bei Schiffe versenken ja ganz nett sind.

DP-Maintenance 27. Aug 2017 20:27

Dieses Thema wurde am "27. Aug 2017, 20:27 Uhr" von "Luckie" aus dem Forum "Multimedia" in das Forum "Software-Projekte der Mitglieder" verschoben.

Luckie 27. Aug 2017 20:28

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Mach den Anhang bitte an deinen ersten Beitrag und lösche den Link zum Download.

Glados 27. Aug 2017 21:13

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Zitat:

Ich werd' da weiter dran arbeiten und mich um effizieteren Gebrauch der Variablen kümmern. Womöglich hab ich dann halt mehr Parameter,
Wieso solltest du dadurch mehr Parameter haben? Du verschiebst du aktuell globalen Variablen einfach dort hin, wo sie besser aufgehoben sind und änderst die Stellen ab, wo du auf sie zugreifst.

Zitat:

Aber ich fürchte das wird nicht das Problem sein für die hohe CPU-Auslastung oder?
Nein.

Danny92 27. Aug 2017 21:25

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Ja das mit den Variablen werde ich schon hinbiegen.
Nur das mit dem Delay ist wirklich notwendig, zumindest im OnMouseDown-Ereignis von der FeldKI-Komponente, da mir das Programm bei Sleep einfrieren würde (von wegen keine Rückmeldung), und das ist natürlich nicht so toll, mit Delay kommt das nicht mehr vor. In der BtDemo macht das natürlich kein Sinn klar...
Aber ich brauche jedenfalls an mehreren Stellen in OnMouseDown gewisse Pausen mit Verzögerung zum Abspielen der Soundeffekte. Okay ich sehe schon, dass Delay dafür verantwortlich ist. Dankeschön. Kann ich die Prozedur evtl. ressourcensparender gestalten?

Glados 27. Aug 2017 21:30

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Probier mal negaH's Delay aus. Vielleicht bringts ja was. http://www.delphipraxis.net/6620-delay.html

Danny92 27. Aug 2017 21:42

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Jaaa die hab ich auch gerade gefunden. Na super, da sinkt die Auslastung der CPU gleich mal um lockere 20 Prozentpunkte...Einwandfrei.

Aviator 27. Aug 2017 21:48

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Nabend,

also das mit dem Delay hast Du ja schon rausgefunden. Das ist eigentlich der Auslöser für die mega CPU Auslastung.

Vielleicht könntest Du, um von der Delay Methode wegzukommen, einen Standard-Timer starten der in einem 25ms Interval läuft. In der OnTimer Methode zählst du dann bspw. die Durchläufe. Je nach Anzahl der Durchläufe befindest du dich einem definierten Zustand und löst das Repaint Event des entsprechenden Grids aus. Im Grid wird der Status abgefragt und das entsprechende Image gemalt.

Kann man sicherlich ausbauen, aber es wäre mal ein Anfang. Zumindest ballerst Du dein Programm nicht mit der Messageverarbeitung voll was in dem Fall dann schon kontraproduktiv ist.


Zudem lädst Du immer wieder die Images aus einer Ressource in ein TImage. Das könntest Du einmalig beim Start des Spiels bzw. beim Programmstart machen. Das würde dann auch nochmal ein kleines bisschen Geschwindigkeit bringen.

Aber ansonsten schönes Spiel für den Anfang. :thumb:


Allerdings muss ich mich trotzdem ziemlich durch den SourceCode kämpfen. Ich glaube wenn Du das Programm jetzt nochmal im Gesamten überarbeiten würdest und weißt worauf Du achten musst, dann wäre es sicherlich um einige Zeilen und Hilfsvariabeln kürzer. Aber perfekt lesbaren SourceCode schreiben kann sowieso niemand. Man verbessert sich nur mit der Zeit. Und ich würde sagen, dass Du da auf einem guten Weg bist. Weiter so :!: :thumb:

Noch ein Tipp: Beachte die Meldungen die Dir der Compiler auswirft und bearbeite die so schnell wie möglich. Wenn es irgendwann mal 100 oder gar 1000 sind, dann hast Du da kein Bock mehr drauf.

Zitat:

Zitat von Compiler
[dcc32 Warnung] USchiff.pas(18): W1010 Methode 'Destroy' verbirgt virtuelle Methode vom Basistyp 'TObject'
[dcc32 Warnung] USchiff.pas(65): W1036 Variable 'index' ist möglicherweise nicht initialisiert worden
[dcc32 Hinweis] USchiff.pas(15): H2219 Das private-Symbol 'TrefferSindZusammenhaengend' wurde deklariert, aber nie verwendet
[dcc32 Warnung] UFlotte.pas(31): W1010 Methode 'Destroy' verbirgt virtuelle Methode vom Basistyp 'TObject'
[dcc32 Warnung] UFlotte.pas(325): W1036 Variable 'j' ist möglicherweise nicht initialisiert worden
[dcc32 Hinweis] Unit1.pas(154): H2164 Variable 'FirstTickCount' wurde deklariert, aber in 'TForm1.Delay' nicht verwendet
[dcc32 Hinweis] Unit1.pas(185): H2164 Variable 'rs' wurde deklariert, aber in 'TForm1.spieleFXab' nicht verwendet
[dcc32 Hinweis] Unit1.pas(186): H2164 Variable 'extension' wurde deklariert, aber in 'TForm1.spieleFXab' nicht verwendet
[dcc32 Hinweis] Unit1.pas(617): H2077 Auf 'spielende' zugewiesener Wert wird niemals benutzt
[dcc32 Hinweis] Unit1.pas(659): H2077 Auf 'spielende' zugewiesener Wert wird niemals benutzt
[dcc32 Hinweis] Unit1.pas(1056): H2164 Variable 'ResStream' wurde deklariert, aber in 'TForm1.FormCreate' nicht verwendet
[dcc32 Hinweis] Unit1.pas(79): H2219 Das private-Symbol 'm_DllDataSize' wurde deklariert, aber nie verwendet
[dcc32 Hinweis] Unit1.pas(80): H2219 Das private-Symbol 'mp_DllData' wurde deklariert, aber nie verwendet
[dcc32 Hinweis] Unit1.pas(81): H2219 Das private-Symbol 'mp_MemoryModule' wurde deklariert, aber nie verwendet


Danny92 27. Aug 2017 22:32

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Jaa, vielen Dank für diese glorreichen Worte. :) Ja die Explosion.gif wird immer aus der Ressource geladen das kann ich noch verbessern. Bloß mit dem Timer im 25ms Intervall verstehe ich noch nicht so ganz. Sinn und Zweck von Delay ist ja nicht, ein Grid irgendwann neu zu zeichnen, sondern die Ausführung an Ort und Stelle im OnMouseDown-Ereignis kurzzeitig zu pausieren, bis die an der Stelle aufgerufenen Soundeffekte abgespielt sind, denn sonst hört man doch z.B. schon den Schuss des Gegners, wo ich doch gerade erst getroffen habe. Wenn ich anstelle von Delay nun einen Timer setze, dann pausiert dieser doch nicht die Ausführung des Codes? Ganz am Anfang habe ich mal mit Timer rumgespielt, bis ich am Ende auch 5-6 Stück hatte, die sich dann nur noch gegenseitig aufgerufen, an- und abgeschalten haben, und es war ein völliges Chaos und total verwirrend. So finde ich das nun eigentlich ganz elegant und effizient gelöst...naja effizient; jedenfalls geht die CPU-Auslastung schon mal in die richtige Richtung...

Und die Compilerhinweise werde ich natürlich noch in Angriff nehmen, bisher war das Programm nur eine einzige Baustelle in dem gar nix ging. Dies ging nicht und das ging nicht. Ich hab mich von einem Problem zum nächsten programmiert. Vor 2 Monaten konnte ich noch nicht ahnen, eine dll Bibliothek in eine Ressource zu laden, um damit Soundeffekte abzuspielen^^

Danny92 27. Aug 2017 23:04

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Im OnCreate-Ereignis habe ich hinzugefügt:
Delphi-Quellcode:
  for i:=low(explosion) to high(explosion) do
  begin
    explosion[i].LoadFromResourceName(hInstance,'exp'+IntToStr(i));
  end;
Sowie im TExplosionTimer habe ich
Delphi-Quellcode:
Image1.Picture.Bitmap.LoadFromResourceName(hInstance,'exp'+IntToStr(expindex));
durch
Delphi-Quellcode:
image1.Picture.Bitmap.Assign(explosion[expindex]);
ersetzt. Funktioniert im OnCreate schon nicht. Explosion ist ein Array[0..12] of TBitmap. Ist das nicht ansatzweise richtig?

Ghostwalker 28. Aug 2017 09:47

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Was gibts für eine Fehlermeldung/Fehlverhalten ?

TiGü 28. Aug 2017 09:49

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Zitat:

Zitat von Danny92 (Beitrag 1379656)
Delphi-Quellcode:
image1.Picture.Bitmap.Assign(explosion[expindex]);
ersetzt. Funktioniert im OnCreate schon nicht. Explosion ist ein Array[0..12] of TBitmap. Ist das nicht ansatzweise richtig?

Machst du denn vorher irgendwo sowas wie:
Delphi-Quellcode:
for i := Low(explosion) to High(explosion) do
begin
  explosion[I] := TBitmap.Create;
end;
Du musst die Platzhalter im Array schon mit Leben füllen.

Danny92 28. Aug 2017 16:56

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Natürlich das habe ich vergessen:oops:

haentschman 28. Aug 2017 19:48

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Liste der Anhänge anzeigen (Anzahl: 3)
Hallo Danny...:P

Erst mal Glückwunsch, daß du ein Programm, was funktioniert, fertiggestellt hast. :thumb:

Hinter den Kulissen hast du allerdings ein paar Fehler. :? Du hast selbst gesagt du willst dich verbessern. :thumb:

Hinweise:
1: StyleGuide mal durcharbeiten
2: :warn: Dich mit DRY dringend beschäftigen.
3. :warn: Nicht alle Dateien in einem Ordner. Besser eine Ordnerstruktur für dich definieren und als Template für neue Projekte verwenden. :thumb:
(Beispiel: siehe Bild1)
4. :warn: Codeformatter benutzen. (z.B. CnPack)
5. :thumb: kein WTITH im Quelltext (Bild4)
6. Hinweise des Beitrages #10, Punkte 1-4 http://www.delphipraxis.net/1379518-post10.html durcharbeiten.
* Styleguide der 2. :wink:
* DRY (Bild3) Hier kann man eine procedure machen mit den entsprechenden Parametern.
* KISS
* CamelCase
* Denglisch bitte vermeiden. :wink:
Delphi-Quellcode:
...
function getLaenge: integer;
besser
Delphi-Quellcode:
...
function IsShip(a: TPoint): Boolean;
Verbesserungen:
* ReportMemoryLeaksOnShutdown := True; in die Projektdatei aufnehmen. 8-)
* mehrere Klassen können auch in der selben Unit stehen.
* Sprechende Namen verbessern. Heute haben wir keine Speicherprobleme mehr. 8-)
* Formatter immer benutzen
* then immer in der gleichen Zeile wie if (siehe Styleguide)
Delphi-Quellcode:
if a.X=b.X then
begin
  vmin:=min(a.Y,b.Y); vmax:=max(a.Y,b.Y);
  laenge:=vmax-vmin+1;
  if (laenge>5) or (Length(vSchiff[laenge])>=flottengroesse[laenge]) then
    result:=false
  else
  begin
    abbruch:=false;
    i:=vmin;
    repeat
      if feld[a.X-1,i-1]=1 then abbruch:=true;
      Inc(i);
    until abbruch or (i>vmax);
    i:=vmin;
    if a.X>1
    then
      repeat
        if feld[a.X-2,i-1]=1 then abbruch:=true;
        Inc(i);
      until abbruch or (i>vmax);
    i:=vmin;
    if a.X<feldgroesse
    then
      repeat
        if feld[a.X,i-1]=1 then abbruch:=true;
        Inc(i);
      until abbruch or (i>vmax);
    if vmin>1 then
      if feld[a.X-1,vmin-2]=1 then abbruch:=true;
    if vmax<feldgroesse then
      if feld[a.X-1,vmax]=1 then abbruch:=true;
    if abbruch
    then result:=false
    else result:=true
  end;
end else
..wird:
Delphi-Quellcode:
  if a.X = b.X then
  begin
    vmin := min(a.Y, b.Y);
    vmax := max(a.Y, b.Y);
    laenge := vmax - vmin + 1;
    if (laenge > 5) or (Length(vSchiff[laenge]) >= flottengroesse[laenge]) then
      result := false
    else
    begin
      abbruch := false;
      i := vmin;
      repeat
        if feld[a.X - 1, i - 1] = 1 then
          abbruch := true;
        Inc(i);
      until abbruch or (i > vmax);
      i := vmin;
      if a.X > 1 then
        repeat
          if feld[a.X - 2, i - 1] = 1 then
            abbruch := true;
          Inc(i);
        until abbruch or (i > vmax);
      i := vmin;
      if a.X < feldgroesse then
        repeat
          if feld[a.X, i - 1] = 1 then
            abbruch := true;
          Inc(i);
        until abbruch or (i > vmax);
      if vmin > 1 then
        if feld[a.X - 1, vmin - 2] = 1 then
          abbruch := true;
      if vmax < feldgroesse then
        if feld[a.X - 1, vmax] = 1 then
          abbruch := true;
      if abbruch then
        result := false
      else
        result := true
    end;
  end
  else
Fehler:
1. Warnungen abarbeiten
Dringend:
Zitat:

[dcc32 Warnung] USchiff.pas(18): W1010 Methode 'Destroy' verbirgt virtuelle Methode vom Basistyp 'TObject'
Zitat:

[dcc32 Warnung] UFlotte.pas(31): W1010 Methode 'Destroy' verbirgt virtuelle Methode vom Basistyp 'TObject'
:warn:
Delphi-Quellcode:
destructor Destroy; override;
:warn: Daher kommen auch die Ressource Leaks. (Bild2)

...ansonsten weiter so. :thumb: Du hast ja das Forum als Hilfe.

Danny92 28. Aug 2017 20:42

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ja cnPack hab ich soeben schon mal installiert. Was soll ich sagen? Es sieht schon mal prima aus! Im Destroy-Ereignis gebe ich die Bitmap im Array wieder frei. Damit konnte ich die Speicherleaks auf folgende im Anhang reduzieren. Aber was ist der Rest? Ich vermute mal, das hat wieder irgendwas mit der Bass.dll aus der Ressource zu tun. Ich hab mich damit wie gesagt noch nie so intensiv beschäftigt...
Mit dem StyleGuide werde ich mir mal intensiver beschäftigen. Und mit einem sog. Duplicate Code Finder 1.0 konnte ich lediglich eine Stelle in der SetzeFlotte-Prozedur finden, die sich wiederholt; das werde ich auch noch besser machen und einfach ein Array draus machen, in dem ich beide Flotten speichere. Dann fallen die unterschiedlichen Bezeichner weg, was das Verdoppeln überflüssig macht. :wink:

Aber die Speicherleaks sind mir gerade noch ein Rätsel...
PS: Denglisch war schon oft mein Problem.:roll:

Danny92 29. Aug 2017 06:31

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Okay jetzt habe ich die Speicherlecks gefunden. Einmal hab ich in TFlotte.Destroy vergessen, die einzelnen Schiffe im Array wieder freizugeben.
Und das andere Leck war etwas schwieriger zu finden: Im Destroy-Ereignis der Form hat noch ein
Delphi-Quellcode:
BTMemoryFreeLibrary(mp_MemoryModule);
  FreeMemory(mp_DllData);
gefehlt. :thumb:

haentschman 29. Aug 2017 07:08

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Liste der Anhänge anzeigen (Anzahl: 3)
Hallöle...:P
Zitat:

Ja cnPack hab ich soeben schon mal installiert.
:thumb: Ein großer Werkzeugkasten, wo man nicht alles braucht, der dir aber das Leben erleichtert. Ein paar Optionen in der IDE solltest du deakivieren. (Optionen1+2)
1: Die Linien von Castalia können weg, es sein denn du magst die dünnen Linien lieber. Dann mußt du die Linien im CnPack abschalten.
2. Programmierhilfe: Die vom CnPack ist deutlich besser. Die findet auch Teilstrings. Nur doppelt suchen ist langsam...8-)
Zitat:

Duplicate Code Finder 1.0 konnte ich lediglich eine Stelle in der SetzeFlotte-Prozedur finden, die sich wiederholt
DRY ist was anderes. :P Du hast Glück. Für meine Sprachtherapie habe ich einen Mini Vortrag über DRY gehalten. (Anhang) Fällt dir zwischen Seite 5 und 6 was auf?

:wink:

Nachtrag DRY:

aus deinem Code:
Delphi-Quellcode:
procedure TForm1.LeuchteKnopfTimer(Sender: TObject);
begin
  if not playerhasshot
  then
  if blinkcount<25 then
  begin
    if BoxPlayer.Color=clRed
    then BoxPlayer.Color:=clNavy
    else BoxPlayer.Color:=clRed;
    LeuchteKnopf.Interval:=random(80)+21;
    Inc(blinkcount);
  end else
  begin
    blinkcount:=0;
    LeuchteKnopf.Enabled:=False;
    LeuchteKnopf.Interval:=1;
    BoxPlayer.Color:=clNavy;
  end
  else
  if blinkcount<25 then
  begin
    if BoxKI.Color=clRed
    then BoxKI.Color:=clNavy
    else BoxKI.Color:=clRed;
    LeuchteKnopf.Interval:=random(80)+21;
    Inc(blinkcount);
  end else
  begin
    blinkcount:=0;
    LeuchteKnopf.Enabled:=False;
    LeuchteKnopf.Interval:=1;
    BoxKI.Color:=clNavy;
  end;
end;
besser:
Delphi-Quellcode:
procedure TForm1.LeuchteKnopfTimer(Sender: TObject);
begin
  if not playerhasshot then
  begin
    LightingButton(BoxPlayer);
  end
  else
  begin
    LightingButton(BoxKI);
  end
end;
ausgelagert z.B. als private procedure:
Delphi-Quellcode:
procedure TForm1.LightingButton(GroupBox: TGroupbox);
begin
  if blinkcount < 25 then
  begin
    if GroupBox.Color = clRed then
      GroupBox.Color := clNavy
    else
      GroupBox.Color := clRed;
    LeuchteKnopf.Interval := random(80) + 21;
    Inc(blinkcount);
  end
  else
  begin
    blinkcount := 0;
    LeuchteKnopf.Enabled := False;
    LeuchteKnopf.Interval := 1;
    GroupBox.Color := clNavy;
  end;
end;

Danny92 2. Sep 2017 09:42

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Ich habe noch einen Fehler gefunden, aber ich weiß noch nicht, wie der zustande kommt. Setzt man die spielfeldgröße auf 5x5 Felder (spinedit. Max in der form2 muss evtl auf 5 gesenkt werden), und benutzt 1 fünfer Schiff sowie 1 dreier Schiff, so kommt es beim spielstart beim Klick auf zufällig in ca 10% der Fälle dass das fünfer Schiff nicht erscheint, sondern nur das kleine. Weiß jemand, wieso? Ich hab nicht den blassesten Schimmer und weiß auch nicht, wie man den Fehler schnell und einfach finden kann. Das passiert ja nur relativ selten...

Delphi-Laie 2. Sep 2017 10:22

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Wird randomize zum Programmstart aufgerufen?

Falls ja, schalte es ab.

Beobachte daraufhin Dein Programm: Ist die Reihenfolge bzw. die Verteilung dieser ominösen 10% in der Menge aller Spiele ab / nach dem Programmstart immer die gleiche? Falls ja, hat es mit dem (delphiinternen) Zufallsgenerator zu tun.

HolgerX 2. Sep 2017 13:05

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Hmm..

Ist dieses Problem auch mit dem Source deines ersten Post reproduzierbar?

Ansonsten (ohne in den Source hinein zu schauen):
- Hast Du beim Platzieren der Schiffe eine Kollisionsabfrage (damit nicht Schiffe aufeinander liegen)?
- Wird die linke oder obere Position des äußeren Kästchen des 5er Schiff auch auf 1/x bzw y/1 gesetzt?
Denn ansonsten würde das 5er Schiff ja aus dem Feld 'herausragen'.
- Was passiert mit dem Schiff, wenn es 'herausragt'? Wird es dann komplett nicht gezeichnet, oder nur der sichtbare Teil?

(Nur mal ein paar Fragen ;) )

himitsu 2. Sep 2017 15:39

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Zitat:

Zitat von Delphi-Laie (Beitrag 1380167)
Wird randomize zum Programmstart aufgerufen?

Bei Verwendung der VCL wird das automatisch erledigt, weil Viele es sändig vergessen.
(in der Initialisierung der VCL wird einmal Randomize ausgeführt)

Was man aber "normalerweise" niemals machen sollte.
"immer wieder" Randomize aufzurufen oder RandSeed zu setzen.
Es gab schon Leute, die dachten es wird "zufälliger", wenn sie vor jedem Auslesen von Random das gemacht haben und sich dann wunderten, dass dem nicht so ist. (eher das Gegenteil)



Ja, der TE hat einmal im OnCreate der Hauptform ein Randomize drin, also so weit alles OK.

Delphi-Laie 2. Sep 2017 16:34

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Zitat:

Zitat von himitsu (Beitrag 1380179)
Zitat:

Zitat von Delphi-Laie (Beitrag 1380167)
Wird randomize zum Programmstart aufgerufen?

Bei Verwendung der VCL wird das automatisch erledigt, weil Viele es sändig vergessen.
(in der Initialisierung der VCL wird einmal Randomize ausgeführt)

Ich weiß nicht, auf welche Delphiversion bzw. ab welcher Delphiversion Du Dich beziehst, aber bis Delphi 7, das ich zur Compilierung meines Sortieranimationsprogrammes benutze, ist es sehr wohl unterschiedlich, ob ich randomize (im Initialisationsteil der ersten VCL-Unit) aufrufe oder es unterlasse. Verzichte ich auf diesen Befehl, sind die Zufallszahlen nach jedem Programmstart die gleichen.

Edit: Ein "automatisches" Randomize ist ein zweischneidiges Schwert. Wie will man "vernünftig", "produktiv" debuggen, wenn man keine reproduzierbaren Ergebnisse erhält?

himitsu 2. Sep 2017 18:20

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Zitat:

Zitat von Delphi-Laie (Beitrag 1380182)
Edit: Ein "automatisches" Randomize ist ein zweischneidiges Schwert. Wie will man "vernünftig", "produktiv" debuggen, wenn man keine reproduzierbaren Ergebnisse erhält?

Nicht mit Zufalls werten arbeiten, oder vorher den Test "ordentlich" initialisieren (RandSeed), wenn es nötig ist.

Das mit der Automatik wurde nicht gleich zu beginn eingebaut.
Dachte bei D7 ist es schon da, aber spätestens mit XE ist es so.

Danny92 3. Sep 2017 20:50

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Ja es wird alles beachtet was beim Plazieren der Schiffe notwenig ist; dass es aus dem Feld nicht herausragt, dass dieses Schiff mit anderen nicht kollidiert oder sie sich berühren. Und solange nicht all diese Bedingungen erfüllt sind, wird das Schiff zufällig neu plaziert. Und mir ist schon unverständlich wie man da aus der Schleife rauskommt, solange noch nicht alle Schiffe plaziert worden sind^^
Wenn ich randomize in FormCreate (wird ja nur einmal aufgerufen) auskommentiere, bleibt der Fehler weiterhin. :?

Danny92 3. Sep 2017 21:09

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hier noch einmal die Prozedur zum Setzen der Flotte:

Delphi-Quellcode:
procedure TForm1.SetzeFlotte(ofPlayer: boolean);
var
  zahl, i, j, k, fehlenNoch, vofPlayer: Integer;
  done, alleProbiert: Boolean;
  richtungProbiert: array[0..3] of Boolean;
begin
  vofPlayer:=StrToInt(BoolToStr(ofPlayer, false)) + 1;
  flotte[vofPlayer].Free;
  flotte[vofPlayer]:=TFlotte.Create(flottengroesse[5], flottengroesse[4],
  flottengroesse[3], flottengroesse[2], flottengroesse[1]);
  for i:=5 downto 1 do
  begin
    j:=1;
    fehlenNoch:=flotte[vofPlayer].AnzahlFehlenderSchiffeDerGroesse(i);
    while j <= fehlenNoch do
    begin
      done:=false;
      repeat
        zahl:=random(feldgroesse * feldgroesse) + 1;
        for k:=0 to 3 do richtungProbiert[k]:=false;
        a:=Point(((zahl - 1) mod feldgroesse) + 1, ((zahl - 1) div feldgroesse) + 1);
        repeat
          k:=random(4);
          alleProbiert:=true;
          case k of
          0: if a.Y >= i then b:=Point(a.X, a.Y - i + 1);
          1: if a.Y + i - 1 <= feldgroesse then b:=Point(a.X, a.Y + i - 1);
          2: if a.X >= i then b:=Point(a.X - i + 1, a.Y);
          3: if a.X + i - 1 <= feldgroesse then b:=Point(a.X + i - 1, a.Y);
          end;
          if not richtungProbiert[k]
          then done:=flotte[vofPlayer].SetzeSchiff(a, b);
          richtungProbiert[k]:=true;
          for k:=0 to 3 do
            if not richtungProbiert[k] then alleProbiert:=false;
        until alleProbiert or done;
      until done;
      Inc(j);
    end;
  end;
  FeldPlayer.Repaint;
end;
Hier die darin benutzte boolsche Funktion SetzeSchiff(a, b: TPoint) aus der Klasse TFlotte, die genau dann true zurückgibt, wenn das Platzieren des Schiffes im Feld unter den bekannten Regeln erfolgreich war:

Delphi-Quellcode:
function TFlotte.SetzeSchiff(a: TPoint; b: TPoint): boolean;
var
  laenge, vmin, vmax, i: integer;
begin
  if IstGueltig(a, b) then
  begin
    if a.X=b.X
    then laenge:=max(a.Y,b.Y)-min(a.Y,b.Y)+1
    else laenge:=max(a.X,b.X)-min(a.X,b.X)+1;
    if laenge>1
    then Inc(schiffsanzahl);
    SetLength(vSchiff[laenge],Length(vSchiff[laenge])+1);
    vSchiff[laenge,high(vSchiff[laenge])]:=TSchiff.Create;
    if a.X=b.X then
    begin
      vmin:=min(a.Y,b.Y); vmax:=max(a.Y,b.Y);
      for i:=vmin to vmax do
      if laenge=1
      then feld[a.X-1,i-1]:=3
      else feld[a.X-1,i-1]:=1;
      result:=vSchiff[laenge,high(vSchiff[laenge])].SetzeSchiff(Point(a.X,vmin),Point(a.X,vmax));
    end else
    if a.Y=b.Y then
    begin
      vmin:=min(a.X,b.X); vmax:=max(a.X,b.X);
      for i:=vmin to vmax do
      if laenge=1
      then feld[i-1,a.Y-1]:=3 //Setze Mine
      else feld[i-1,a.Y-1]:=1;
      result:=vSchiff[laenge,high(vSchiff[laenge])].SetzeSchiff(Point(vmin,a.Y),Point(vmax,a.Y));
    end
    else
      result:=false
  end
  else
    result:=false
end;
Und hier zuletzt die darin benutzte boolsche Funktion SetzeSchiff(a, b: TPoint) aus der Klasse TSchiff, die bei Erfolg ebenfalls true liefert:

Delphi-Quellcode:
function TSchiff.SetzeSchiff(a,b: TPoint): boolean;
var
  istSchiff: boolean;
  i: integer;
begin
  istSchiff:=(a.X=b.X) and (abs(a.Y-b.Y)<5) or (a.Y=b.Y) and (abs(a.X-b.X)<5);
  if istSchiff then
  begin
    vStart:=a; vEnde:=b;
    if a.X=b.X
    then laenge:=abs(a.Y-b.Y)+1
    else laenge:=abs(a.X-b.X)+1;
    vWaagerecht:=a.Y=b.Y;
    SetLength(vSchiff,laenge);
    for i:=1 to laenge do vSchiff[i-1]:=false;
  end;
  result:=istSchiff;
end;
So und jetzt kommt das Irre: bleibt die äußerste Zählschleife in SetzeFlotte als i:=5 downto 1, wird in manchen Fällen das 5er Schiff nicht gezeichnet, die Flotte besteht also nur aus dem 3er (linkes Bild im Anhang). Ändere ich die Zählschleife aber in i:=1 to 5, wird (wenn auch noch seltener) das 3er Schiff vergessen und nur das 5er Schiff plaziert (rechtes Bild im Anhang).

Was ist da los?! Bug?

Danny92 3. Sep 2017 23:33

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Ok ich habe den Fehler gefunden^^

HolgerX 4. Sep 2017 05:19

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Hmm..


Zitat:

Zitat von Danny92 (Beitrag 1380240)
Ok ich habe den Fehler gefunden^^

Und was war es? ;)

Danny92 4. Sep 2017 05:38

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Der Case-Zweig in SetzeFlotte musste wie folgt abgeändert werden:

Delphi-Quellcode:
            case k of
            0: if a.Y >= i then
                 b := Point(a.X, a.Y - i + 1)
                else
                  richtungProbiert[0]:=True;
            1: if a.Y + i - 1 <= feldgroesse then
                 b := Point(a.X, a.Y + i - 1)
               else
                 richtungProbiert[1]:=True;
            2: if a.X >= i then
                 b := Point(a.X - i + 1, a.Y)
               else
                 richtungProbiert[2]:=True;
            3: if a.X + i - 1 <= feldgroesse then
                 b := Point(a.X + i - 1, a.Y)
               else
                 richtungProbiert[3] := True;
            else

            end;

Delphi-Laie 5. Sep 2017 16:38

AW: Tipps und Ratschläge für Spiel & für effizientes Programmieren
 
Zitat:

Zitat von himitsu (Beitrag 1380186)
Zitat:

Zitat von Delphi-Laie (Beitrag 1380182)
Edit: Ein "automatisches" Randomize ist ein zweischneidiges Schwert. Wie will man "vernünftig", "produktiv" debuggen, wenn man keine reproduzierbaren Ergebnisse erhält?

Nicht mit Zufalls werten arbeiten, oder vorher den Test "ordentlich" initialisieren (RandSeed), wenn es nötig ist.

Das mit der Automatik wurde nicht gleich zu beginn eingebaut.
Dachte bei D7 ist es schon da, aber spätestens mit XE ist es so.

Nein, und auch bei Turbo-Delphi (das wohl intern einem Delphi 2006 entsprechen dürfte) gibt es noch kein "automatisches" Randomize.


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:20 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz