![]() |
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:. |
AW: Objekte aus Textdatei erstellen
So, jetzt mit mehr Zeit etwas genauer.
Du kannst Klassen folgendermaßen registrieren (am Beispiel der TodPanel):
Delphi-Quellcode:
Und dann zur Laufzeit:
// Klassenunit
unit odPanel; interface type TodPanel = class(TPanel) ... end; TodPanelClass = class of TodPanel; procedure Register; // wenn Du das Control für die IDE registrieren willst implementation procedure Register; begin RegisterComponents('odControls', [TodPanel]); end; initialization RegisterClasses([TodPanel]); end.
Delphi-Quellcode:
In dieser Richtung solltest Du mal schauen.
FindClass('TodPanel');
odPanel := TodPanelClass(GetClass('TodPanel')).Create(Self); Ach so, es hängt ggf. auch von Deiner Delphiversion ab. |
AW: Objekte aus Textdatei erstellen
Ich nehme mal an, dass ich für meine Klassen stattdessen "RegisterClasses" nehmen soll.
Aber das Problem ist jetzt dabei, dass TImageSprite keine "persistent class" ist und ich die deshalb nicht registrieren kann. Ich versteh auch im Moment nicht wo jetzt der Sinn dahinter ist. Gäbe es denn ne andere Möglichkeit, als die Klassenvererbung zu ändern? Falls nicht, wovon müsst ich theoretisch TImageSprite ableiten? |
AW: Objekte aus Textdatei erstellen
Theoretisch von TPersistant, aber ich würde die Registrierung einfach bei einem eigenen Factory-Objekt machen wie schon geschrieben. Sprich:
Delphi-Quellcode:
Und in MyFactory das ganze in ein TDictionary<string, TClass> werfen, wobei der Key der Klassenname als String der Klasse ist. Dann kannst du aus diesem Dictionary jederzeit über den Namen der Klasse die Klasse finden und kannst die entsprechend erzeugen.
MyFactory.RegisterClass(TBaum);
... |
AW: Objekte aus Textdatei erstellen
@jaenicke :thumb:
So könnte die Klasse aussehen
Delphi-Quellcode:
das Registrieren dann wie schon erwähnt
unit Entity;
TEntity = class end; TEntityClass = class of TEntity; TEntityFactory = class class procedure RegisterClass( const AName : string; AClass : TEntityClass ); overload; class procedure RegisterClass( AClass : TEntityClass ); overload; class function Construct( const AName : string ) : TEntity; // oder mit Generics class function Construct<T : TEntityClass>( const AName : string ) : T; end;
Delphi-Quellcode:
und erzeugen dann mit
unit Baum;
uses Entity; TBaum = class( TEntity ) end; TBusch = class( TEntity ) end; initialization TEntityFactory.RegisterClass( 'Baum', TBaum ); TEntityFactory.RegisterClass( 'Busch', TBusch ); end.
Delphi-Quellcode:
var
LBaum : TEntity; LBusch : TEntity; LBaum := TEntityFactory.Construct( 'Baum' ); LBusch := TEntityFactory.Construct( 'Busch' ); |
AW: Objekte aus Textdatei erstellen
Hab jetzt das mit dem "Dictionary" probiert. Nur heißt das bei Lazarus TFPGMap und hat bisschen andere Namen, wobei das in meinen Augen ansonsten wenig Unterschiede hat.
Eine Klasse einfügen funktioniert und auslesen geht glaube ich auch*. Nur schmeißt der mir jetzt hier ne Fehlermeldung aus (External SIGSEGV):
Delphi-Quellcode:
Ich persönlich würde ja einfach mal sagen, dass ich Zeile 2 einfach falsch geschrieben hab, aber als ich überprüfen wollte*, ob der überhaupt die Klasse ausliest kam mir der Inhalt von "Value" etwas verdächtig vor:
Value := Dictionary.GetKeyData('Baum');
with TImageSprite(Dictionary.GetKeyData('Baum')).Create(AdSpriteEngine) do begin x := i * 96; y := j * 96; z := 30; end;
Code:
Ist das so schon richtig oder passt da das record nicht rein? Wäre ein record nicht eine Gruppierung aus Variablen, also keine Klasse?
Value = TClass($00573390) = record{}
|
AW: Objekte aus Textdatei erstellen
Okay, hat sich erledigt, ich hatte vergessen den "class of"-Befehl zu benutzen.
Delphi-Quellcode:
TUnbenutzbarClass = class of TUnbenutzbar
...
Delphi-Quellcode:
Dictionary := TFPGMap<String, TUnbenutzbarClass>.Create;
...
Delphi-Quellcode:
Jedenfalls danke für die Hilfe! Wir werden uns dann später auch an den Streams und Factorys versuchen :D
Value := Dictionary.GetKeyData('Baum');
TestObjekt := Value.Create(AdSpriteEngine); |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:52 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