Verbesserungen für ein reines Textadventure

Übersicht BlitzBasic Beginners-Corner

Neue Antwort erstellen

 

MasterCoderGanon

Betreff: Verbesserungen für ein reines Textadventure

BeitragMo, Jul 21, 2014 20:17
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo liebe Programmierer Gemeinde.

ich bin schon sehr lange Fan von PenAndPaper-Spielen, wem das etwas sagt, und weil ich als Leidenschaftlicher Spielleiter meine Kreationen sehr gerne ins digitale Umwandeln würde und außerdem keine großen Erfahrungen mit dem Programmieren von Spielen habe bin ich zu Blitz Basic gekommen.

Nach kurzer Einarbeitungszeit sah ich mich in der Lage loszulegen und ich bin soweit schon ganz zufrieden, dafür dass ich keine Erfahrung habe. Ich habe zwar bis jetzt noch nicht viel vom Gesamtwerk geschafft, jedoch könnte ich so schon bis zum ende weiter machen. Damit komme ich auch zum Grund dieses Foren-Beitrages. Denn bevor ich jetzt so weiter mache würde ich gerne einige Punkte klären und Anregungen einbringen um die Spielerfahrung zu optimieren.

1.Grafiken werde ich noch Einfügen, da ich die jedoch selber machen will, werde ich dies zum Schluss machen.(Muss ich da irgendwas wichtiges beachten.

2. Wäre es Sinvoll die einzelnen Passagen mit Funktionen ineinander zu verschachteln?(Funktion 1 führt zu Funktion 2 oder 3)

3. Ist die Schleife mit dem Counter für mögliche falscheingaben sinnvoll?(nicht im Sinne von Zweckmäßig sondern eher in Hinsicht auf PLatz, Übersichtlichkeit und Arbeitsspeicher)

Ich wäre sehr dankbar für eure Hilfe Very Happy

Code: [AUSKLAPPEN]
Graphics 800,600,0,1
SeedRnd MilliSecs ()
;AlphaFenster///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Print "Liebe Alpha-Testerinnen und Tester."
Print "Bei dieser Version handelt es sich um eine Alpha. Das heißt, dass das Spiel noch große Lücken hat."
Print "Da gerade, dass programmieren von Spielen mit vielen Möglichkeiten sehr komplex ist bitte ich Lücken"
Print "in der Handlung zu Entschuldigen und zu melden."
Print "Nun aber viel Spaß."
WaitKey
Cls
;AlphaFenster///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Locate 0, 0
Print "Hey Kumpel. Du hast wohl gehört, dass wir einen neuen Zombie-Schlächter suchen, was?"
Print "Sag mal... Wie heißt du?
Name$ = Input()
Print "Aha " + Name + " ist ein netter Name. Der Name eines Zombie-Töters."
Print "Aber ich muss dich noch etwas fragen bevor wir oder besser gesagt du loslegen kannst."

;Der Spieler wird gefragt ob  er die Vorgeschichte kennt.///////////////////////////////////////////////////////////////////////////////////////
Print "Hast du schon jemals zuvor Tears gespielt " + Name + "?"                     
Default_Counter = 0
Repeat
Vorgeschichtenabfrage$ = Input("Ja oder Nein: ")
Print ""                     
   If Vorgeschichtenabfrage = "Ja" Or Vorgeschichtenabfrage = "JA" Or Vorgeschichtenabfrage = "ja"                              
   Default_Counter = Default_Counter +1
   Print "Dann lass uns Anfangen"                                 
   ElseIf Vorgeschichtenabfrage = "Nein" Or Vorgeschichtenabfrage = "nein" Or Vorgeschichtenabfrage = "NEIN"                           
   Default_Counter = Default_Counter +1
   Print "Dann hör mal gut zu."                                 
   Print "Vorgeschichte:"                                       
   Else                                                   
   Print "Ungültige Eingabe."                                       
   EndIf
Until Default_Counter = 1
WaitKey
Cls
;Der Spieler wird gefragt ob  er die Vorgeschichte kennt.///////////////////////////////////////////////////////////////////////////////////////

;Die Charakterbestimmung erfolgt.///////////////////////////////////////////////////////////////////////////////////////////////////////////////
Locate 0, 0
Charakterfrage_1_Counter = 0
Print "Aber " + Name + " bevor du entgültig loslegen kannst brauchst du noch eine neue Persönlichkeit.
Print "Möchtest du einen vordefinierten Charakter?
Default_Counter = 0
Repeat
Charakterfrage_1$ = Input("Ja oder Nein: ")
   If Charakterfrage_1 = "Ja" Or Charakterfrage_1 = "JA" Or Charakterfrage_1 = "ja"
   Default_Counter = Default_Counter + 1
   Print "Du hast die Wahl zwischen den folgenden Charakteren:
   Print "Da wäre Lisa, die hübsche ehemalige Psychologie- und Mathematik Professorin.
   Print "Oder Armin, der einschüchternde Ex-Navi Seal und Überlebenskünstler.
   Charakterfrage_1_Counter = Charakterfrage_1_Counter + 1
   ElseIf Charakterfrage_1 = "Nein"
   Default_Counter = Default_Counter + 1
   Charakterfrage_1_Counter = Charakterfrage_1_Counter + 2
   Else
   Print "Ungültige Eingabe."
   EndIf
   Until Default_Counter = 1
   
;Funktion vordefinierter Charakter**************************************************************************************************************
Repeat
   If Charakterfrage_1_Counter = 1
   Default_Counter = 0
   Charakterfrage_2$ = Input("Drücke L für Lisa oder A für Armin: ")
   EndIf
   
   If Charakterfrage_2 = "L" Or Charakterfrage_2 = "l"
   Default_Counter = Default_Counter + 1
   Print "Ok. Lisa wird nun zu " + Name + "."
   Print "Jetzt kann alles losgehen."
   ElseIf Charakterfrage_2 = "A" Or Charakterfrage_2 = "a"
   Default_Counter = Default_Counter + 1
   Print "Ok. Armin wird nun zu " + Name + "."
   Print "Jetzt kann alles losgehen."
   Else
   Print "Ungültige Eingabe."
   EndIf
Until    Default_Counter = 1
;Funktion vordefinierter Charakter**************************************************************************************************************

;Funktion eigener Charakter*********************************************************************************************************************



Fehlt noch















;Funktion eigener Charakter*********************************************************************************************************************

WaitKey

;Die Charakterbestimmung erfolgt.///////////////////////////////////////////////////////////////////////////////////////////////////////////////

;Die Szene im Keller von Gebäude A./////////////////////////////////////////////////////////////////////////////////////////////////////////////
Cls
Locate 0, 0
Print "Du wachst langsam und mit schmerzenden Knochen auf.
Print "Du befindest dich nicht in deinem Bett im Hotel von Neu-Anfang sondern in einem kalten Keller."
Print "Nur durch zugezimmerte Fenster fällt etwas Licht."
Print "Neben dir befinden sich noch drei weitere Personen in diesem Keller, die aber noch schlafen."

Default_Counter = 0
Repeat
Handlungsfrage_1$ = Input("Möchtest du dich UMSEHEN, WARTEN oder die anderen WECKEN?: ")
Knall_Counter_Frage = 0
   Print ""
   If Handlungsfrage_1 = "Umsehen"
   Default_Counter = Default_Counter + 1
   Print "Als du dich umsiehst siehst du, dass es außer euch Vieren nur eine Tür in diesem Raum gibt."
   Print "Nachdem du dich umgesehen hast ertönt in der Ferne ein lauter Knall, der die Anderen weckt."
   Print "Möchtest du Versuchen den Knall zu identifizieren?"
   Knall_Counter_Frage = Knall_Frage + 1
   ElseIf Handlungsfrage_1 = "Warten"
   Default_Counter = Default_Counter + 1
   Print "Nachdem du eine Zeit lang gewartet hast hörst du einen sehr lauten Knall in weiter Ferne."
   Print "Möchtest du Versuchen ihn zu identifizieren?
   Knall_Counter_Frage = Knall_Counter_Frage + 1
   ElseIf Handlungsfrage_1 = "Wecken"
   Default_Counter = Default_Counter + 1
   Print "Du weckst die Anderen und Ihr stellt euch gegenseitig vor."
   Print "Die drei Personen heißen Dieter, Anna-Maria und Steffen."
   Print "Während ihr euch noch gegenseitig vorstellt hört ihr einen lauten Knall in der Ferne."
   Print "Dieter kann es eindeutig als Explosion eines Treibstofftanks identifizieren"
   Print "und Anna-Maria erinnert sich, dass das Osttor von Neu-Anfang heute einen neuen Treibstofftank"
   Print "bekommen sollte."
   Print ""
   Print "Während ihr noch darüber rätselt, was am Osttor passiert ist, hört ihr vor der Tür zwei Männer."
   Else
   Print "Ungültige Eingabe."
   EndIf
Until Default_Counter = 1
   Knallfrage = 0
   If Knall_Counter_Frage = 1
   Knall_Frage = Input("Ja oder Nein?: ")
   Else
   Knall_Frage = 524
   EndIf

   If Knallfrage = "Ja" Or Knallfrage = "JA" Or Knallfrage = "ja"
   ElseIf Knallfrage = "Nein" Or Knallfrage = "nein" Or Knallfrage = "NEIN"
   Print "Du entscheidest dich gegen die Identifizierung."
   EndIf
WaitKey
End

BladeRunner

Moderator

BeitragMo, Jul 21, 2014 20:40
Antworten mit Zitat
Benutzer-Profile anzeigen
Mehrerlei:
Vermeide Print und Input, nimm stattdessen Text. Für Eingaben findest Du im Portal diverse Routinen zum Ersatz von Input in den Codearchiven.
Print und Input nutzen immer den Frontbuffer, das wird spätestens wenn Du noch teilanimierte Grafiken zeigen willst zum Problem.

Deine Abfragen sind sehr rudimentär. Statt auf "Ja" "ja" "JA" zu prüfen kannst Du die Eingabe zB. mittles lower umwandeln und musst nur noch einen Fall testen. Allerdings funktioniert das nur bei Worten ohne Umlaute.

Grundsätzlich kannst Du schon so weitermachen, aber es wird schnell ein Riesenwust von Verkettungen. Daher wäre es sinnig sich gedanken zu machen immer wieder kehrende Teile in Funktionen auszulagern.
Bei einem klassischen Adventure wären das: Eingabe, Parsen und Auswertung. Eingabe und Parsen sind immer gleich, lediglich die Auswertung muss angepasst an Ort und Inventar des Spielers erfolgen. So lässt sich massenhaft redundanter Code vermeiden.

Ein simpler Parser zerlegt die Eingabe in einzelne Worte (suchen mittels instr nach Leerzeichen und dann trennen an den gefundenen Stellen mittels mid), und ordnet die gefundenen Worte folgenden Kategorien zu: Verb, Objekt, Füllwort. Wenn ein Satz mindestens ein Verb enthält ist es eine potentiell gültige Eingabe (zB "Nord" als kurze Richtungsangabe, Nord wird also den Verben zugeordnet obwohl es eigentlich keines ist.)
komplexere Befehlseingaben lassen sich eigentlich immer auf die Maximalform Verb, Objekt1, Objekt2 zurückführen:
Lege das Schwert in die Truhe wird zu:
Lege - Verb
das, in, die sind Füllwörter, die ignoriert werden.
Schwert und Truhe sind dann die beiden Objekte.
Nun kann für jedes Verb und jedes Objekt eine Konstante vergeben werden.
Nehmen wir an, lege hätte den Wert 3 bei Verben, Schwert den Wert 1 bei Objekten und Truhe die 3 bei Objekten, dann würde deine Raumbezogene Abfrage so aussehen:

If Verb = 3 and obj1 = 1 and obj2 = 3 then print"okay, das Schwert ist nun in der Truhe"

Dieses simpelbeispiel prüft natürlich noch nicht ob Schwert im Besitz des Spielers ist, und auch nicht ob Truhe sich im aktuellen Raum befindet. Ausserdem müsste dann das Schwert noch aus dem Inventory entfernt werden und in der Truhe plaziert. Du siehst, das kann recht schnell komplex werden. Allerdings lässt sich mit etwas Vorplanung alles so umsetzen.

Viel Erfolg!
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92
 

CO2

ehemals "SirMO"

BeitragMo, Jul 21, 2014 20:45
Antworten mit Zitat
Benutzer-Profile anzeigen
Willkommen im Forum!

Also, zu deinen Punkten:

1.) Beim laden des Bildes wäre es wichtig, den relativen, nicht den absoluten Pfad zu nehmen, da sonst der Spielordner auf jedem Rechner im gleichen Verzeichnis liegen muss, um ein Absturz zu verhindern (Statt "C:\Test\TestBild.bmp" lieber "TestBild.bmp", wenn wir davon ausgehen, dass dein Spiel-Ordner "\Test\" ist.)
Des Weiteren ist es evtl. wichtig, sich mit Befehlen wie MaskImage, SetBuffer, Flip, etc. auseinander zu setzen.

2.) Auf jeden Fall! Vielleicht wäre es auch sinnvoll, den gesamten Text in eine Datei auszulagern, welche du dann mit deinem Programm liest (So bist du flexibler, wenn du den Text in eine andere Sprache übersetzen willst, etc.)

3.) Ein Tipp: Mit den Befehlen Upper() und Lower() kannst du die Eingabe im Gesamten groß, bzw. klein schreiben. (Aus "Ja" wird mit Lower() "ja", etc.) Dies vereinfacht die Abfrage des Eingegebenen.

Schau dir einmal Types an. Damit kannst du die Daten, die zum Spieler gehören einfacher speichern.
mfG, CO²

Sprachen: BlitzMax, C, C++, C#, Java
Hardware: Windows 7 Ultimate 64-Bit, AMX FX-6350 (6x3,9 GHz), 32 GB RAM, Nvidia GeForce GTX 750 Ti

BladeRunner

Moderator

BeitragMo, Jul 21, 2014 20:52
Antworten mit Zitat
Benutzer-Profile anzeigen
Einen hab ich noch: Kennst Du die alten SCUMM-Klassiker wie zb Maniac Mansion oder Tales of Monkey Island?
Diese Spiele haben einen sehr reduzierten Befehlssatz, der aber äüßerst intelligent ist: Mit einem knappen Dutzend Befehlsworte kann man alles erreichen. Egal wieviele Verben also dein Parser kennt, er wird sich eigentlich immer auf diese Standartaktionen reduzieren lassen. "Iss" und "Trink" sind identisch zu "benutze", erhalten also den selben Wert.
Dann kann der Spieler zwar eingeben trink den Schlüssel unter der Tür, und das Spiel wird dennoch "Benutze den Schlüssel mit der Tür" verstehen, aber mal im Ernst: die wenigsten Spieler werden das bemerken, weil die Eingabe ihnen zu abstrus erscheint um sie zu testen.
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

Tennisball

BeitragMo, Jul 21, 2014 20:55
Antworten mit Zitat
Benutzer-Profile anzeigen
Hi,

Also erstmal ein paar Tipps, dann versuche ich deine Fragen zu beantworten.

- Einrückung. Die ist bei dir nicht ganz konsequent. Hier mal ein Beispiel, wie es sein sollte:
BlitzBasic: [AUSKLAPPEN]
If a = 1 Then
tueEtwas()

If b = 2 Then
tueNochEtwas()
End If
Else
tueEtwasGanzAnderes()
End If

- Nutze Text anstatt Print

- Zeilen wie:
BlitzBasic: [AUSKLAPPEN]
If Vorgeschichtenabfrage = "Ja" Or Vorgeschichtenabfrage = "JA" Or Vorgeschichtenabfrage = "ja"
kannst du zusammenfassen:
BlitzBasic: [AUSKLAPPEN]
If Lower(Vorgeschichtenabfrage) = "ja" Then
Dies konvertiert erst die Eingabe in Kleinbuchstaben und vergleicht dann das mit "ja".
Wo wir gerade bei der Zeile sind ... Then solltest du immer dazu schreiben, dann ist es übersichtlicher.

- Benutze Funktionen! -> Function

- Die ganzen Strings solltest du in Dateien auslagern und zu Beginn z.B. in ein Array einlesen. Siehe ReadFile, ReadLine.

- Gib dem Spieler die Möglichkeit, das Spiel zu beenden. Wink

- Du könntest deine Räume als Objekte eines selbst definierten Types speichern und da auch die Texte etc. reinpacken. Dieser Punkt könnte unter Umständen zu schwierig für einen Anfänger werden und ist nicht zwingend notwendig. Aber schau dir auf jeden Fall Types an. Die sind wirklich essenziell.

... Und da die anderen schneller waren beantworte ich jetzt doch nur noch Frage 3.
Das würde ich so lösen. Dort brauchst du keine extra Abbruchbedingung + Variable, sondern springst gegebenenfalls einfach mit Exit raus.
BlitzBasic: [AUSKLAPPEN]
Repeat
Local eingabe$ = Input()
Select Lower(eingabe)
Case "l"
Print "Lisa"
Exit
Case "a"
Print "Armin"
Exit
Default
Print "Ungueltige Eingabe"
End Select
Forever
Oh, und vergiss hier nicht, auch eine Option für's Abbrechen zur Verfügung zu stellen!

Du wirst noch viel lernen müssen, aber gib nicht auf, dann wirst du das alles irgendwann meistern. Wink

Gruß,
Tennisball
 

MasterCoderGanon

BeitragMo, Jul 21, 2014 22:28
Antworten mit Zitat
Benutzer-Profile anzeigen
Shocked Leute, Leute Shocked

Vielen Dank für eure schnellen Tipps und Befehle die ihr mir gegeben habt. Auf die Art und Weise konnte ich den Code um einiges verkürzen und den Defaultcounter endlich rausnehmen, sowie die vielen Antwortmöglichkeiten wegnehmen. Rolling Eyes Rolling Eyes Rolling Eyes
Ich werde den gesamten Text jetzt erstmal in ne gesonderte Datei schreiben. Das mit den entsprechenden Befehlen scheint mir recht einfach und umsetzbar.

Das mit der Monkey Island Idee finde ich Interessant und ich werde mal überlegen ob bzw. wie ich das umsetze.

Ich hätte nur noch einmal zwei Fragen bezüglich des Text statt Input Befehls.
1. Warum läuft das denn über den Frontbuffer? Für mich macht das irgendwie keinen Sinn.
2. Kann mir jemand noch mal einen Foenbeitrag oder irgendeine andere seite verlinken wo das anschaulich erklärt ist, auch wie das abläuft? Ich finde hier nämlich nur recht komplexe Beispiel die ich nicht recht verstehen will, aber langfristig komm ich wohl nicht rum. Sad

Nochmal vielen Dank für eure liebe Hilfe. Very Happy Very Happy Very Happy

DAK

BeitragMo, Jul 21, 2014 22:33
Antworten mit Zitat
Benutzer-Profile anzeigen
Was gut funktionieren täte mit dem Auslagern von Text in eine Datei ist, dass du dir Sitationen schreibst.
Eine Situation besitzt einen Text, der angezeigt wird, sowie eine Liste an Möglichkeiten, die dann jeweils zu weiteren Situationen führen. Ein Beispiel:

Code: [AUSKLAPPEN]
Situation 1:
Text=Du stehst vor einer Tür. Was machst du?
Auswahl1Cond=öffnen
Auswahl1Ziel=2
Auswahl2Cond=eintreten
Auswahl2Ziel=3

Situation 2:
Text=Du hast die Tür geöffnet
....

Situation 3:
Text=Du hast die Tür eingetreten
....


Dabei kannst du das Ganze auch auf mehrere Teile für die Konditionen erweitern, um z.B. ein Verb und zwei Objekte unterzubringen, wie es BladeRunner vorgeschlagen hat.

Auf diese Weise brauchst du nur eine Engine programmieren, die mit dem Ganzen umgeht, und brauchst für den Rest nur externe Skriptdateien schreiben.
Damit bleibt deine Engine frei von Story-Inhalten und dadurch klein und übersichtlich. Gleichzeitig bleiben auch die externen Scripts übersichtlich, da jede einzelne Situation für sich bearbeitet werden kann.

Alles was du dafür brauchst sind Types. Die sind nicht so schwer, wie sie von Anfang an ausschauen.
Gewinner der 6. und der 68. BlitzCodeCompo

Tennisball

BeitragMo, Jul 21, 2014 22:42
Antworten mit Zitat
Benutzer-Profile anzeigen
Zitat:
Ich hätte nur noch einmal zwei Fragen bezüglich des Text statt Input Befehls.
1. Warum läuft das denn über den Frontbuffer? Für mich macht das irgendwie keinen Sinn.

Vermutlich meinst du Text statt Print. Print gibt immer in den Frontbuffer aus. Text gibt in den Buffer aus, den man mit SetBuffer "ausgewählt" hat. Letzteres ist wichtig für das sogenannte Doublebuffering (Bei Bedarf mal googlen). Kurze Erklärung: Hier wird alles erst in den BackBuffer gezeichnet, und dann mit Flip in den Frontbuffer übertragen, welcher dann angezeigt wird. Würde man das nicht machen, kann die Grafikausgabe flackern. Mischst du Doublebuffering mit Print, wirst du vermutlich deine Texte nicht sehen können, da sie mit Flip einfach überschrieben werden. Darum Text benutzen. Hier mal ein simples Beispiel:
BlitzBasic: [AUSKLAPPEN]
Graphics(640, 480)
SetBuffer(BackBuffer())

Local timer% = CreateTimer(60)

While Not KeyHit(1)
Text(MouseX(), MouseY(), "Beweg die Maus")

WaitTimer(timer)
Flip() ; Überträgt den Inhalt des Backbuffers in den Frontbuffer, welcher angezeigt wird.
Cls() ; Leert den Backbuffer.
Wend

Nimmst du die Zeilen mit Flip bzw. SetBuffer raus, wird es nicht flüssig aussehen/flackern.
Ich habe außerdem noch einen Timer hinzugefügt, damit die CPU nicht zu 100% ausgelastet wird. Wink

Gruß,
Tennisball

Neue Antwort erstellen


Übersicht BlitzBasic Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group