![]() |
Delphi-Version: 5
Objekte aus Textdatei erstellen
Moin Moin,
Ich arbeite grad' an einem kleinen Spiel mit der Andorra 2D-Engine, wobei jedoch mein Problem eher Delphi bezogen ist. Das Problem liegt in der Mapgenerierung, die in Textdateien abgespeichert wird. Bis jetzt sah die Mapgenerierung so aus: Es steht ein Raster, dass aus Zahlen besteht, in etwa so: 1;2,3; 5;2;7; 8;2;3; Die Zahlen zeigen dabei die Bilder für den Maphintergrund an. Die einzelnen Bilder wurden zuvor in eine Liste mit den entsprechenden Nummern geladen. Anschließend werden beim Laden der Map für jede Zahl ein Objekt an der entsprechenden Stelle und passenden Bildchen erstellt. Funktionieren tut's wunderbar, nur reicht das noch nicht für eine richtige Map: Wie stelle ich es an, dass Objekte von verschiedenen Klassen gebildet werden, abhängig von dem was in der Textdatei steht? Beispiel (Mehrere Ebenen sind bereits möglich): TBaum; 0; TBaum; TBrett; 0; TNpc; 0; TBaum; THaus; Dabei muss ne praktische Lösung her, da ja noch ne ganze Menge Objekte dazukommen könnten. Ich hatte mir schon gedacht, dass ich irgendwie alle Objekte in eine Liste reinpacke und dann bei der Generierung daraus gesucht wird... aber wie genau das gehen soll weiß ich auch nicht. |
AW: Objekte aus Textdatei erstellen
Warum benutzt du denn Textdateien? Hat das einen Grund?
Denn viel simpler wäre es, wenn du deinen Klassen jeweils die Methoden LoadFromStream und SaveToStream spendieren würdest, die das ganze binär speichern und laden. Dazu dann noch in der äußersten Ebene LoadFromFile und SaveToFile, die einen TFileStream aufmachen und an deine Streammethoden weitergeben, schon bist du fertig. Das ist sehr viel einfacher, nur die Dateien sind manuell schlecht lesbar. Aber wenn das nicht wichtig ist, würde ich das eher so machen. |
AW: Objekte aus Textdatei erstellen
Lesbar muss es nicht sein, dafür mit nem selbst gemachten Editor erstellbar, aber das sollte ja da auch kein Problem sein.
Falls ich das jetzt richtig verstanden habe (TFileStream ist Oberklasse von allen Objekten?), wird das auch nicht funktionieren, da ich die Objekte von einer anderen Klasse der Sprite Engine ableiten muss. Ansonsten müsste ich vielleicht große Teile der Engine umschreiben... und das trau ich mir nicht zu :D Und um ehrlich zu sein arbeite ich auch nicht alleine daran. Ein paar andere und ich wollten einfach mal ein Spiel selber machen und ein paar Erfahrungen sammeln. Daher die simplen Texdateien :wink:. |
AW: Objekte aus Textdatei erstellen
Hier mal ein Beispiel:
Delphi-Quellcode:
Und benutzen kannst du das dann so:
unit UnitXYZ;
interface uses Classes, SysUtils; type TStringReaderWriter = class helper for TStream function ReadString: string; procedure WriteString(const AValue: string); end; TMyGameObject = class private type TExample = class private var FExampleString: string; FExampleInteger: LongInt; procedure SetExampleInteger(const Value: LongInt); procedure SetExampleString(const Value: string); public procedure SaveToStream(const ATarget: TStream); procedure LoadFromStream(const ASource: TStream); property ExampleString: string read FExampleString write SetExampleString; property ExampleInteger: LongInt read FExampleInteger write SetExampleInteger; end; var FInterestingString: string; FExampleObject: TExample; procedure SetInterestingString(const Value: string); public constructor Create; destructor Destroy; override; procedure SaveToStream(const ATarget: TStream); procedure LoadFromStream(const ASource: TStream); procedure SaveToFile(const AFilename: String); procedure LoadFromFile(const AFilename: String); property InterestingString: string read FInterestingString write SetInterestingString; property ExampleObject: TExample read FExampleObject; end; implementation { TMyGameObject.TExample } procedure TMyGameObject.TExample.LoadFromStream(const ASource: TStream); begin FExampleString := ASource.ReadString; ASource.ReadBuffer(FExampleInteger, SizeOf(FExampleInteger)); end; procedure TMyGameObject.TExample.SaveToStream(const ATarget: TStream); begin ATarget.WriteString(FExampleString); ATarget.WriteBuffer(FExampleInteger, SizeOf(FExampleInteger)); end; procedure TMyGameObject.TExample.SetExampleInteger(const Value: LongInt); begin FExampleInteger := Value; end; procedure TMyGameObject.TExample.SetExampleString(const Value: string); begin FExampleString := Value; end; { TStringReaderWriter } function TStringReaderWriter.ReadString: string; var ResultString: AnsiString; StringSize: Integer; begin Result := ''; ReadBuffer(StringSize, SizeOf(StringSize)); SetLength(ResultString, StringSize); ReadBuffer(Pointer(ResultString)^, StringSize); {$ifdef UNICODE} Result := Utf8ToString(ResultString); {$else} Result := Utf8Decode(ResultString); {$endif} end; procedure TStringReaderWriter.WriteString(const AValue: string); var StringSize: Integer; StringToSave: AnsiString; begin StringToSave := Utf8Encode(AValue); StringSize := Length(StringToSave); WriteBuffer(StringSize, SizeOf(StringSize)); WriteBuffer(Pointer(StringToSave)^, StringSize); end; { TMyGameObject } constructor TMyGameObject.Create; begin FExampleObject := TExample.Create; end; destructor TMyGameObject.Destroy; begin FExampleObject.Free; inherited; end; procedure TMyGameObject.LoadFromFile(const AFilename: String); var FileContents: TFileStream; begin FileContents := TFileStream.Create(AFilename, fmOpenRead); try LoadFromStream(FileContents); finally FileContents.Free; end; end; procedure TMyGameObject.LoadFromStream(const ASource: TStream); begin FInterestingString := ASource.ReadString; FExampleObject.LoadFromStream(ASource); end; procedure TMyGameObject.SaveToFile(const AFilename: String); var FileContents: TFileStream; begin FileContents := TFileStream.Create(AFilename, fmCreate); try SaveToStream(FileContents); finally FileContents.Free; end; end; procedure TMyGameObject.SaveToStream(const ATarget: TStream); begin ATarget.WriteString(FInterestingString); FExampleObject.SaveToStream(ATarget); end; procedure TMyGameObject.SetInterestingString(const Value: string); begin FInterestingString := Value; end; end.
Delphi-Quellcode:
Auf diese Weise ist nach außen immer alles gekapselt und du kannst die Objekte nach außen immer einfach benutzen ohne dich um die Interna zu kümmern.
var
MyGameData: TMyGameObject; begin if dlgOpen.Execute then begin MyGameData := TMyGameObject.Create; try MyGameData.LoadFromFile(dlgOpen.Filename); ShowMessage(MyGameData.InterestingString); finally MyGameData.Free; end; end; |
AW: Objekte aus Textdatei erstellen
Okay, danke für die ausführliche Antwort!
Wir werden es dann mal so versuchen, aber das Hauptproblem ist ja immer noch vorhanden: Ich versuch's mal anhand des aktuellen Codes zu erklären:
Delphi-Quellcode:
Raster ist hierbei sozusagen die verarbeitete Textdatei als einfaches Array, wobei es dann als Beispiel '3' ausgeben würde und dann das entsprechende Bild ausgewählt werden würde.
//Map bauen
for i := 1 to reader.GetRasterX do // 1 bis 32 (Breite des Rasters) begin for j := 1 to reader.GetRasterY do begin with TMapUB.Create(AdSpriteEngine) do begin //Map an Hand der Textdatei bauen Image := AdImageList1.Find(raster[j - 1, i - 1]); // * //Immer in 96 Abständen laden x := i * 96; y := j * 96; end; end; end; Wir wollen ja aber nun, dass genau an dieser Stelle zwischen einzelnen Klassen statt einzelnen Bildern unterschieden wird. |
AW: Objekte aus Textdatei erstellen
Ich würde es mir dann in etwa so etwa in der Art vorstellen:
Delphi-Quellcode:
raster[j - 1, i - 1].Create
Sodass das Programm eben das hier ausführen würde:
Delphi-Quellcode:
TBaum1.Create
|
AW: Objekte aus Textdatei erstellen
Darauf passt das Factory-Pattern, d.h. eine Factory, bei der sich die Klassen z.B. selbst registrieren (z.B. in initialization der Units). Diese Factory kennt dann die Klassen und kann die auch erstellen, wenn du dort dann nach einer Klasse anfragst.
|
AW: Objekte aus Textdatei erstellen
Zitat:
Am Beispiel aus dem Internet:
Delphi-Quellcode:
Dieser Teil wird ja bei der Factory sozusagen "ausgelagert", wobei jedoch die if-anweisungen bleiben. Problem beim Spiel wird's aber sein, dass ganz viele Objekte dazukommen werden und 100 if-Anweisungen unpraktisch wären. Mein Wunsch wär es ja jetzt das auf ein paar Zeilen zu reduzieren.
if ASelector = 'Furniture' then
Result := TFurniture.Create else if ASelector = 'Chair' then Result := TChair.Create else if ASelector = 'Cupboard' then Result := TCupboard.Create else if ASelector = 'Coffee Table' then Result := TCoffeeTable.Create else if ASelector = 'Kitchen Table' then Result := TKitchenTable.Create else if ASelector = 'Table' then Result := TTable.Create Wie gesagt, ich bin noch grad' am Lesen. |
AW: Objekte aus Textdatei erstellen
Vielleicht mal etwas allgemeiner:
Man kann seine Klassen registrieren, damit Objekte dieser Klasse zur Laufzeit erzeugt werden können. Das ist aber etwas aufwendig. Ungefähr läuft das dann so ab:
Delphi-Quellcode:
(Such mal ggf. nach "Objekte nach Klassennamen erzeugen" o.ä.)
// nur smbolischer Code, die genauen Funktionen habe ich nicht im Kopf
VorbereitungFuerBaum; VorbereitungFuerHaus; RegisterClass(TBaum, THaus); ... Baum := CreateObject(TBaum); Haus := CreateObject(THaus); Einfacher kannst Du das natürlich auch selbst in die Hand nehmen:
Delphi-Quellcode:
Wenn alle Objekte von einer bestimmten Basisklasse abgeleitet werden, dann kann man geschickter Weise diese Basisklasse als Rückgabetyp der Funktion wählen.
function GetNewObject(aClassName: String): TObject;
begin if aClassName = 'TBaum' then Exit(TBaum.Create(Self)); if aClassName = 'THaus' then Exit(THaus.Create(Self)); if aClassName = 'Irgendwas' then Exit(TXYZ.Create(Self)); Result := nil; end; Vielleicht nützt das als praktischer Ansatz. |
AW: Objekte aus Textdatei erstellen
Zitat:
Das scheint nämlich ein guter Ansatz zu sein :). Jedenfalls hab ich mich eben auch noch daran versucht und nicht so ganz weit gekommen. Zunächst aber etwas zur "Basisklasse": Die Hierarchie sieht im Moment so aus: TImageSprite (vorgegeben durch Engine) | TObjekt | TUnbenutzbar | TBaum1 (hier würden dann auch die ganzen anderen Objekte stehen) Ob das jetzt so sinnvoll ist weiß ich nicht, aber es beizubehalten wär schon ganz nett. Daher würde eine Basisklasse auch wegfallen. Man sollte aber wissen, dass alles ab TObjekt in einer anderen Unit ausgelagert ist. Jetzt aber GetClass: Was mache ich falsch? Wär das überhaupt so möglich?
Delphi-Quellcode:
Bei mir ist CRef immer leer (ohne direkte Registrierung der Klasse). Registrieren kann ich es nicht, weil er mir dann immer ne Fehlermeldung ausgibt, dass das keine "persistent class" ist. Aber laut dem Link
CRef := GetClass('TBaum1');
![]() Fragen über Fragen... Tut mir leid, wenn es jetzt ganz einfach ist, ich bin noch ziemlich unerfahren :lol:. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:36 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