Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Do Jul 03, 2025 01:54

Foren-Übersicht » Programmierung » Allgemein
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 9 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags:
BeitragVerfasst: Di Mär 18, 2003 11:46 
Offline
DGL Member
Benutzeravatar

Registriert: Sa Mär 15, 2003 14:07
Beiträge: 7
Bei einem Echtzeitspiel muss man dynamisch Objekte eines vorher definierten Typs erstellen und wieder löschen, wenn ihre Zeit abgelaufen ist.
Meine Frage ist, wie man solche Objekttypen möglichst günstig anlegt, so dass die Engine eine theoretisch unbegrenzte Menge von Objekten verwalten kann. Bisher habe ich es immer mit arrays von records gemacht, aber was ist jetzt, wenn es noch verschiedenartige Objekte geben soll? Soll ich die einheitliche record-Definition einfach erweitern oder gibt es da eine völlig andere Strategie, seine Objekte zu verwalten?


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Mär 18, 2003 11:53 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Sep 23, 2002 19:27
Beiträge: 5812
Programmiersprache: C++
Die einfachste Methode, ein dynamisches Array aus verschiedenen Objekten zu erstellen besteht in der Nutzung von Zeigern, wie an folgendem Beispiel leicht zu sehen :

Code:
  1.  
  2. ObjArray : array of Pointer;
  3. ...
  4. procedure TEngine.ManageObject;
  5. var
  6.  i : Integer;
  7. begin
  8. if Length(ObjArray) > 0 then
  9.  for i := Low(ObjArray) to High(ObjArray do
  10.   begin
  11.   if ObjArray[i]^ is TPlayerObject then
  12.     with ObjArray[i]^ as TPlayerObject do
  13.       TPlayerObject(ObjArray[i])^.Update;
  14.   if ObjArray[i]^ is TBuilding then
  15.     with ObjArray[i]^ as TBuilding do
  16.       TBuilding (ObjArray[i])^.Update;
  17. end;
  18. ...
  19.  


Der Code ist nicht getestet, kann also sein das du da noch ein paar Kleinigkeiten ändern musst!

Hoffe der Code ist verständlich.Wichtig sinde folgende Operatoren/Funktionen :
Operator IS : Prüft ob eine Objekt (bzw ein Objekt hinter einem Zeiger) von einem bestimmten Typ is
Operator AS : Behandelt ein Objekt (bzw ein Objekt hinter einem Zeiger) als den hinter AS angegebene Objekttyp
Typecast : Wandelt einen Pointer (bzw ne andere Variable in den angegebenen Typ um (z.B. Zeile TBuilding (ObjArray[i])^.Update)

Mit dieser Methode lässt sich recht leicht ein Array mit verschiedenen Objekten verwalte.Alternativ kannste natürlich auch statt des dynamischen Arrays ein Objekt des Typs TList nutzen, welches eigentlich nichts weiter als eine leicht verwaltbare (z.B. via Add, Delete, Insert) Liste von Pointern ist.

_________________
www.SaschaWillems.de | GitHub | Twitter | GPU Datenbanken (Vulkan, GL, GLES)


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Mär 18, 2003 16:23 
Offline
DGL Member
Benutzeravatar

Registriert: Sa Mär 15, 2003 14:07
Beiträge: 7
Hey, das klingt gut! Werd mich gleich daran versuchen.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Mär 18, 2003 16:40 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Son of Satan hat geschrieben:
Code:
  1.  
  2. ObjArray : array of TBasisklasse; // der name sollte anders sein
  3. ...
  4. procedure TEngine.ManageObject;
  5. var
  6.  i : Integer;
  7. begin
  8. if Length(ObjArray) > 0 then
  9.  for i := Low(ObjArray) to High(ObjArray do
  10.   begin
  11.   if ObjArray[i]^ is TPlayerObject then
  12.     with ObjArray[i]^ as TPlayerObject do
  13.       TPlayerObject(ObjArray[i])^.Update;
  14.   if ObjArray[i]^ is TBuilding then
  15.     with ObjArray[i]^ as TBuilding do
  16.       TBuilding (ObjArray[i])^.Update;
  17. end;
  18. ...
  19.  

Was mir gerade aufgefallen ist.
Man könnte das noch wesentlich vereinfachen. So wie SOS es vorgeschlagen hat geht es ohne Zweifel aber ich bin nun mal faul. ;)

Wenn du dann eine Basisklasse erstellst und TPlayerObject und TBuilding davon ableitest, dann wäre es viel einfacher.
Du müsstest alles was beide (TPlayerObject und TBuilding) Klassen besitzen in die Basisklasse übernehmen. Ich dachte danbei an die methode Update.
Und dann bräuchtest du nur noch nach der Basisklasse casten und dir wäre es sozusagen egal ob es sich um ein gebäude oder einen Spieler handelt.

Also so wie ich mir das vorstellen würde.
Code:
  1.  
  2. ObjArray : array of Pointer;
  3. ...
  4. procedure TEngine.ManageObject;
  5. var
  6.   i : Integer;
  7. begin
  8.   if Length(ObjArray) > 0 then begin
  9.     for i := Low(ObjArray) to High(ObjArray)
  10.       do ObjArray[i].Update;
  11.   end;
  12. end;
  13. ...
  14.  

Da es sich hier um Klassen handelt werden so oder so bloß deren pointer gespeichert also könnte man auch gleich die Klassen ablegen. Und dir wäre es an dieser Stelle egal um was es sich handelt du willst ja nur das Update aufrufen. Im spezialfall könntest du immer noch schauen um was es sich handelt und dieses dann expliziet casten und dinge tun.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Mär 18, 2003 20:03 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Mai 06, 2002 20:27
Beiträge: 479
Wohnort: Bremen
Ich würde anstatt eines einfachen Arrays die Klasse TList verwenden. Die kann im Prinzip das gleiche wie ein dynamisches Array of Pointer aber gibt dir ein Paar ganz nützliche Funktionen an die Hand. Z.B. kannst du bei TList ganz einfach Objekte hinzufügen, löschen und was du sonst noch zur verwaltung brauchst.
Außerdem ist eine TList ziemlich schnell - besser bekommst du's auch nicht hin.

Dann würde ich einen ObjektManager bauen, der von TList erbt oder TList verwendet und alle Objekte Im Spiel managed. Natürlich kannst du diese Funktionen auch direkt in die Engine einbauen, aber eine zusätzliche Abstraktion wäre vielleicht ganz ratsam.

_________________
Selber Denken macht klug!


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Mär 18, 2003 20:37 
Offline
DGL Member

Registriert: Mo Jan 20, 2003 20:10
Beiträge: 424
Wohnort: nähe Starnberg
Ab Delphi 5 steht TObjectList zu Verfügung, welches die reservierten Objekte automatisch freigeben kann.

Code:
  1.  
  2. TBaseObject = class(TObject)
  3. ....
  4.   Methoden/Properties die überall gleich sind, z. B.:
  5.   procedure Render; virtual; abstract;
  6. ....
  7. end;
  8.  
  9. TBaseObjectList = class(TObjectList)
  10. protected
  11.   function GetItem(Index : Integer) : TBaseObject;
  12. public
  13.   property Items[Index : Integer] : TBaseObject read GetItem; default;
  14. end;
  15.  
  16. function TBaseObject.GetItem(Index : Integer) : TBaseObject;
  17. begin
  18.   Result := (inherited Items[Index] as TBaseObject);
  19. end;
  20.  


So könnte eine Liste mit Objekten angesprochen werden:

Code:
  1.  
  2. ...
  3. begin
  4.   for Loop := 0 to (BaseList.Count - 1) do begin
  5.     BaseList[Loop].Render;
  6.   end;
  7. end;
  8. ...
  9.  


Damit hast Du schon eine einfache Objektverwaltung.

KidPaddle

_________________
http://www.seban.de


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Mär 19, 2003 12:38 
Offline
DGL Member
Benutzeravatar

Registriert: Sa Mär 15, 2003 14:07
Beiträge: 7
Wow, das sind ganz schön viele Ideen für meinen kleinen Kopf!

Ich fasse zusammen (korrigiert mich, wenn ich mich irre):

- Sobald ich den Operator IS benutze, muss das anliegende Element des Pointer-Arrays eine Instanz (nennt man das so?) einer Klasse sein.

- .Update lässt mich auf die einzelnen Elemente der Instanz zugreifen.

- Lossy eX meint, dass ich alle meine verschiedenen Objektdefinitionen in eine Basisklasse aufnehmen kann, um die Objekte nicht generell voneinander unterscheiden zu müssen. Das käme dann wieder einem array of record recht nahe, es sei denn, man kann die Objekte doch irgendwie unterscheiden. Kann man das?
Das Andere ist wiederum, dass die Größe, die meine Objekte im Speicher belegen werden, je nach Objekttyp recht unterschiedlich sein können. Ich rechne damit, dass ein 'großes' Objekt etwa 5-10x so viel Speicher einnimmt wie ein 'kleines' und sehr viel komplexere Physik (ebenso KI) benötigt.

Frage: Was macht .Update alles? Muss ich .Update als Methode erst noch definieren?

- TList in der unit Classes ist eine leichter zu verwaltende Form des array of Pointer. Lithander schlägt vor, dass die Unterscheidungsroutinen für die in der TList gespeicherten Instanzen von Objektklassen sich zentral in einem Objektmanager befinden. So könnte die Engine also alle Objekte gleich behandeln, der Objektmanager setzt die einheitlichen Befehle wie 'Objekt_zeichnen' um in die für den jeweiligen Objekttyp spezifische Form. So ein Objektmanager könnte sehr nützlich sein, um die Übersichtlichkeit und somit Ausbaufähigkeit der Engine zu wahren.

- TObjectList bietet mir die Möglichkeit, einzelne Objekte aus der Objektliste zu isolieren und separat zu behandeln.

- Die Verwendung von TComponent anstatt TObjectList wäre auch zu überlegen.


Momentan tendiere ich dazu, jeden Objekttyp in eine eigene Klasse zu packen und die Basisroutinen wie 'rendern' gleích mit einzubauen.
Eine Frage noch: Wenn ich jetzt den Pointer-array benutze und recht verschiedenartig große Objekte darin ständig binde und wieder freigebe, wird das nicht auf die Dauer den heap recht zerstückeln? Ist das bei der TList besser gelöst und wie?


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Mär 19, 2003 16:29 
Offline
DGL Member

Registriert: Mi Okt 16, 2002 15:06
Beiträge: 1012
Hi,

also ich hab bis jetzt alles egal ob Prog oder Spiel auf Class bzw record basis gemacht und hab mir Hilfsfunktionen geschrieben um einträge aus nem Dynamischen Array zu löschen, verschieben, ändern hinzufügen usw.

Da ich finde das manchmal die Pointer art manchmal etwas ankotzen, grad wenn man mit vererbung arbeitet und Pointer man immer auf speicher achten muss.

z.b. so (Dynamisches Shortcut system für Xenorate):

Code:
  1.  
  2.  
  3. type
  4.   TXHotKeyProgram = (
  5.     xhpNone,
  6.     xhpNext,
  7.     xhpPrev,
  8.     xhpStop,
  9.     xhpPause,
  10.     xhpPlay
  11.   );
  12.  
  13.   TXHotKeyDownKey = (
  14.     xdkNone,
  15.     xdkAlt,
  16.     xdkShift,
  17.     xdkCtrl
  18.   );
  19.  
  20.   TXHotKey = record
  21.     KeyCode : Word;
  22.     DownKey : TXHotKeyDownKey;
  23.     Prog    : TXHotKeyProgram;
  24.   end;
  25.  
  26.   TXHotKeys = class(TObject)
  27.   private
  28.     Keys : array of TXHotKey;
  29.     procedure Clear;
  30.     function GetHotKey(Index : Integer) : TXHotKey;
  31.     function GetCount : Integer;
  32.   public
  33.     constructor Create;
  34.     destructor Destroy;
  35.     procedure Add(const _KeyCode : Word; const _DownKey : TXHotKeyDownKey; const _Prog : TXHotKeyProgram);
  36.     procedure Delete(const _Index : Integer);
  37.     procedure Edit(const _Index : Integer; const _KeyCode : Word; const _DownKey : TXHotKeyDownKey; const _Prog : TXHotKeyProgram);
  38.     property HotKey[Index: Integer]: TXHotKey read GetHotKey;
  39.   end;
  40.  
  41. implementation
  42.  
  43. constructor TXHotKeys.Create;
  44. begin
  45.   inherited;
  46. end;
  47.  
  48. destructor TXHotKeys.Destroy;
  49. begin
  50.   inherited;
  51.   Clear;
  52. end;
  53.  
  54. procedure TXHotKeys.Clear;
  55. begin
  56.   SetLength(Keys, 0);
  57. end;
  58.  
  59. function TXHotKeys.GetCount : Integer;
  60. begin
  61.   Result := Length(Keys);
  62. end;
  63.  
  64. function TXHotKeys.GetHotKey(Index : Integer) : TXHotKey;
  65. begin
  66.   // Standard werte
  67.   Result.KeyCode := 0;
  68.   Result.Prog := xhpNone;
  69.   // Prüfen ob der bereich gültig ist
  70.   if (Index < 0) or (Index > High(Keys)) then Exit;
  71.  
  72.   // Bereich gültig, also ausgeben
  73.   Result := Keys[Index];
  74. end;
  75.  
  76. function TXHotKeys.GetCount : Integer;
  77. begin
  78.   Result := Length(Keys);
  79. end;
  80.  
  81. procedure TXHotKeys.Add(const _KeyCode : Word; const _DownKey : TXHotKeyDownKey; const _Prog : TXHotKeyProgram);
  82. var
  83.   Count : Integer;
  84. begin
  85.   Count := Length(Keys)+1;
  86.   SetLength(Keys, Count);
  87.   Keys[Count-1].KeyCode := _KeyCode;
  88.   Keys[Count-1].Prog := _Prog;
  89.   Keys[Count-1].DownKey := _DownKey;
  90. end;
  91.  
  92. procedure TXHotKeys.Delete(const _Index : Integer);
  93. var
  94.   OldEntrys      : array of TREintrag;
  95.   I, Count, L, H, ActIndex : Integer;
  96. begin
  97.   // SwissDelphiCenter Methode
  98.   if _Index > High(Keys) then Exit;
  99.   if _Index < Low(Keys) then Exit;
  100.   if _Index = High(Keys) then
  101.   begin
  102.     SetLength(Keys, Length(Keys) - 1);
  103.     Exit;
  104.   end;
  105.   Finalize(Keys[_Index]);
  106.   System.Move(Keys[_Index +1], Keys[_Index],
  107.   (Length(Keys) - _Index -1) * SizeOf(TXHotKey) + 1);
  108.   SetLength(Keys, Length(Keys) - 1);
  109.  
  110.   // Meine Methode
  111.   // Find ich besser, da oberige Methode nur für Records ohne Vererbung gedacht ist
  112.   // Da könnte man auch genauso gut Pointer verwenden :P
  113.   {
  114.  
  115.   // Prüfen ob der Index im Gültigem Bereich ist
  116.   // Wenn Nicht, dann raus aus der Prozedur
  117.   if (_Index > High(Keys)) or (_Index < Low(Keys)) then Exit;
  118.  
  119.   // Anzahl Einträge, Low und High in Temporäre Variable speichern
  120.   Count := GetCount;
  121.   L := Low(Keys);
  122.   H := High(Keys);
  123.  
  124.   // Alte Einträge vorher in das Temporäre Array speichern
  125.   // Array vorher initialiseren, usw
  126.   SetLength(OldEntrys, Count);
  127.   For I := L to H do
  128.     OldEntrys[I] := Keys[I];
  129.  
  130.   // Neue grösse für Einträge festlegen
  131.   SetLength(Keys, Count-1);
  132.   // Alte Daten, AUSSER den Eintrag den wir auslassen (löschen) möchten ins Array kopieren
  133.   // ActIndex auf 0 setzen, das ist unser Startindex
  134.   ActIndex := 0;
  135.   For I := L to H do
  136.   begin
  137.     // Prüfen ob I der schleife nicht der Index nicht
  138.     if I <> _Index then
  139.     begin
  140.       // Alten Eintrag von I in den neuen Eintrag ActIndex hinzufügen
  141.       Keys[ActIndex] := OldEntrys[I];
  142.       // ActIndex um 1 erhöhen
  143.       Inc(ActIndex);
  144.     end;
  145.   end;
  146.  
  147.   }
  148. end;
  149.  
  150. procedure TXHotKeys.Edit(const _Index : Integer; const _KeyCode : Word; const _DownKey : TXHotKeyDownKey; const _Prog : TXHotKeyProgram);
  151. begin
  152.   Keys[_Index].KeyCode := _KeyCode;
  153.   Keys[_Index].Prog := _Prog;
  154.   Keys[_Index].DownKey := _DownKey;
  155. end;
  156.  
  157.  


bidde schön, hoffe ihr könnt was damit anfangen.

matane,
Finalspace


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 9 Beiträge ] 
Foren-Übersicht » Programmierung » Allgemein


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 7 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.011s | 14 Queries | GZIP : On ]