![]() |
Strafzeitenverwaltung in eigener Unit
Hallo Wissende,
nach einiger Abstinenz bin ich nun auch mal wieder hier und hoffe, dass ich meine Frage im richtigen Sub-Forum stelle. Ich bin gerade dabei ein kleines Uhrenprogramm für Eishockey zu erstellen. In diesem Programm soll eine Spielzeituhr realisiert werden (schon erledigt) und die Strafzeiten müssen für beide Teams erstellt und verwaltet werden. Ich bin nun etwas am Grübeln, wie ich das am Besten anstellen kann, weil ja die Strafzeiten ablaufen müssen, intern dann aber die eventuell noch 2. laufende Strafzeit des gleichen Teams an Stelle der ersten bereits abgelaufenen Strafzeit gestellt werden soll. Also mal ganz grob, bevor ich hier zu viel Verwirrung stifte. Die einfachste Version ist. Das Spiel läuft, es wird gefoult, durch Team A (Heim) und der foulende Spieler erhält eine Zeitstrafe von 2 Minuten. Im Besten Fall läuft die Zeit ab (von 2:00 auf 0:00 oder umgekehrt) und die Uhr kann intern gelöscht werden. Ungefähre Ausgabe wäre dann:
XML-Code:
Nun kann es aber sein, dass innerhalb der aktuell laufenden 2 Minuten Strafe, ein weiteres Foul eines Spielers vom Team A begangen wird. In dem Fall soll also eine weitere Strafzeit hinzugefügt werden. Wir hätten in dem Fall, die noch laufende erste Strafzeit und die neu hinzugekommene Strafzeit.
<Team who="A" />
<Penalty Nr="1" Time="1:59" />
XML-Code:
Das Spiel geht nun weiter und beide Strafzeiten laufen. Die "erste" Strafzeit läuft nun auf 0:00 runter. Nun sollte die 2. Strafzeit an Stelle der ersten Strafzeit rutschen.
<Team who="A" />
<Penalty Nr="1" Time="0:38" /> <Penalty Nr="2" Time="2:00" />
XML-Code:
Ich würde die ganze Logik gern in eine eigene Unit oder Klasse stecken, muss aber zugeben, dass ich nicht wirklich weiss, wo ich anfangen soll. Ich habe schon ein paar Funktionen in Units ausgelagert, aber eine gesamte Struktur noch nie.
<Team who="A" />
<Penalty Nr="1" Time="1:22" /> Kann mir bitte jemand ein paar Tipps geben? Ich stell auch gern die Sourcen hier ein, wenns soweit ist. |
AW: Strafzeitenverwaltung in eigener Unit
Spontan würde ich da eine TObjectList in Betracht ziehen. Du definierst Dir ein Objekt mit den entsprechenden Feldern und fügst dies in die Liste ein. Ist die Zeit abgelaufen, löschst Du das Objekt wieder aus der Liste. Die Ausgabe erfolgt dann zyklisch (Timer) mittels einer Schleife über die Objekte.
|
AW: Strafzeitenverwaltung in eigener Unit
Man könnte statt der TObjectList auch
![]() |
AW: Strafzeitenverwaltung in eigener Unit
Das sollte in diesem Fall auf das selbe Ergebnis hinauslaufen, aber klar, wieso nicht?
|
AW: Strafzeitenverwaltung in eigener Unit
Natürlich kommt das gleiche Ergebnis heraus, wäre ja auch schrecklich, wenn nicht, oder? :shock:
Ich finde, wenn Delphi schon eine Klasse speziell für solche Fälle anbietet, kann man die ruhig auch mal benutzen ;) |
AW: Strafzeitenverwaltung in eigener Unit
Hallo,
erstmal danke für Euren Hinweise. Wie gesagt mein Wissen über Units und eigene Klassen ist arg begrenzt. Ich habe jetzt mal so angesetzt. Allerdings erschliesst sich mir noch nicht ganz, wie ich dass dann aufrufen soll, ob ich die Zeiten dann über nen Timer im Hauptfenster aktualisieren muss oder in der Klasse selbst und wo ich nun die Penalties als Liste erhalte... Fragen über Fragen. Irgendwas mach/denk ich da noch falsch... Hier erstmal mein aktueller Stand. Danke für einen prüfend beleerenden Blick.
Delphi-Quellcode:
unit unt_penalties;
interface type TPenalties = class public Team : Char; //character of Team (A or B) TTime : String; //total time of penaltie CTime : String; //current time of penalty constructor Create; class function AddPenalty(Team:Char;TimeOfPenalty : String): String; //add a penalty class function DeletePenalty(Team:Char;TimeOfPenalty : String): String; //delete a penalty class function EditPenalty(Team:Char;TimeOfPenalty : String): String; //edit a penalty end; implementation constructor TPenalties.Create; begin inherited; end; class function TPenalties.AddPenalty(Team:Char;TimeOfPenalty : String): String; begin //add a penalty end; class function TPenalties.DeletePenalty(Team:Char;TimeOfPenalty : String): String; begin //delete a penalty end; class function TPenalties.EditPenalty(Team:Char;TimeOfPenalty : String): String; begin //edit a penalty end; end. |
AW: Strafzeitenverwaltung in eigener Unit
Der Code ist so Unsinn. Du hast hier 2 Klassen zu einer einzigen verschmolzen, bzw. du hast hier eine Klasse, die gleichzeitig die Daten eines einzelnen Eintrages speichert *und* eine Liste von ebendiesen Einträgen verwalten soll. Das kann nicht funktionieren. Grundregel der OOP: Jede Klasse hat genau *eine* Aufgabe zu erfüllen.
Was du brauchst sind 2 Klassen: - die erste Klasse repräsentiert einen Strafzeit-Eintrag - die zweite Klasse verwaltet Instanzen dieser Klasse in einer Liste (kann z.B. TQueue oder TObjectList sein) Ich vermute außerdem, dass du Objekte und Klassen durcheinander wirfst. Ein Objekt ist alles in deiner Umgebung: eine Lampe, ein Schuh, eine Tastatur, eine Maus, ein Mensch... eben Dinge mit bestimmten Eigenschaften (wie z.B. Farbe, Größe etc.) und Methoden (z.B. Laufen, Springen, Essen...). Eine Klasse hingegen ist quasi ein Bauplan für ein solches Objekt - sie gibt an, welche Felder, Eigenschaften, Methoden etc. es gibt. Das Objekt, d.h. eine Klasseninstanz, ist dann das konkrete "Ding", das durch die Klasse definiert wurde. Hiervon kann es natürlich auch mehrere geben, genau so wie man mehrere Häuser nach dem gleichen Bauplan bauen kann. Benutzen tust du in der Regel immer nur die konkreten Objekte, nicht die Klassen. Daher sind auch die vielen class functions sicher nicht das was du willst, diese beziehen sich nämlich nicht auf die Instanzen einer Klasse, sondern auf die Klasse (den Bauplan) selbst, was eigentlich nur für Spezialfälle gedacht ist. Du willst aber mit Objekten arbeiten, und das ist mit class functions, so wie du sie verwendest, nicht machbar, da du z.B. nicht auf ein Feld (Variable) eines einzelnen Objekts zugreifen kannst, da die Klasse gar nicht wissen kann, welches Objekt gemeint ist, denn davon kann (und soll) es ja schließlich mehrere geben. Ich würde dir dringend empfehlen, ein Tutorial (oder noch besser ein Buch) über OOP zu lesen, bevor du weitermachst. Ohne die Grundlagen zu kennen, wirst du dir nämlich nur unnötig in den Fuß schießen. Als Anfänger kann es sehr leicht passieren, dass man seine Klassen nicht sinnvoll aufteilt (wie man z.B. bei deinem Code sieht), und dann hinterher u.U. ein Programm erhält, dass zwar Klassen und Objekte nutzt, aber in trotzdem in keinster Weise objektorientiert ist. |
AW: Strafzeitenverwaltung in eigener Unit
Moin Philip,
danke für deine umfangreichen Ausführungen. Ich glaube/hoffe, dass ich es nun etwas besser verstehe. Ich habe nun auch den Code so aufgesplittet, dass die eigentliche Zeitstrafe eine Klasse abgeleitet von TObject ist und Penalties eine Klasse abgeleitet von TObjectlist ist. Der Code sollte nun um einiges besser aussehen. Zum besseren Verständnis erlaube ich mir aber trotzdem noch ein paar Fragen. Vorher aber schon mal der Code. Ich habe ihn so kurz wie möglich gehalten.
Delphi-Quellcode:
unit unt_penalty;
interface type TPenalty = class public Team : Char; //character of Team (A or B) TTime : String; //total time of penaltie CTime : String; //current time of penalty Direction : byte; //direction of running time 0:asc;1:desc constructor Create; end; implementation constructor TPenalty.Create; begin inherited; end; end.
Delphi-Quellcode:
Und so habe ich es testhalber im MainForm mal eingebunden:
unit unt_penalties;
interface uses Contnrs, unt_Penalty; type TPenalties = class(TObjectList) public constructor Create; function AddPenalty(Penalty : TPenalty; PenaltyList : TObjectList): TObjectList; //add a penalty function DeletePenalty(Penalty : TPenalty; PenaltyList : TObjectList): TObjectList; //delete a penalty function EditPenalty(Penalty : TPenalty; PenaltyList : TObjectList): TObjectList; //edit a penalty end; implementation uses Classes; constructor TPenalties.Create; begin inherited; end; function TPenalties.AddPenalty(Penalty : TPenalty; PenaltyList : TObjectList): TObjectList; begin //add a penalty with PenaltyList do begin Add(Penalty); end; end; function TPenalties.DeletePenalty(Penalty : TPenalty; PenaltyList : TObjectList): TObjectList; begin //delete a penalty with PenaltyList do begin Delete(IndexOf(Penalty)); end; end; function TPenalties.EditPenalty(Penalty : TPenalty; PenaltyList : TObjectList): TObjectList; begin //edit a penalty end; end.
Delphi-Quellcode:
Es lässt sich kompilieren, aber ich habe natürlich noch das Problem, dass ich nicht weiss, wie ich dann an die einzelnen Penalty-Einträge in der Penaltyliste komme, wenn ich diese z.B.
procedure TForm1.FormCreate(Sender: TObject);
var MyPenalty : TPenalty; MyPenalties : TPenalties; begin //erzeugen des penalty und zuweisen der daten MyPenalty := TPenalty.Create; MyPenalty.Team := 'A'; MyPenalty.TTime:= '00:02:00'; MyPenalty.CTime:= '00:02:00'; MyPenalty.Direction:= 0; //erzeugen der penaltyliste und hinzufügen des penalty myPenalties := TPenalties.Create; mypenalties.AddPenalty(MyPenalty, MyPenalties); end; Löschen, Editieren oder wegen laufenden Uhr updaten möchte... Die Daten (die einzelnen Zeitstrafen) muss ich natürlich auch im Programm visualiseren, d.h. ich muss natürlich vorher die Daten nach Teams und Höhe der laufenden Uhr sortieren und dann intern (später auch extern) anzeigen. Hast Du hierfür noch einen Tipp? Habe ich sonst noch was falsch gemacht? |
AW: Strafzeitenverwaltung in eigener Unit
1. Empfehlung: Du solltest TPenalty einen Kontruktor spendieren, dem man alle Daten übergeben kann.
2. Fehler: function AddPenalty BITTE keinen 2. Parameter. Das ist sinnlos, dafür ist doch gerade das Objekt da. 3.
Delphi-Quellcode:
???? Eine Penalty läuft doch immer "ab" oder nicht?
Direction : byte; //direction of running time 0:asc;1:desc
4. Datenhaltung. Strings würde ich hier nicht einsetzen. Viel besser wäre ein Zeitpunkt, zu dem die Strafzteit abläuft. Ich weis jetzt nicht, wie du die Spielzeit intern mitnimmt, aber dementsprechend wäre TTime oder integer angebracht. Daten und Darstellung bitte trennen. das wäre es erstmal von meiner Seite ;) |
AW: Strafzeitenverwaltung in eigener Unit
Hallo,
also ich habe die Penalty etwas angepasst.
Delphi-Quellcode:
Das Create ist nun doppelt drin. Vielleicht kann das eine ja nun auch ganz raus!?
unit unt_penalty;
interface uses Classes; type TPenalty = class public Team : Char; //character of Team (A or B) TTime : String; //total time of penaltie CTime : String; //current time of penalty Direction : byte; //direction of running time 0:asc;1:desc constructor Create(ATeam:Char; ATTime, ACTime : String; ADirection : byte); overload; constructor Create; overload; end; implementation constructor TPenalty.Create; begin inherited; end; constructor TPenalty.Create(ATeam:Char; ATTime, ACTime : String; ADirection : byte); begin inherited Create; Team := ATeam; TTime := ATTime; CTime := ACTime; Direction := ADirection; end; end. Zu Deinen Punkten Julius: 1. erledigt 2. ich bin echt ´n Blinder. :cyclops: 3. Wie sicher bist Du Dir da? Ich habe heute einiges an Zeit verbracht dies auf Offiziellen Seiten nachzulesen. Fakt ist. Die reine Spielzeit kann sowol aufsteigen, als auch absteigend angezeigt werden. Kommt auf die Liga an. Insofern, dachte ich mir, dass es günstig sein könnte diese Eigenschaft zu vergeben... 4. Ja. Das ist aktuell meinem internen Zeitverfahren geschuldet und der Frage, an welcher Stelle ich denn dann nun die Zeiten (aktuell Spielzeit und jede einzelne laufende Strafzeit) intern und später dann auch extern aktualisieren lassen soll. Aktuell läuft auf dem Hauptform einfach nur ein Timer, der die Sekunden und Minuten hoch- oder runterzählt. Verrückt ist im übrigen auch, dass folgendes nach dem Zuweisen der ersten Testdaten nicht möglich ist:
Delphi-Quellcode:
Inkompatible Typen TPenalty und TObject (ein as TObject half auch nicht...)
//erzeugen des penalty und zuweisen der daten
MyPenalty := TPenalty.Create; MyPenalty.Team := 'A'; MyPenalty.TTime:= '00:02:00'; MyPenalty.CTime:= '00:02:00'; MyPenalty.Direction:= 0; //erzeugen der penaltyliste und hinzufügen des penalty myPenalties := TPenalties.Create; mypenalties.AddPenalty(MyPenalty); //erneutes zuweisen des penalty aus der liste, um zugriff zu prüfen MyPenalty := mypenalties[0]; ShowMessage(MyPenalty.Team); |
AW: Strafzeitenverwaltung in eigener Unit
Das liegt daran, dass die Eigenschaft Items deiner TPenalties-Klasse (worauf du mit dem []-Operator implizit zugreifst) von TObjectList geerbt ist. TObjectList wurde so konzipiert, dass es Objekte aller Arten speichern kann, deshalb wurde bei der Deklaration der "Urahn" aller Klassen unter Delphi, TObject, gewählt. Da du aber wie gesagt jedes erdenkliche Objekt in dieser Liste speichern kannst, beschwert sich Delphi: Denn es könnte ja etwas völlig anderes als eine TPenalty-Instanz dort gespeichert sein, und das gäbe dann einen Fehler.
Du hast zwei Möglichkeiten: Entweder du machst einen Typecast auf TPenalty, oder du überschreibst gleich in TPenalties die Items-Property. Das geht etwa so:
Delphi-Quellcode:
Natürlich ist es sinnvoll, in dem Zug auch gleich die Methoden Add, Remove etc zu überschreiben, sodass diese ebenfalls typensicher sind, sprich als Parameter TPenalty statt TObject entgegen nehmen. So sicherst du die Klasse auch gegen Bedienfehler von außen ab, denn solange der Nutzer der Klasse es nicht gerade mit Typecasts darauf anlegt, Fehler zu provozieren, schmeißt der Compiler beim Kompilieren jedes mal einen Fehler und bricht den Kompiliervorgang ab, wenn irgendwo ein falsches Objekt zugewiesen wird.
TPenalties = class(TObjectList)
private function GetItem(Index: integer): TPenalty; procedure SetItem(Index: integer; AItem: TPenalty); public // Siehe dazu die Online-Hilfe zum Stichwort Properties... property Items[Index: integer]: TPenalty read GetItem write SetItem; default; end; function TPenalties.GetItem(Index: integer): TPenalty; begin // per inherited wird der gleichnamige Getter von TObjectList aufgerufen, // das Ergebnis wird nach TPenalty gecastet Result := TPenalty(inherited GetItem(Index)); end; procedure TPenalties.SetItem(Index: integer; AItem: TPenalty); begin // Hier ist kein Typecast nötig, weil TObject der "Urahn" von allen Klassen // und somit auch TPenalty ist, daher also alle Klassen zu den Schnittstellen von // TObject kompatibel sind. inherited SetItem(Index, AItem); end; |
AW: Strafzeitenverwaltung in eigener Unit
Hallo,
vielen Dank für die helfenden Hinweise und das Codebeispiel. Ich denke, dass ich dabei wieder was gelernt habe! Ich würde nun gern Eure Meinung wissen, wie Ihr die laufenden Uhren verwalten/steuern würdet. Aktuell ist die laufende Hauptzeit über einen Timer gelöst. Schön ist sicher anders, aber ich wusste mir nicht anders zu helfen. Soll ich das Increasen/Decreasen der Strafzeitenuhren dann auch hier über den Timer machen, oder wäre das eher nicht so gut!?
Delphi-Quellcode:
//globale Deklaration
var original: TSystemTime; procedure TForm1.int_clockTimer(Sender: TObject); begin //check, how the clock should run case rad_direction.ItemIndex of 0 : begin // clock should run asc original.wsecond:= original.wSecond + 1; if original.wSecond = 60 then begin original.wSecond :=0; original.wMinute :=original.wMinute + 1; end; end; 1 : begin // clock should run desc //stop clock and end case, cause time is up if (original.wSecond = 0) and (original.wMinute = 0) then begin //stop timer int_clock.Enabled := false; end else begin if original.wSecond = 0 then begin original.wSecond := 59; original.wMinute := original.wMinute - 1; end else original.wsecond := original.wSecond - 1; end; end; end; //show the clock - internal lbl_mtime_int.Caption := Format('%.2d:%.2d', [original.wMinute,original.wSecond]); btn_set_data.Enabled := not(int_clock.Enabled); end; |
AW: Strafzeitenverwaltung in eigener Unit
Hallo,
Zitat:
Ich empfehle dir hierfuer uebrigens ein Observer-Pattern. Kurz eine Erklaerung: Du hast eine Klasse GameClock. Diese Methode kapselt einen Timer und hat eine Start/Stop-Methode. Start startet die Uhr, Stop haelt sie an. Beim Starten der Uhr schickst du dann eine Message an alle registrierten PenaltyClocks, dass diese auch starten. Beim Stop funktioniert das analog. Bei dieser Gelegenheit noch nen Hinweis: beachte dass du nie mehr als 2 Strafzeiten laufen lassen darfst. Ein kleines Beispiel: Mannschaft A kassiert 2 Minuten. PP beginnt, Mannschaft A kassiert nochmal 2 Minuten. Jetzt laufen beide Strafzeiten und A spielt in doppelter Unterzahl. Wenn jetzt nochmal eine Strafe fuer A ausgesprochen wird, landen die 2 Minuten auf der Uhr, werden aber nicht runtergezaehlt bis nicht eine der vorherigen Strafzeiten abgelaufen sind. Im Beispiel oben bedeutet das: dein Observer-Pattern darf nur die ersten beiden PenaltyClocks einer Mannschaft starten. Laeuft eine PenaltyClock aus, muss sie die erste nicht laufende PenaltyClock der eigenen Mannschaft starten. So, ich hoffe das macht noch halbwegs Sinn was ich da geschrieben habe ;) Greetz alcaeus |
AW: Strafzeitenverwaltung in eigener Unit
Hallo,
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Die Uhrenklasse habe ich jetzt erstmal so aufgesetzt. Natürlich fehlen da noch die Funktionen "nach aussen hin".
Delphi-Quellcode:
unit unt_game_clock;
interface uses ExtCtrls; type TGameClock = class private GameClock : TTimer; public constructor Create; Procedure StartClock; Procedure StopClock; end; implementation constructor TGameClock.Create; begin inherited; end; Procedure TGameClock.StartClock; begin GameClock.Enabled := True; end; Procedure TGameClock.StopClock; begin GameClock.Enabled := False; end; end. |
AW: Strafzeitenverwaltung in eigener Unit
Hallo,
Zitat:
Zitat:
Code:
Ein paar kurze Erklaerungen: es gibt einen Timer fuer die Zeitmessung, sowie zwei Arrays von PenaltyClocks. Die Methode addPenaltyClock fuegt eine PenaltyClock der GameClock hinzu und uebergibt der PenaltyClock ein paar Informationen.
class GameClock
{ private timer; private homePenaltyClocks; private awayPenaltyClocks; public function create() { this->timer = new Timer; } public function addPenaltyClock(team, clock) { clock->team = team; clock->gameClock = this; if (team == 'home') { this->homePenaltyClocks->push(clock); } else { this->awayPenaltyClocks->push(clock); } } public function start() { this->timer->start(); for (i = 0; i < this->homePenaltyClocks->count() && i < 2; i++) { this->homePenaltyClocks[i]->start(); } for (i = 0; i < this->awayPenaltyClocks->count() && i < 2; i++) { this->awayPenaltyClocks[i]->start(); } } public function stop() { this->timer->stop(); for (i = 0; i < this->homePenaltyClocks->count() && i < 2; i++) { this->homePenaltyClocks[i]->stop(); } for (i = 0; i < this->awayPenaltyClocks->count() && i < 2; i++) { this->awayPenaltyClocks[i]->stop(); } } public function penaltyClockComplete(team) { // Erstes Element aus dem Array entfernen // Wenn dann immer noch 2 Strafzeiten da sind, die zweite starten. } } start() und stop() starten/stoppen die Zeitmessung sowie die der ersten beiden PenaltyClocks (soweit vorhanden). penaltyClockComplete() sagt der GameClock, dass eine Strafzeit abgelaufen ist und eine eventuell weitere gestartet werden muss (das darfst du dann selbst implementieren - ist aber auch keine Kunst).
Code:
Die PenaltyClock enthaelt einen eigenen Timer, da sie unabhaengig zur GameClock laufen muss.
class PenaltyClock
{ private timer; public gameClock; public team; public function create() { this->timer = new Timer(); } public function start() { this->timer->start(); } public function stop() { this->timer->stop(); } public function onTimerComplete() { if (!this->gameClock || !this->team) { return; } this->gameClock->penaltyClockComplete(team); } } start() und stop() brauchen ja keine Informationen. Im onTimerComplete()-Event wird dann (sofern team und gameClock gesetzt sind) der entsprechenden GameClock gesagt, dass eine Strafzeit fuer eine entsprechende Mannschaft abgelaufen ist. Das sollte das Prinzip ein bisschen naeher bringen. Der Code ist nicht der schoenste, aber nachdem du eh Delphi-Code brauchst musst du ihn sowieso neu schreiben - so dass auch weniger Code doppelt vorkommt ;) Zitat:
Zitat:
Zitat:
Greetz alcaeus |
AW: Strafzeitenverwaltung in eigener Unit
Moin,
@alcaeus: Vielen Dank für Deine Mühe. Dein Code riecht sehr nach php. :-) Ich werde ihn mir im Laufe des Tages mal anschauen und versuchen ihn zu implementieren bzw. zu verstehen. Versuchen deshalb, weil ich natürlich nicht ganz faul war. Ich poste hier mal meine bisherige Uhrenklasse. Da ist nun schon fast alles drin, wenngleich in Deinem letzten Post auch noch mal Hinweise drin waren, die ich auch wieder nicht auf dem Zettel hatte (z.b. interne Trennung von 2+2). Insofern läuft das mit der Zeit jetzt schon. Allerdings hat meine Version ein paar Pferdefüsse. - Intervall im Timer bei 1000 -> mir ist nicht klar, wie ich das mit 10 und dem Hochzählen machen soll - Alle Uhren laufen derzeit syncron. Somit kann es hier und da zu diversen kleinen Differenzen kommen - Der Timer im Hauptform steuert die Hauptuhr und liest die Daten aus der PenaltyList aus, die über einen anderen Timer (GameClock) gesteuert werden Gelöst ist schon - das man variabel sagen kann, wieviele Strafzeiten max. gleichzeitig laufen dürfen -- die anderen uhren "rutschen" dann nach - eine Ausgabe ob gerade Powerplay ist und welches Team das hat Und fairerweise hier noch mein Code.
Delphi-Quellcode:
So und jetzt schau ich mir Deins mal näher an.
unit unt_game_clock;
interface uses Windows, Dialogs, SysUtils, ExtCtrls, unt_penalties, unt_penalty, myGlobals, ShellApi; type TGameClock = class private GameClock : TTimer; //this is the timer max_RunningPenalties : byte; //this is a number of max. running penalties per team public Direction : byte; //this is the direction of the running clock IsPowerplay : Boolean; //this is a flag for powerplay PP_OnTeam : byte; //this is a flag for the team, which is under powerplay constructor Create; Procedure StartClock; Procedure StopClock; Procedure GameClockTimer(Sender: TObject); end; implementation uses Math; constructor TGameClock.Create; begin inherited; //create timer and init GameClock := TTimer.Create(nil); max_RunningPenalties := 2; isPowerplay := False; end; procedure TGameClock.GameClockTimer(Sender: TObject); var i, ListOffset : integer; TimeHolder : TSystemTime; h,m,s,msec : Word; IncreaseOffset : Boolean; int_team_a, int_team_b : byte; begin //init of variables int_team_a := 0; int_team_b := 0; ListOffset := 0; //walk over all penalties for i := 0 to PenaltyList.Count -1 do begin if not(PenaltyList.Items[i-ListOffset] <> nil) then exit; if PenaltyList.Items[i-ListOffset].Team = 'A' then inc(int_team_a); if PenaltyList.Items[i-ListOffset].Team = 'B' then inc(int_team_b); //here we check, if we have a powerplay-situation if ((int_team_a <= max_RunningPenalties) and (int_team_b <= max_RunningPenalties)) then begin //set isPowerplay true or false if int_team_a <> int_team_b then IsPowerplay := True else IsPowerplay := False; //set the team, which is on powerplay if int_team_a > int_team_b then PP_OnTeam := 1 else PP_OnTeam := 2; end; //here we check, if a team has more then the allowed max number of penalties //if yes => we dont count the timing for this penalty if ((PenaltyList.Items[i-ListOffset].Team = 'A') and (int_team_a <= max_RunningPenalties)) or ((PenaltyList.Items[i-ListOffset].Team = 'B') and (int_team_b <= max_RunningPenalties)) then begin IncreaseOffset := False; DecodeTime(StrToTime(PenaltyList.Items[i-ListOffset].CTime),h,m,s,msec); TimeHolder.wHour := h; TimeHolder.wMinute := m; TimeHolder.wSecond := s; TimeHolder.wMilliseconds := msec; case PenaltyList.Items[i-ListOffset].Direction of 0 : begin // clock should run asc TimeHolder.wsecond:= TimeHolder.wSecond + 1; if TimeHolder.wSecond = 60 then begin TimeHolder.wSecond :=0; TimeHolder.wMinute :=TimeHolder.wMinute + 1; end; end; 1 : begin // clock should run desc //delete that penalty, cause time is over if (TimeHolder.wSecond = 0) and (TimeHolder.wMinute = 0) then begin //delete it later, after sending out the 0:00 IncreaseOffset := True; end else begin if TimeHolder.wSecond = 0 then begin TimeHolder.wSecond := 59; TimeHolder.wMinute := TimeHolder.wMinute - 1; end else TimeHolder.wsecond := TimeHolder.wSecond - 1; end; end; end; //adwise the new CurrentTime PenaltyList.Items[i-ListOffset].CTime := Format('%.2d:%.2d:%.2d', [TimeHolder.wHour,TimeHolder.wMinute,TimeHolder.wSecond]); //we have to delete an item if IncreaseOffset then begin PenaltyList.Delete(i-ListOffset); inc(ListOffset); end; end; end; end; Procedure TGameClock.StartClock; begin GameClock.OnTimer := GameClockTimer; GameClock.Enabled := True; end; Procedure TGameClock.StopClock; begin GameClock.Enabled := False; end; end. |
AW: Strafzeitenverwaltung in eigener Unit
versuchs mal mit:
Delphi-Quellcode:
ungetestet, sollte aber so gehen...
type
TPenalties = class(TObjectList) private function GetItems(Idx: Integer): TPenalty; procedure SetItems(Idx: Integer; Value: TPenalty); public property Items[Idx: Integer]: TPenalty read GetItems write SetItems; end; ... function TPenalties.GetItems(Idx: Integer): TPenalty; begin Result := (inherited GetItems(Idx)) as TPenalty; end; procedure TPenalties.SetItems(Idx: Integer; Value: TPenalty); begin inherited SetItems(Idx, Value); end; Edit: hm, irgendwie hab ich die letzten Beiträge nicht gesehen... sorry... |
AW: Strafzeitenverwaltung in eigener Unit
Zitat:
Ich hab aber darauf geachtet, es so zu schreiben dass es relativ leicht verwendbar ist. Voraussetzung ist aber, dass Delphi Objektreferenzen speichert und keine Kopien der Objekte. Wie das gehandhabt wird hab ich nicht mehr im Kopf. Ich geh mal in Ruhe deinen Code durch - das dauert bei mir etwas laenger, da ich seit mehr als 4 Jahren (!) keine einzige Zeile Delphi geschrieben hab :oops: Greetz alcaeus |
AW: Strafzeitenverwaltung in eigener Unit
Zitat:
Zitat:
MfG Fabian |
AW: Strafzeitenverwaltung in eigener Unit
Zitat:
Zitat:
Greetz alcaeus |
AW: Strafzeitenverwaltung in eigener Unit
Entschuldigt wenn ich mich einmische, aber ich hätte da ein paar Ideen einzuwerfen.
@TE: in der TPenalty Klasse speichers du die Zeit als string Zitat:
Aber jetzt zu meinem Vorschlag:
Delphi-Quellcode:
Was haltet ihr davon?
TPenalty = class(TObject)
public Team : boolean; // Team (A oder B) (there will be no more than 2 teams a game) Start, // start of penalty Ende : LongInt; // end of penalty (oder auch Dauer, wenn man will) constructor Create; overload; constructor Create(fTeam : boolean; fStart, fEnde : LongInt); overload; end; TPenalties = class(TObjectList) private function GetItem(Index: integer): TPenalty; procedure SetItem(Index: integer; Value: TPenalty); public property Items[Index: integer]: TPenalty read GetItem write SetItem; default; end; TGameClock = class private PenaltyList : TPenalties; max_RunningPenalties : byte; isPowerplay : boolean; GameClock : TTimer; Clock : LongInt; procedure Tick(Sender: TObject); public constructor Create; procedure StartGame; procedure StartClock; procedure StopClock; // will hold the logic of no more than 2 penalties a team procedure AddPenalty(fTeam : boolean; fDuration : LongInt); end; implementation {$R *.dfm} function TPenalties.GetItem(Index: Integer): TPenalty; begin Result := (inherited GetItem(Index)) as TPenalty; end; procedure TPenalties.SetItem(Index: Integer; Value: TPenalty); begin inherited SetItem(Index, Value); end; constructor TGameClock.Create; begin inherited; //create timer and init GameClock := TTimer.Create(nil); max_RunningPenalties := 2; isPowerplay := False; end; procedure TGameClock.StartClock; begin GameClock.OnTimer := Tick; GameClock.Enabled := True; end; procedure TGameClock.StartGame; begin Clock := 0; GameClock.Enabled := True; end; procedure TGameClock.StopClock; begin GameClock.Enabled := False; end; procedure TGameClock.AddPenalty(fTeam : boolean; fDuration : LongInt); var i : integer; FirstRunning, Last, New : TPenalty; begin for i := 0 to PenaltyList.Count - 1 do begin if (PenaltyList.Items[i].Start < Clock) and (PenaltyList.Items[i].Ende > Clock) and (PenaltyList.Items[i].Team = fTeam) then begin if not Assigned(FirstRunning) then begin FirstRunning := PenaltyList.Items[i]; end else begin Last := PenaltyList.Items[i]; end; end; end; // FirstRunning is now the first running penalty and // Last is the last given penalty besides the first running // first set standard values New := TPenalty.Create(fTeam, Clock, Clock + fDuration); if Assigned(FirstRunning) then begin if Assigned(Last) then begin // if two or more add penalty time at end of last penalty New.Start := Last.Ende; New.Ende := Last.Ende + fDuration; end; end; PenaltyList.Add(New); end; procedure TGameClock.Tick(Sender: TObject); var i, ListOffset : integer; int_team_a, int_team_b : byte; PP_OnTeam : byte; begin //init of variables int_team_a := 0; int_team_b := 0; // next Tick of clock Clock := Clock + GameClock.Interval; // add intervall to our clock for i := 0 to PenaltyList.Count - 1 do begin if (PenaltyList.Items[i].Start <= Clock) and (PenaltyList.Items[i].Ende > Clock) then begin if PenaltyList.Items[i].Team then inc(int_team_a) else inc(int_team_b); end; end; //here we check, if we have a powerplay-situation if ((int_team_a <= max_RunningPenalties) and (int_team_b <= max_RunningPenalties)) then begin IsPowerplay := int_team_a <> int_team_b; //set the team, which is on powerplay if IsPowerplay then begin if int_team_a > int_team_b then PP_OnTeam := 1 else PP_OnTeam := 2; end; end; //walk over all penalties for i := 0 to PenaltyList.Count - 1 do begin // only running ones if (PenaltyList.Items[i].Start <= Clock) and (PenaltyList.Items[i].Ende > Clock) then begin // show penalties // assign to whatever it should display (differentiation not done) // start of penalty TimeToStr(PenaltyList.Items[i].Start / 1000); // "/ 1000" because all times are stored in milliseconds // start of penalty TimeToStr(PenaltyList.Items[i].Ende / 1000); // if the clock is running the backwards then // TimeToStr(gamelength - PenaltyList.Items[i].Ende / 1000); // gamelength is the length of the game in milliseconds end; end; // at the end show current time TimeToStr(Clock / 1000); end; { TPenalty } constructor TPenalty.Create; begin inherited; end; constructor TPenalty.Create(fTeam: boolean; fStart, fEnde: LongInt); begin Create; Team := fTeam; Start := fStart; Ende := fEnde; end; Ich gehe die Sache andersherum an. Die Penalty Klassen sind nur für die "Datenablage" und die Timerklasse koordiniert alles. Damit habe ich dann auch gleich eine Historie aller Strafen und kann später eine Auswertung implementieren wenn ich will. Gruß David |
AW: Strafzeitenverwaltung in eigener Unit
Ich find es generell nicht besonders sinnvoll, Boolean für Daten zu missbrauchen, die nicht "wahr" oder "falsch" sind. Das verwirrt nur. Wenn du einen Datentyp mit 2 Werten haben willst, nimm dafür ein Enum:
Delphi-Quellcode:
[edit]
TTeam = (tmTeamA, tmTeamB);
TPenalty = class(TObject) public Team : TTeam; ... Und ich würde etwas mehr auf die Namenskonventionen achten: Felder sollte man mit dem Präfix F (für Field) beginnen. Also
Delphi-Quellcode:
statt
TPenalty = class(TObject)
public FTeam : TTeam; FStart, FEnde : LongInt;
Delphi-Quellcode:
Parameter von Funktionen erhalten hingegen kein Präfix. Wenn es Mehrdeutigkeiten oder Namenskonflikte gibt, wird ihnen manchmal ein A vorangestellt. F als Präfix für Parameter ist jedoch irreführend, da F wie gesagt für Field steht.
TPenalty = class(TObject)
public Team : TTeam; Start, Ende : LongInt; Und ich würde noch darauf achten, durchgehend englische Bezeichner zu verwenden, aber das ist Geschmackssache. Ansonsten ist dein Vorschlag, die Objekte als reine Datenobjekte zu handhaben, imo gut. Man sollte die Datenspeicherung und die Logik immer versuchen zu trennen. Hier hat es außerdem den Vorteil, dass man mit nur einem Timer auskommt. [/edit] |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:47 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