|
![]() |
|
Registriert seit: 7. Jun 2006 Ort: Karlsruhe 3.724 Beiträge FreePascal / Lazarus |
#1
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; Geändert von Namenloser (18. Jun 2010 um 21:59 Uhr) |
![]() |
Registriert seit: 26. Jul 2002 Ort: Sachsen 1.198 Beiträge Delphi XE5 Professional |
#2
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;
Danke
Tom |
![]() |
Registriert seit: 11. Aug 2003 Ort: München 6.537 Beiträge |
#3
Hallo,
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!? 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
Andreas B.
Die Mutter der Dummen ist immer schwanger. Ein Portal für Informatik-Studenten: ![]() |
![]() |
Registriert seit: 26. Jul 2002 Ort: Sachsen 1.198 Beiträge Delphi XE5 Professional |
#4
Hallo,
Hallo, Genauer gesagt musst du das sogar.
Im internationalen Eishockey wird die letzte Minute inkl. Zehntelsekunden abgearbeitet - wenn jetzt jemand 52.5 Sekunden vor Schluss nen Penalty kommt darfst du diese halbe Sekunde nicht untern Tisch fallen lassen.
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
![]() 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.
Danke
Tom Geändert von torud (19. Jun 2010 um 12:56 Uhr) |
![]() |
Registriert seit: 11. Aug 2003 Ort: München 6.537 Beiträge |
#5
Hallo,
Ups, dass hatte ich so detailliert nicht auf dem Zettel. Vielleicht macht es hier ja auch Sinn in der Uhrenklasse die Zehntel optional zu halten. Ich müsste dann aber sozusagen aus den 1000ms im Timer eine 100 machen. Sollte ich die 1000 bis zum Erreichen der Minute lassen und dann wenn ich Minute = 0 und Sekunde > 0 ist, den Timer.Interval auf 100 stellen oder grundsätzlich immer?
Das hier verstehe ich nicht ganz. Der Timer würde dann im private der Uhrenklasse gekapselt werden. So weit ist alles klar. Start-Stop ist auch klar. Was soll aber passieren, wenn ich die Message an die PenaltyClocks (die es ja aktuell noch nicht gibt) sende? Ich dachte, dass der gekapselte Timer alle Uhren "steuert", also die Hauptuhr und die Strafzeituhren zum laufen bringt, indem ER selbst die einzelnen Uhren "zählt". Ist es also so zu verstehen, dass der gekapselte Timer nur die anderen Timer anschieben/anhalten soll???
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 ![]() Jo, davon hatte ich was gehört. Mir ist noch nicht ganz klar, wie das bei den 5-10 und 20-Minuten-Strafen ist, wo ja der Ausschluss des Spielers noch hinzukommt. Bei manchen Strafen ist es wohl so, dass DER oder EIN Spieler raus muss, aber natürlich nicht die vollen 10 Minuten absitzt. Dann gibt es da noch die Regel, dass die 2 Minuten-Strafzeit sofort gelöscht wird, wenn das in Unterzahl befindliche Team ein Tor "bekommt", respektive, dass bei einer 2+2-Strafe die ersten 2 Minuten gelöscht werden und direkt die 2. Strafzeit losläuft. Ziemlich irre!
![]()
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.
Jo, im Grossen und Ganzen scheinst Du nicht nur Ahnung vom Programmieren zu haben, sondern auch einiges über die Regeln im Eishockey zu wissen. Cool!
![]() Greetz alcaeus
Andreas B.
Die Mutter der Dummen ist immer schwanger. Ein Portal für Informatik-Studenten: ![]() |
![]() |
Registriert seit: 26. Jul 2002 Ort: Sachsen 1.198 Beiträge Delphi XE5 Professional |
#6
Moin,
@alcaeus: Vielen Dank für Deine Mühe. Dein Code riecht sehr nach php. ![]() 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.
Danke
Tom |
![]() |
Registriert seit: 11. Aug 2003 Ort: München 6.537 Beiträge |
#7
@alcaeus: Vielen Dank für Deine Mühe. Dein Code riecht sehr nach php.
![]() ![]() 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 ![]() Greetz alcaeus
Andreas B.
Die Mutter der Dummen ist immer schwanger. Ein Portal für Informatik-Studenten: ![]() |
![]() |
Themen-Optionen | Thema durchsuchen |
Ansicht | |
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 |
LinkBack |
![]() |
![]() |