Datei lesen & schreiben für Anfänger

Übersicht BlitzBasic FAQ und Tutorials

Neue Antwort erstellen

Midimaster

Betreff: Datei lesen & schreiben für Anfänger

BeitragFr, Jan 08, 2010 11:36
Antworten mit Zitat
Benutzer-Profile anzeigen
Datei lesen & schreiben für Anfänger

Dies ist ein Tutorial für blutige Anfänger. Es erklärt, wie man Daten aus einer Datei lesen kann, um sie dann im Spiel als Parameter für Figuren und Levels zu verwenden.

Du lernst die wichtigsten Datei-Befehle und ihren Einsatz. Am Ende kannst du deine INI-Dateien in deinen Programmen benutzen.

Wer lieber sofort ein fertiges INI-Konzept ohne Lernen nutzen möchte, sei auf diesen Code von OJay verwiesen: https://www.blitzforum.de/foru...ht=readini

(Ich bitte alle, sich mit eigenen Einträgen rauszuhalten und dafür lieber den Thread "Kritik an Midimaster-Tutorials" zu nutzen. Dieses Tutorial soll den Anfänger möglichst durch keine Diskussionen irritieren.) Kritik hier hin: Kritik-Thread

Allen Anfängern lege ich wärmstens ans Herz dieses Tutorial durchzuarbeiten. Es spart euch viele Fehler in späteren Programmen. Fragen von Hilfesuchenden bitte ins BB-Beginners-Corner (https://www.blitzforum.de/forum/viewforum.php?f=18), aber auf keinen Fall hier hin posten!

Das Tutorial ist didaktisch konzipiert aufgebaut. Die Lektionen bauen aufeinander auf.


Lektion I und II: Das Grundwissen

Lektion I: Öffnen, Schließen, Txt-Datei schreiben Arrow Lektion I

Lektion II: erste Werte lesen Arrow Lektion II

(nach Lektion I und II hast du nun das Wissen, sofort in Lektion VI zu springen: Ein Spielfeld-Editor)


Lektion III bis V: Auf dem Weg zur INI-Datei

Lektion III: Werte suchen Arrow Lektion III

Lektion IV: verschiedene Levels trennen Arrow Lektion IV

Lektion V: Sicherheit muss sein Arrow Lektion IV


Lektion VI: Ein Spielfeld-Editor

Lektion VI: Ein einfacher Spielfeld-Editor Arrow Lektion IV


Lektion VII bis IX: Speichern von Dateien

Lektion VII: Schreiben einer einfachen Liste, wie sie in Kapitel II vorgestellt wird
Arrow Lektion VII

Lektion VIII: Schreiben einer "benannten Liste, wie sie in Kapitel III vorgestellt wird
Arrow Lektion VIII

Lektion IX: Schreiben einer echten INI-Datei, wie sie in Kapitel IV vorgestellt wird
Arrow
  • Zuletzt bearbeitet von Midimaster am Do, Apr 15, 2010 11:40, insgesamt 9-mal bearbeitet

Midimaster

Betreff: Lektion I: Öffnen, Schließen, Txt-Datei schreiben

BeitragFr, Jan 08, 2010 14:02
Antworten mit Zitat
Benutzer-Profile anzeigen
Lektion I: Öffnen, Schließen, Txt-Datei schreiben

In diesem Kapitel wirst du lernen, wie du TXT-Dateien für deine Programme nutzen kannst. Diese TXT-Dateien erstellst du mit einem beliebigen Editor. Du kannst dies sogar gleich in der BlitzIde mit einer 2.Codeseite erstellen. Achte aber darauf, dass bei dieser Datei die Datei-Endung nicht ".bb" wie sonst ist, sondern speichere sie unter ".txt" ab.

Dateien, die Werte enthalten, die du im Spiel brauchst, nennt man INI-Dateien. Dies stammt von dem Wort "Initialisieren", was so viel heißt wie "mit Startwerten versehen". Im Grunde sind die INI-Dateien aber nichts anderes als einfach Text-Dateien mit mehreren Zeilen aus Buchstaben und Zahlen. Dadurch kannst du die Werte im Klartext lesen und leicht verändern.


Erstelle eine erste INI-Datei

Öffne im BB ein zweites Fenster und schreibe dort

BlitzBasic: [AUSKLAPPEN]
Hubert
23
100.90


Speichere den "Code" als "Test.txt". Fertig!

Datei vorhanden?

Jetzt wechseln wir in das erste Fenster und wollen ein Programm schreiben, das auf der Festplatte nachsieht, ob die Datei vorhanden ist. Eine Datei zu öffnen, die eventuell nicht vorhanden ist, führt nämlich zu einem Absturz. Hinweis: Achte darauf, das du den Code dieses Programms in das gleiche Verzeichnis speicherst, in dem sich auch die TXT-Datei befindet!

BlitzBasic: [AUSKLAPPEN]
If FileType("Test.txt") =1 Then
Print "Datei ist vorhanden"
EndIf

Da es keinen speziellen Befehl gibt, der prüft ob eine Datei vorhanden ist, bedienen wir uns eines Tricks: Die Funktion FILETYPE() liefert eigentlich zurück, ob es sich bei dem Suchbegriff um einen Ordner oder eine Datei handelt. Wenn aber die Datei gar nicht vorhanden ist, meldet sie die Zahl 0. (und das auch noch ohne Absturz!)

Du kannst diese Funktion in eine eigene Funktion packen und ihr so einen neuen Namen geben:
BlitzBasic: [AUSKLAPPEN]
Function FileExists(Datei$)
If FileType(Datei) =1 Then
Return True
EndIf
End Function

Diese Funktion brauchen wir ab jetzt in allen weiteren Kapiteln. Nimm sie daher immer mit.

Sesam, öffne Dich

BlitzBasic: [AUSKLAPPEN]
DateiHandle%=ReadFile(Datei$)

Dateien sind vor willkürlichem Zugriff geschützt. So kannst du nicht einfach in irgendeine Datei hineinsehen, denn sie sind alle "verschlossen". Erst durch das "Öffnen" mit READFILE() wird der Zugriff möglich. Außerdem erhalten wir ein "Handle", eine Art Telefonnummer, über die wir die Datei ansprechen sollen. Das bedeutet, die Datei ist im "offenen" Zustand immer noch nicht für jedermann offen, sondern nur für den, der das "Handle" kennt.

Eine geöffnete Datei müssen wir nach dem Lesen unbedingt wieder schließen:
BlitzBasic: [AUSKLAPPEN]
CloseFile DateiHandle%


Jetzt "öffnen" wir die Datei. Hier nun der gesamte Code:
BlitzBasic: [AUSKLAPPEN]
Local Datei$, DateiHandle%
Datei = "Test.txt"
If FileExists(Datei$) Then
DateiHandle=ReadFile(Datei)
CloseFile DateiHandle
EndIf

Gewöhne dir an, nach dem Schreiben der READFILE-Zeile immer gleich die Zeile mit dem CLOSEFILE anzufügen. Dazwischen kommt später weiterer Code. Aber so stellst du sicher, dass du die CLOSEFILE-Zeile nicht vergisst. Eine Datei, bei der vergessen wurde sie wieder zu schließen, wäre der Super-GAU.
  • Zuletzt bearbeitet von Midimaster am Mo, Jan 11, 2010 12:07, insgesamt einmal bearbeitet

Midimaster

Betreff: Lektion II: erste Werte lesen

BeitragFr, Jan 08, 2010 14:41
Antworten mit Zitat
Benutzer-Profile anzeigen
Lektion II: Erste Werte lesen

So, nun wären wir soweit, den ersten Wert zu lesen. Der Befehl READLINE liest immer eine Zeile aus der Datei und reicht sie an die Variable weiter.

BlitzBasic: [AUSKLAPPEN]
Local Datei$, DateiHandle%, Name$
Datei="Test.txt"
If FileExists(Datei$) Then
DateiHandle=ReadFile(Datei)
Name=ReadLine(DateiHandle)
CloseFile DateiHandle
EndIf
Print "Name: "+ Name
BlitzBasic: [AUSKLAPPEN]
Function FileExists(Datei$)
If FileType(Datei) =1 Then
Return True
EndIf
End Function



Willst Du mehrere Werte holen, rufst Du einfach immer wieder READLINE() auf und erhältst immer die nächste Zeile aus der Datei:
BlitzBasic: [AUSKLAPPEN]
Global Name$, Alter%, Geld#
Local Datei$, DateiHandle%
Datei="Test.txt"
If FileExists(Datei$) Then
DateiHandle=ReadFile(Datei)
Name=ReadLine(DateiHandle)
Alter=ReadLine(DateiHandle)
Geld=ReadLine(DateiHandle)
CloseFile DateiHandle
EndIf
Print "Name: "+ Name
Print "Alter: " + Alter
Print "Vermögen: " + Geld + " EUR"


Bei Vermögen% sollte dir auffallen, dass die PRINT-Ausgabe "100.9" ausgibt, während wir in die TXT-Datei eigentlich "100.90" geschrieben hatten. Da Geld# als eine FLOAT-Variable und nicht als STRING definiert ist, enthält sie nicht die exakte Wiedergabe der Zeile, sondern ihren Wert. Und das ist 100.9 das gleiche wie 100.90. Überdenke also immer, ob du den einzulesenden Wert als "identischen" Text benötigst, oder ob du im Programm den "Wert" davon brauchst.


Fazit:
Dieses Modell eignet sich bereits für einfache Programme. Es hat seine Schwächen, aber reicht für viele Zwecke aus.

Eine der Schwächen:
Verwendest du mehr als 30 Zeilen, dann verliert man in der TXT-Datei leicht den Überblick welche Zeile welchen Wert repräsentiert. In Kapitel III gibt es schönere Lösungen.

Hier eine zweite Schwäche, die schon zu einem ernsthaften Fehler mutieren kann:
Eine Mischung von Werten und Texten in einer TXT-Datei kann zu Problemen im Programm führen: Wenn aufgrund eines Programmierfehlers die aktuelle Zeile der geöffneten Datei gerade auf einem Namen steht, du aber bei READLINE() gerade das Alter einlesen möchtest. Die Lösung hierzu findest du auch in Lektion III
  • Zuletzt bearbeitet von Midimaster am Mo, Jan 11, 2010 12:09, insgesamt einmal bearbeitet

Midimaster

Betreff: Lektion III: Werte suchen

BeitragFr, Jan 08, 2010 19:30
Antworten mit Zitat
Benutzer-Profile anzeigen
Lektion III: Werte suchen

Eine reine Auflistung von Werten ohne genaue Bezeichnung, zu welchem Parameter sie gehören, ist sehr unübersichtlich:
Zitat:
0
234
46,5
0
0
0
0
0.6


Viel schöner wäre es, wenn auch in der TXT-Datei eine Bezeichnung zu jedem Wert vorhanden wäre:

Zitat:
Name=Hubert
Alter=23
Geld=100.90


Die Routinen, um die Zeilen eine solche Datei zu lesen ändern sich nicht im Vergleich zum Beispiel der letzten Lektion. Wir ergänzen das ganze nur um einen "Parser", das ist ein Textzerleger.

BlitzBasic: [AUSKLAPPEN]
Function TrenneWert$(Zeile$)
Local Wert$, Da%
Da=Instr(Zeile,"=")
Wert=Mid( Zeile, Da+1, -1)
Return Wert$
End Function

Dies ist eine Grundfunktion zum Abtrennen des Werte-Teils rechts vom "="-Zeichen. Die Funktion erwartet als Übergabeparameter eine Textzeile, sucht dort dann das "="-Zeichen und speichert dessen Position in die Variable DA%. Dann wird alles rechts von DA% in einen String WERT$ zwischengespeichert und zuletzt der WERT$ an das aufrufende Programm zurückgegeben.

Dort sieht das Ganze dann so aus:

MainTeil: BlitzBasic: [AUSKLAPPEN]
Local Datei$, DateiHandle%, Name$, Temp$
Datei="Test.txt"
If FileExists(Datei$) Then
DateiHandle=ReadFile(Datei)
;-----------------neu-------------------
Temp=ReadLine(DateiHandle) ;
Name=TrenneWert( Temp ) ;
;---------------------------------------
CloseFile DateiHandle
EndIf
Print "Name: "+ Name
Funktionen-Teil: BlitzBasic: [AUSKLAPPEN]
Function TrenneWert$(Zeile$)
Local Wert$, Da%
Da=Instr(Zeile,"=")
Wert=Mid( Zeile, Da+1, -1)
Return Wert$
End Function

Function FileExists(Datei$)
If FileType(Datei) =1 Then
Return True
EndIf
End Function

Nur zwei Code-Zeilen sind darin neu im Main-Teil: Die aus READLINE() gewonnene Datei-Zeile wird zunächst in eine Zwischenvariable Temp$ verfrachtet. Die wird als Parameter in die Funktion TrenneWert() übergeben. Die Funktion TrenneWert() seinerseits liefert ihren "WERT" an die endgültige Variable Name$ zurück.

Bestimmten Eintrag suchen

Natürlich lässt sich auch der linke Teil einer Zeile, also alles was vor dem "="-Zeichen steht abtrennen. Dazu wäre eine Funktion LinkerTeil() zu schreiben.

Damit hätten wir die Möglichkeit zu prüfen, ob es sich bei der Zeile überhaupt um die richtige Zeile für den gesuchten Wert handelt:
Code: [AUSKLAPPEN]
;(pseudocode)
If LinkerTeil() = VariablenName Then
   Return RechterTeil()
Endif


Oder noch besser: wir könnten eine Funktion schreiben, welche die gesamte Datei nach der passenden Zeile absucht. Damit müssten wir in der TXT-Datei nicht mehr die vorgeschriebene Reihenfolge der Werte einhalten. Egal, wo "Name=Hubert" steht, diese Funktion würde "Hubert" finden und zurückliefern.

Dies ist theoretisch so zu erreichen:
BlitzBasic: [AUSKLAPPEN]
Local Datei$, DateiHandle%, Name$, Temp$
Datei="Test.txt"
SuchWort= "Name"
DateiHandle=ReadFile(Datei)
Repeat
Temp=ReadLine(DateiHandle)
If RechterTeil(Temp) = SuchWort Then Exit
Forever
CloseFile DateiHandle
Name = TrenneWert(Temp)
Print "Name: " + Name

Diese REPEAT/FOREVER-Schleife liest einfach alle Zeilen der Datei. Wenn sie die Zeile mit dem Suchwort gefunden hat, bricht sie die Schleife ab, schließt die Datei und übergibt den Wert.

Lesen bis ans Ende der Datei

Wenn wir diese Funktion schreiben, sollten wir noch einen Sicherheitsaspekt bei der Suche mitbeachten. Es könnte durchaus sein, dass die gesuchte Zeile nicht existiert.

Bei der Suche nach der nicht existierenden Zeile, kommt unsere Funktion irgendwann an das Ende der Datei. Versucht sie, darüber hinaus noch eine Zeile zu lesen, gibt das einen Absturz.

Der Befehl EOF(DateiHandle) wird uns helfen. Er steht für "End Of File". Mit ihm lässt sich feststellen, ob das Dateiende bereits gekommen ist.

BlitzBasic: [AUSKLAPPEN]
DateiHandle=ReadFile(Datei)
While Eof(DateiHandle) = False
Temp=ReadLine(DateiHandle)
Wend
CloseFile DateiHandle



Die endgültige Fassung der Datei-Lese-Funktion.

Verwende sie so:BlitzBasic: [AUSKLAPPEN]
Name = LiesWert("Name")
BlitzBasic: [AUSKLAPPEN]
Geld = LiesWert("Geld2")


BlitzBasic: [AUSKLAPPEN]
Function LiesWert$(SuchWort$)
Local Datei$, DateiHandle%, Wert$, Temp$, Da%
Datei="Test.txt"
If FileExist(Datei) Then
DateiHandle=ReadFile(Datei)
While Eof(DateiHandle) = False
Temp=ReadLine(DateiHandle)
If LinkerTeil(Temp) = SuchWort Then
Wert=TrenneWert(Temp)
Exit
EndIf
Wend
CloseFile DateiHandle
EndIf
Return Wert$
End Function


Function LinkerTeil$(Zeile$)
Local Wert$, Da%
Da=Instr(Zeile,"=")
If Da>0
Wert=Left( Zeile, Da-1)
EndIf
Return Wert$
End Function

Function TrenneWert$(Zeile$)
Local Wert$, Da%
Da=Instr(Zeile,"=")
Wert=Mid( Zeile, Da+1, -1)
Return Wert$
End Function

Function FileExists(Datei$)
If FileType(Datei) =1 Then
Return True
EndIf
End Function
  • Zuletzt bearbeitet von Midimaster am Mi, Jun 15, 2011 11:48, insgesamt 4-mal bearbeitet

Midimaster

Betreff: Lektion IV: verschiedene Levels trennen

BeitragSo, Jan 10, 2010 11:38
Antworten mit Zitat
Benutzer-Profile anzeigen
Lektion IV: verschiedene Levels trennen

Bisher hast du zwei verschiedene Modelle kennen gelernt, wie man Spiel-Parameter in Dateien organisieren könnte:

Das erste Modell war eine einfach Werte-Liste und eignet sich für bis zu 20 Werten:
Zitat:
Hubert
23
100.90
Wie man solche Dateien liest lernst du in Lektion II.


Das zweite Modell war schon übersichtlicher. Die Werte werden benannt. Das erlaubt dir, die Reihenfolge zu verändern, die Werte werden trotzdem gefunden. Dieses Modell eignet sich für bis zu 100 Werte:
Zitat:
Name=Hubert
Alter=23
Geld=100.90
Wie man solche Dateien liest lernst du in Lektion III.


Die Königsklasse ist aber die vollständige INI-Datei, bei der es auch noch Überschriften gibt. Das ermöglicht dir, den gleichen Parameternamen mehrfach zu verwenden.
Zitat:
[Spieler]
Name=Hubert
Alter=23
[Level 1]
Geld=100.90
...
[Level 2]
Geld=80.70
...
[Level 3]
Geld=45.50


Hierfür sind nur wenige Änderungen in unserem Modell II nötig: Zunächst wird nach einer bestimmten Überschrift Sektion$ gesucht. Ist sie gefunden, dann wird wie gewohnt nach dem Eintrag gesucht und der Wert zurückgegeben.

theoretisch: BlitzBasic: [AUSKLAPPEN]
Sektion$="Level 1"
While Eof(DateiHandle) = False
Temp=ReadLine(DateiHandle)
If Temp = Sektion Then
Wert = SucheNachEintrag()
Exit
EndIf
Wend


Damit wir garantiert nur Überschriften finden, ergänzen wir Sektion$ um die eckigen Klammern. BlitzBasic: [AUSKLAPPEN]
Sektion="[" + Sektion + "]"

Sobald wir die Sektion gefunden haben, lassen wir den Rest eine neue Funktion machen. Dieser Funktion müssen wir aber neben dem Suchwort auch das DateiHandle mitteilen, damit sie an der gleichen Stelle der Datei weitersuchen kann. BlitzBasic: [AUSKLAPPEN]
Wert = SucheNachEintrag(DateiHandle, SuchWort)



Aufruf-Funktion: BlitzBasic: [AUSKLAPPEN]
Function LiesWert$(Sektion$, SuchWort$)
Local Datei$, DateiHandle%, Wert$, Temp$
Datei="Test.txt"
Sektion="[" + Sektion + "]"
If FileExists(Datei) Then
DateiHandle = ReadFile(Datei)
While Eof(DateiHandle) = False
Temp = ReadLine(DateiHandle)
If Temp = Sektion Then
Wert = SucheNachEintrag(DateiHandle,Suchwort)
Exit
EndIf
Wend
CloseFile DateiHandle
Return Wert
EndIf
End Function


Die dazugehörende Zeilensuchfunktion erinnert stark an die alte LiesWert():
BlitzBasic: [AUSKLAPPEN]
Function SucheNachEintrag$(DateiHandle$, SuchWort$)
Local Temp$, Wert$
While Eof(DateiHandle) = False
Temp = ReadLine(DateiHandle)
If LinkerTeil(Temp) = SuchWort Then
Wert = TrenneWert(Temp)
Return Wert
EndIf
Wend
End Function


Ab jetzt kannst du deine Parameter-Dateien statt mit der Endung .TXT besser mit der Endung .INI versehen, denn du hast soeben eine vollständige INI-Implementierung in dein Programm erreicht. Der Aufruf erfolgt an jeder beliebigen Stelle deines Programms mit:


Die endgültige Fassung der Funktion Datei suchen mit Sektionen

Verwende sie so:
BlitzBasic: [AUSKLAPPEN]
Name = LiesWert("Spieler", "Name")
BlitzBasic: [AUSKLAPPEN]
Geld = LiesWert("Level 2", "Geld")
BlitzBasic: [AUSKLAPPEN]
Leben = LiesWert("Level 1", "Leben")


BlitzBasic: [AUSKLAPPEN]
Function LiesWert$(Sektion$, SuchWort$)
Local Datei$, DateiHandle%, Wert$, Temp$
Datei="Test.txt"
Sektion="[" + Sektion + "]"
If FileExists(Datei) Then
DateiHandle = ReadFile(Datei)
While Eof(DateiHandle) = False
Temp = ReadLine(DateiHandle)
If Temp = Sektion Then
Wert = SucheNachEintrag(DateiHandle,Suchwort)
Exit
EndIf
Wend
CloseFile DateiHandle
Return Wert
EndIf
End Function


Function SucheNachEintrag$(DateiHandle$, SuchWort$)
Local Temp$, Wert$
While Eof(DateiHandle) = False
Temp = ReadLine(DateiHandle)
If LinkerTeil(Temp) = SuchWort Then
Wert = TrenneWert(Temp)
Return Wert
EndIf
Wend
End Function


Function LinkerTeil$(Zeile$)
Local Wert$, Da%
Da=Instr(Zeile,"=")
If Da>0
Wert=Left( Zeile, Da-1)
EndIf
Return Wert$
End Function


Function TrenneWert$(Zeile$)
Local Wert$, Da%
Da=Instr(Zeile,"=")
Wert=Mid( Zeile, Da+1, -1)
Return Wert$
End Function


Function FileExists(Datei$)
If FileType(Datei) =1 Then
Return True
EndIf
End Function
  • Zuletzt bearbeitet von Midimaster am Mi, Jun 15, 2011 11:49, insgesamt 3-mal bearbeitet

Midimaster

Betreff: Lektion V: Sicherheit muss sein

BeitragMi, Jan 13, 2010 9:07
Antworten mit Zitat
Benutzer-Profile anzeigen
Lektion V: Sicherheit muss sein

Bisher enthalten unsere Code-Zeilen nur wenig Sicherheitsfunktionen. Planst du, die Funktion an Dritte weiterzugeben, müsste das gesamte Projekt noch so aufgemotzt werden, dass mögliche User-Eingabefehler abgefangen werden.

Denkbare Fehler/Probleme sind:


- Ini-Datei wird nicht gefunden
- Sektion wird nicht gefunden
- Eintrag wird nicht gefunden
- Groß/Kleinschreibung willkürlich
- Kommentarzeilen in der INI-Datei
- frei wählbarer Name der INI-Datei
- Faulenzer

Erläuterungen:

Nicht gefunden
Die ersten drei Punkte dürften ziemlich klar sein. Eine gute INI-Function sollte hier dem User eine Meldung machen, oder vielleicht zu einer "manuellen Suchfunktion" wechseln.

Groß/Kleinschreibung

Entscheide ob die Suche nach dem Eintrag "Name" auch mit dem Suchbegriff "NAME" oder "name" erfolgreich sein soll.

Kommentarzeilen
Prüfe, ob deine INI-Function mit Zeilen zurechtkommt, die gar nicht zur Wertespeicherung dienen. Kommentare und auskommentierte Werte-Zeilen:
Zitat:
[Spieler]
; gibt die Spielerdaten an:
Name=Hubert
Alter=23
[Level 1]
; Geld = 1000 ; war zuviel
Geld=100.90
...


frei wählbarer Name der INI-Datei
Dies wird spätestens dann wichtig, wenn in einem Programm zwei INI-Dateien benötigt werden.

Faulenzer
möglicherweise wäre es sehr bequem, nicht bei jedem INI-Funktionsaufruf immer wieder Sektion angeben zu müssen, weil z.B. gleich 100x auf die selbe Sektion zugegriffen wird

statt: BlitzBasic: [AUSKLAPPEN]
Name = LiesWert("Level 1", "Name")
Geld = LiesWert("Level 1", "Geld")
Leben = LiesWert("Level 1", "Leben")


lieber: BlitzBasic: [AUSKLAPPEN]
Name = LiesWert("Level 1", "Name")
Geld = LiesWert("Geld")
Leben = LiesWert("Leben")


Hier wären also noch Möglichkeiten für Erweiterungen und Verbesserungen. Es "richt" förmlich nach einer Lektion V.

Midimaster

Betreff: Lektion VI: Ein einfacher Spielfeld-Editor

BeitragMi, Jan 13, 2010 10:47
Antworten mit Zitat
Benutzer-Profile anzeigen
Lektion VI: Ein einfacher Spielfeld-Editor

Für viele RPG-Spiele wird immer wieder ein Spielfeld benötigt, auf dem bestimmte Objekte positioniert sein sollen. Solche Objekte werden oft durch Zahlen in DATA-Zeilen beschrieben:

Code: [AUSKLAPPEN]
Data 1,1,1,1,1,1,1
Data 1,0,4,0,0,0,1
Data 1,0,0,0,0,0,1
Data 1,0,0,9,9,0,1
Data 1,0,0,0,0,0,1
Data 1,1,1,1,1,1,1


Umständlich und unübersichtlich

Zum Bearbeiten des Spielfeldes ist es notwendig, das Programm zu beendet, die Werte in den DATA-Zeilen abzuändern und das Spiel wieder zu starten. Das ist mühselig. Zudem ist spätestens, wenn man mit mehr als 10 Objekten arbeitet, das Spielfeld auch sehr unübersichtlich. Man erkennt nun nicht mehr, was wirklich untereinander steht:

Code: [AUSKLAPPEN]
Data 11,11,11,11,11,11,11
Data 11,0,13,0,0,0,11
Data 11,0,0,0,0,0,11
Data 11,0,0,19,19,0,11
Data 11,0,0,0,0,0,11
Data 11,11,11,11,11,11,11



Das Spielfeld als Datei

Beide Probleme lassen sich mit einer externen MAP-Datei lösen. Hierbei öffnest du wie in Lektion I ein neues Code-Fenster direkt in BB. Dort schreibst du...
Code: [AUSKLAPPEN]
11111111
1  5   1
1    6 1
1      1
11111111

...und speicherst diesen "Text" als Datei "Level1.MAP" in das gleiche Verzeichnis, in das du den Spiel-Code speicherst.

Wie du jetzt schon erkennst, wirkt das Spielfeld jetzt wesentlich übersichtlicher. Das am häufigsten vorkommende Grundelement (z.B. "Wiese") wurde im DATA-Beispiel durch eine 0 dargestellt. In der MAP-Datei wird es durch Leerzeichen symbolisiert.


Die Einlese-Funktion

In das eigentliche Spiel kommt diese Funktion ganz an das Ende deines Codes (hinter "END"):
BlitzBasic: [AUSKLAPPEN]
Function LiesMap()
Local Datei$, DateiHandle%, Zeile$, I%
Datei="Level1.map"
DateiHandle=ReadFile(Datei)
For I=1 To 10
Zeile=ReadLine(DateiHandle)
TrenneZeile Zeile, I
Next
CloseFile DateiHandle
End Function

Du hast sie in ähnlicher Form bereits in der Lektion II kennen gelernt. Neu ist hier, dass wir gleich 10 Zeilen einlesen. Jede Zeile wird zum Zerlegen an die Funktion TrenneZeile() weitergereicht.

Das Spiel

Unser Spiel besteht aus einem Spielfeld mit den Ausmaßen 20 Felder in X- und 10 Felder in Y-Richtung.
Als Elemente haben wir 5 kleine 32x32 Pixel-Bilder (Tiles), die wir alle in einem zusammenhängenden 160x32 Pixel TILE.PNG gespeichert haben. Im Spiel sieht dann der dazu passende Code oft so aus:

BlitzBasic: [AUSKLAPPEN]
Graphics 800,600,0,2
Global Rasen%=0, Wand% = 1, Ich%=2, Gegner% = 3, Geld% = 4
Tile = LoadImage("Tile.png")
Dim SpielFeld%(20, 10)
Repeat
If MouseHit(1) Then
LiesMap()
For X=1 To 20
For Y=1 To 10
DrawImage Tile, X*32, Y*32, SpielFeld(X,Y)
Next
Next
EndIf
Flip
Until KeyHit(1)

Es sind noch keine Spiel-Funktionen enthalten. Lediglich das Bild wird immer wieder neu aufgebaut, wenn wir die Maustaste drücken. Vor jedem Neuaufbau wird die "Level1.Map" neu ausgewertet.


Nun fehlt nur noch die Funktion, die aus einer Zeile die entsprechenden Spielfeld(X,Y)-Werte herausliest:
BlitzBasic: [AUSKLAPPEN]
Function TrenneZeile(Zeile$, Y%)
Local X%, Teil$
For X=1 To 20
Teil=Mid(Zeile,X,1)
SpielFeld(X,Y) = Teil
Next
End Function


TrenneZeile() erhält als Parameter immer einen String (Textzeile aus der Datei):
Code: [AUSKLAPPEN]
"1  5   1"

...und die Information um welche Zeile es sich handelt (Y%). Nun arbeitet sich die FOR/NEXT-Schleife durch alle 20 Positionen im String und schneidet dort jeweils ein einzelnes Zeichen heraus
BlitzBasic: [AUSKLAPPEN]
      Teil=Mid(Zeile,X,1) 

Bei den Zeichen 1 bis 9 wird die Zahl in SpielFeld() gespeichert. Ein Leerzeichen hat den Wert 0. Daher wird bei Leerzeichen tatsächlich die 0 gespeichert. Wenn der String zu kurz war, führt dies ebenfalls zu einer 0. Es ist also nicht nötig in jeder Zeile des "Level1.Map" wirklich 20 Leerzeichen zu erzeugen.

Live arbeiten am laufenden Spiel

So, nun kannst du tatsächlich das Spiel starten und laufen lassen. Gleichzeitig kannst Du in BB die "Level1.Map" bearbeiten. Jedesmal, wenn du nun die Datei speicherst und dann in das Fenster des laufenden Spiels klickst, wird das Spielfeld aktualisiert.



Spielfelder mit mehr als 10 Figuren-Elementen

Hier noch ein Beispiel, wie du erreichst, mehr Elemente in der "Level1.map" zu erzeugen, als die Zahlen "1" bis "9" hergeben:

Verwende keine Zahlen, sondern Buchstaben "A" bis "Z". So kommst du auf 26 verschiedene Elemente.
Das "A" steht dabei für die "1", das "B" für die "2", usw...
Code: [AUSKLAPPEN]
AAAAAAAA
A  M   A
A    Z A
A      A
AAAAAAAA


Um dann von Buchstaben wieder auf Zahlen zu kommen ist in der Funktion TrenneZeile() ein Trick nötig:

BlitzBasic: [AUSKLAPPEN]
Function TrenneZeile(Zeile$, Y%)
Local X%, Teil$, Wert%
For X=1 To 20
Teil=Mid(Zeile,X,1)
;--------------------------neu-----
Wert=Asc(Teil)
If (Wert>=65) And (Wert<=90) Then
Wert=Wert-64
Else
Wert=0
EndIf
SpielFeld(X,Y) = Wert
;-----------------------ende neu-----
Next
End Function


Jeder Buchstabe hat im Computer auch einen "Wert". Diesen Wert nennt man ASCII-Wert, der Computer braucht ihn intern. Das machen wir uns zunütze! Mit der Funktion ASC() kann man diesen Wert in eine Variable speichern.
BlitzBasic: [AUSKLAPPEN]
      Wert=Asc(Teil)

Das "A" beispielsweise hat den ASCII 65, das "B" hat 66, usw... Das Alphabet hat also aufsteigende Werte. Wir verwenden es bis "Z" mit dem Wert 90. Durch Abziehen von 64 erhalten wir dann bei "A" genau die 1, bei "B" die 2.
BlitzBasic: [AUSKLAPPEN]
       Wert=Wert-64



BlitzBasic: [AUSKLAPPEN]
      If (Wert>=65) And (Wert<=90) Then
...
Else
Wert=0

Bei alle Buchstaben, die nicht zwischen "A" und "Z" liegen, setzen wir den Wert% auf 0

Midimaster

Betreff: Dateien aus BlitzBasic schreiben und ändern

BeitragDo, Apr 15, 2010 10:51
Antworten mit Zitat
Benutzer-Profile anzeigen
Dateien aus BlitzBasic schreiben und ändern

Hier folgenen nun die Gegenstücke zu den vorherigen Kapiteln. Hier erfährst Du, wie Du diese Dateien direkt mit BlitzBasic erzeugst, mit Daten füllst oder veränderst.

Dazu gibt hier es 3 Absätze:

1. Schreiben einer einfachen Liste, wie sie in Kapitel II vorgestellt wird

2. Schreiben einer "benannten Liste, wie sie in Kapitel III vorgestellt wird

3. Schreiben in eine INI-Datei , wie sie in Kapitel IV vorgestellt wird.


1. Schreiben einer einfachen Liste, wie sie in Kapitel II vorgestellt wird

Dieser Code erzeugt eine Liste...
Zitat:
Hubert
23
100.90


Das Schreiben in eine Datei unterscheidet sich in nur ganz wenigen Befehlen vom Lesen der Datei:
BlitzBasic: [AUSKLAPPEN]
DateiHandle=WriteFile(Datei)
WriteLine DateiHandle, Wert


Die Datei wird nun nicht mit ReadFile(), sondern mit WriteFile() geöffnet. Eine so geöffnete Datei kann nicht gelesen, sondern nur beschrieben werden. Bereits existierende Daten (oder Bytes) werden dabei ohne Rücksicht überschrieben.

Der eigentliche Schreib-Befehl ist WriteLine(), der an der aktuelle Stelle der Datei eine Zeile einfügt. Der Text der Zeile ist der Inhalt der Variablen (hier Wert%) als String. Damit lässt sich dann auch eine Integerzahl später in einem Texteditor wie Text lesen.


BlitzBasic: [AUSKLAPPEN]
Global Name$, Alter%, Geld#
Local Datei$, DateiHandle%

Name =" Hubert"
Alter = 23
Geld = 100.9


Datei="Test.txt"

DateiHandle=WriteFile(Datei)
WriteLine DateiHandle, Name
WriteLine DateiHandle, Alter
WriteLine DateiHandle, Geld
CloseFile DateiHandle
Print "datei geschrieben"
Print "Ende"


Unabhängig, ob es sich bei den Variablen um Strings, Integer oder Float-Zahlen handelt, speichert dieser Code alles immer korrekt in eine Datei Text.txt ab. Dort werden die Daten zeilenweise abgelegt. Jede Zeile entspricht einem Datensatz. Du musst dir hierbei keine Gedanken um Länge der Strings oder Genauigkeit der Zahlen machen. Und du kannst die Datei jederzeit mit einem Text-Editor öffnen und verändern. Aber Achtung: Die Funktion überschreibt ohne Nachfrage eine bereits existierende Datei Text.txt. Willst Du hier eine Warnmeldung, änderst Du:
BlitzBasic: [AUSKLAPPEN]
Global Name$, Alter%, Geld#
Local Datei$, DateiHandle%

Name =" Hubert"
Alter = 23
Geld = 100.9

Schreibe "Test.txt"
Print "Ende"
End

Function Schreibe( Datei$)
If FileExists(Datei) Then
If Proceed("Datei überschreiben") <> 1 Then
Return
EndIf
EndIf
DateiHandle=WriteFile(Datei)
WriteLine DateiHandle, Name
WriteLine DateiHandle, Alter
WriteLine DateiHandle, Geld
CloseFile DateiHandle
Print "datei geschrieben"
End Function


Function Proceed%(Text$)
Print "Datei existiert schon!
Print" Überschreiben? J/N"
Repeat
A=GetKey()
Until A>0
If (A=106) Or (A=64) Then
Return 1
EndIf
End Function


Function FileExists(Datei$)
If FileType(Datei) =1 Then
Return True
EndIf
End Function


Hier ist nun das Schreiben in eine Funktion gewandert. Zunächst wird geprüft, ob die Datei existiert. Dazu verwendest Du die Function FileExists(), die im Kapitel 1 erklärt wurde.

Wenn die Datei bereits existiert wird beim User nachgefragt, was geschehen soll. Diese Funktion nennen wir Proceed(). Unter B+ oder BMAX exisitiert sie bereits und es genügt der bloße Aufruf. Unter BB oder B3D simulieren wir sie mit einer eigenen Funktion, die auf die Eingabe von "J" oder "N" wartet.

Wenn der User nicht mit "J" geantwortet hat wird die Datei-Schreibe-Funktion abgebrochen, es kommt nicht zum Saven.

Midimaster

Betreff: Schreiben einer "benannten Liste"

BeitragDo, Apr 15, 2010 11:34
Antworten mit Zitat
Benutzer-Profile anzeigen
2. Schreiben einer "benannten Liste", wie sie in Kapitel III vorgestellt wird

Dieser Code erzeugt eine Liste...
Zitat:
Name=Hubert
Alter=23
Geld=100.90

Diese Variante Daten zu verwalten, war beim Lesen ein Riesentheater, weil eine Suchfunktion implementiert werden musste. Bei Datei-Schreiben sind nur wenige Änderungen nötig, damit aus der vorherigen Variante eine Datei mit "benannten Argumenten" wird:
BlitzBasic: [AUSKLAPPEN]
...
DateiHandle=WriteFile(Datei)
WriteLine DateiHandle, "Name" + "=" + Name
WriteLine DateiHandle, "Alter" + "=" + Alter
WriteLine DateiHandle, "Geld" + "=" + Geld
CloseFile DateiHandle
...


Allerdings bist du immer noch gezwungen jedesmal alle Variablen abzuspeichern, auch wenn du nur einen Wert speichern musst.

Dies wäre bequemer mit einer "Suchen & Ersetzen"-Funktion:

BlitzBasic: [AUSKLAPPEN]
Global Datei$="Test.txt"
Dim Zeile$(1000)
.....
SchreibeWert "Name", "Tarzan"
....

Mehrere Stufen wären in einer solchen Funktion notwendig. Zunächst wird die Datei komplett in ein Array eingelesen. Jede Zeile aus der Datei wird zu einem eigenen Array-Eintrag. Am Ende wird die Datei zunächst wieder geschlossen:
BlitzBasic: [AUSKLAPPEN]
    If FileExists(Datei) Then
DateiHandle=ReadFile(Datei)
While Eof(DateiHandle)=0
i=i+1
Zeile(i)=ReadLine(DateiHandle)
Wend
AnzahlZeilen=i
CloseFile DateiHandle
...


Danach wir die Datei diesmal zu Schreiben geöffnet und alle Array-Zeilen durchgescannt und gleich wieder in die Datei zurückgeschrieben:
BlitzBasic: [AUSKLAPPEN]
        ...
DateiHandle=WriteFile(Datei)
For i=1 To AnzahlZeilen
If ...; Eintrag wird entdeckt
EndIf
WriteLine DateiHandle, Zeile(i)
Next
...

wird dabei der gesuchte Eintrag entdeckt, dann wird die Zeile neu modelliert. Die Funktion LinkerTeil() wurde in Kapitel III erklärt:
BlitzBasic: [AUSKLAPPEN]
        ...
DateiHandle=WriteFile(Datei)
For i=1 To AnzahlZeilen
If LinkerTeil (Zeile(I))=SuchWort Then
Zeile(i) = SuchWort + "=" + Inhalt
Erfolg=1
EndIf
WriteLine DateiHandle, Zeile(i)
Next
...


Da es durchaus sein kann, dass der Eintrag völlig neu ist und daher auch nicht gefunden werden kann, wird bei erfolgloser Suche die neue Zeile am Ende der Datei eingefügt:
BlitzBasic: [AUSKLAPPEN]
...
If Erfolg=0 Then
WriteLine DateiHandle, SuchWort + "=" + Inhalt
EndIf
Print "datei geändert"
CloseFile DateiHandle




Hier die gesamte Funktion:
BlitzBasic: [AUSKLAPPEN]
Global Datei$="Test.txt"
Dim Zeile$(1000)
.....
SchreibeWert "Name", "Tarzan"
Function SchreibeWert(SuchWort$, Inhalt$)
Local AnzahlZeilen%, Erfolg%
If FileExists(Datei) Then
DateiHandle=ReadFile(Datei)
While Eof(DateiHandle)=0
i=i+1
Zeile(i)=ReadLine(DateiHandle)
Wend
AnzahlZeilen=i
CloseFile DateiHandle

DateiHandle=WriteFile(Datei)
For i=1 To AnzahlZeilen
If LinkerTeil (Zeile(I))=SuchWort Then
Zeile(i) = SuchWort + "=" + Inhalt
Erfolg=1
EndIf
WriteLine DateiHandle, Zeile(i)
Next
If Erfolg=0 Then
WriteLine DateiHandle, SuchWort + "=" + Inhalt
EndIf
Print "datei geändert"
CloseFile DateiHandle
Else
DateiHandle=WriteFile(Datei)
WriteLine DateiHandle, SuchWort + "=" + Inhalt
CloseFile DateiHandle
Print "datei erstelllt"
EndIf

End Function

; benötigte Funktionen aus früheren Kapiteln:
Function LinkerTeil$(Zeile$)
Local Wert$, Da%
Da=Instr(Zeile,"=")
If Da=0 Then Return
Wert=Left( Zeile, Da-1)
Return Wert$
End Function

Function FileExists(Datei$)
If FileType(Datei) =1 Then
Return True
EndIf
End Function




Fortsetzung folgt....

Midimaster

Betreff: Lektion IX: Schreiben in eine echte INI-Datei

BeitragDi, Apr 20, 2010 9:04
Antworten mit Zitat
Benutzer-Profile anzeigen
Lektion IX: Schreiben in eine echte INI-Datei

Dieser Code erzeugt eine Liste:

Zitat:
[Spieler]
Name=Hubert
Alter=23
[Level 1]
Geld=100.90
...
[Level 2]
Geld=80.70
...
[Level 3]
Geld=45.50

...


Im Gegensatz zum vorherigen Modell, geht es bei der echten INI-Datei darum, die Daten in einer bestimmten Sektion einzutragen. Zusätzlich zu den vorherigen Funktionen muss also immer geprüft werden, ob die Sektion schon vorhanden ist. Wenn nein, wird am Ende der Datei eine neue Sektion und deren Eintrag erstellt

Fehlt andererseits nur der Eintrag in einer bereits bestehenden Sektion, dann darf der neue Eintrag nicht einfach am Ende der Datei angehängt werden, sondern er muss an das Ende der Sektion. Danach folgen weitere Zeilen anschließender Sektionen.


Zunächst lesen wir wieder alle Zeilen der Datei in ein Array ein (wie Lektion XIII). Dann suchen wir nach der Sektion:
BlitzBasic: [AUSKLAPPEN]
...
Sektion="[" + Sektion + "]"
For i=1 To AnzahlZeilen
If Zeile(i) = Sektion Then
Erfolg=1

; hier beginnt später die Suche nach dem Eintrag

EndIf
Next
If Erfolg=0 Then
; dann zwei Zeilen an die Datei anhängen
Zeile(AnzahlZeilen+1)=Sektion
Zeile(AnzahlZeilen+2)=SuchWort + "=" + Inhalt
AnzahlZeilen = AnzahlZeilen+2
EndIf
...

Haben wir die Sektion nicht gefunden, hängen wir zwei Zeilen an das Array an. Später werden die Zeilen dann in die Datei zurückgeschrieben.


Wurde die Sektion entdeckt, beginnt die Suche, ob der Eintrag bereits vorhanden war:
BlitzBasic: [AUSKLAPPEN]
;Fortsetzung:
...
; hier beginnt die Suche nach dem Eintrag
For j=i+1 To AnzahlZeilen
If Left( Zeile(j),1) = "[" Then Exit

If LinkerTeil( Zeile(j) ) = SuchWort Then
Zeile(j) = SuchWort + "=" + Inhalt
Erfolg = 2
EndIf

; hier beginnt die Reaktion auf den nicht gefundenen Eintrag
Next
...

Interessant ist der Abbruch, sobald das Klammerzeichen gefunden wird. Das bedeutet nämlich, dass die nächste Sektion begonnen hat. Der Eintrag wurde also nicht entdeckt. Weitersuchen zwecklos. Haben wir den Eintrag aber gefunden, dann wird der Wert dieser Zeile gleich geändert. Später werden dann alle Zeilen zurückgeschrieben.


Nun die Reaktion auf den Fall, dass zwar die Sektion gefunden wurde, aber der Eintrag nicht:
BlitzBasic: [AUSKLAPPEN]
;Fortsetzung:
...
; hier beginnt die Reaktion auf den nicht gefundenen Eintrag
If Erfolg <>2 Then
For k=AnzahlZeilen To j Step -1
Zeile(k+1) = Zeile(k)
Next
AnzahlZeilen = AnzahlZeilen+1

Zeile(j) = SuchWort + "=" + Inhalt
EndIf
...

Hier werden nun alle Zeilen der nachfolgenden Sektionen um eine Zeile nach hinten versetzt. Dies geht nur, indem wir uns von hinten durch das Array nach vorne wühlen und jeden Eintrag auf die nachfolgende Arrayyposition kopieren. In der so freigewordenen Zeile j fügen wir den Eintrag ein.

Das wars!

Abschließend wird für alle 3 Fälle das gesamte Arrayfeld zurückgespeichert.



So sieht die gesamte Funktion aus:

BlitzBasic: [AUSKLAPPEN]
Global Datei$="Test.txt"
Dim Zeile$(1000)

SchreibeWert "Spieler", "Name", "Tarzan"
SchreibeWert "Spieler", "Alter", 12
SchreibeWert "Level 1", "Alter", 14

Function SchreibeWert(Sektion$, SuchWort$, Inhalt$)
Local AnzahlZeilen%, Erfolg%, i%, j%, k%, DateiHandle%
Sektion="[" + Sektion + "]"
If FileExists(Datei) Then
DateiHandle=ReadFile(Datei)
; komplette Datei wird in ein Array eingelesen
While Eof(DateiHandle)=0
i=i+1
Zeile(i)=ReadLine(DateiHandle)
Wend
AnzahlZeilen=i
CloseFile DateiHandle

; Sektion wird gesucht
For i=1 To AnzahlZeilen
If Zeile(i) = Sektion Then
Erfolg=1

; hier beginnt die Such nach dem Eintrag
For j=i+1 To AnzahlZeilen
If Left( Zeile(j),1) = "[" Then Exit

If LinkerTeil( Zeile(j) ) = SuchWort Then
Zeile(j) = SuchWort + "=" + Inhalt
Erfolg = 2
EndIf
Next

; hier beginnt die Reaktion auf den nicht gefundenen Eintrag
If Erfolg <>2 Then
For k=AnzahlZeilen To j Step -1
Zeile(k+1) = Zeile(k)
Next
AnzahlZeilen = AnzahlZeilen+1
Zeile(j) = SuchWort + "=" + Inhalt
EndIf
Exit
EndIf
Next
EndIf

If Erfolg=0 Then
; dann zwei Zeilen an die Datei anhängen
Zeile(AnzahlZeilen+1)=Sektion
Zeile(AnzahlZeilen+2)=SuchWort + "=" + Inhalt
AnzahlZeilen = AnzahlZeilen+2
EndIf

; das generelle Abspeichern der Zeilen
DateiHandle=WriteFile(Datei)
For i=1 To AnzahlZeilen
WriteLine DateiHandle, Zeile(i)
Next
Print "datei geändert"
CloseFile DateiHandle
End Function

; benötigte Funktionen aus früheren Kapiteln:
Function LinkerTeil$(Zeile$)
Local Wert$, Da%
Da=Instr(Zeile,"=")
If Da=0 Then Return
Wert=Left( Zeile, Da-1)
Return Wert$
End Function

Function FileExists(Datei$)
If FileType(Datei) =1 Then
Return True
EndIf
End Function

Neue Antwort erstellen


Übersicht BlitzBasic FAQ und Tutorials

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group