Schön zu sehen wie es hier nach und nach voran mit dem Projekt geht.
Der Titelbildschirm und der Ladebildschirm gefallen mir auch sehr gut. Passt gut zum restlichen Grafikstil und zur Farbpalette.
Freue mich schon auf eine Demo, bis dahin verfolge ich weiterhin den Thread
Ich habe gestern lange an meinem Speicher/Lade-System rumgespielt und bin jetzt zufrieden damit. Die Telefonhäuschen in der Spielwelt sind Speicherpunkte, die unser kleiner Held nutzen kann um sein Spiel zu sichern. Das ganze System läuft wie folgt ab:
DIe meiste Frimmelarbeit haben tatsächlich das Menü + Interaktion und das Anzeigen der Dateien aus den Files gekostet. Die Speicher- und Ladefunktionen dagegen gingen kurz und knackig. Da Sabaku neugierig war, wie die Funktionen ablaufen, möchte ich euch ein bisschen darüber erzählen. Fragt gerne nach, wenn etwas unklar ist.
Das grobe Prinzip des Game Makers: Spiele bestehen aus verschiedenen Räumen, die verschiedene Objekte beinhalten können. Diese Objekte werden beim Betreten des Raums geladen und können mit Code versehen werden, um bestimmte Aktionen durchzuführen. Ein Object kann verschiedene "Events" beinhalten. Die (meiner Meinung nach) wichtigsten Events:
1. Creation: Ein Codefragment, das beim Erstellen des Objects einmal ausgeführt wird. Damit können Variablen initiiert werden.
2. Step: Ein Codefragment, das jedes Frame abgespielt wird. Sehr mächtig, um das Spielsystem an sich zu steuern.
3. Draw: Ein Codefragment, das jedes Frame abgespielt wird und die gewünschte Information auf dem Bildschirm "zeichnet" (Bilder, Sprites). Quasi der "Zeichnen-Step".
Natürlich gibt es noch unzählig andere Events, die sehr nützlich sind, die möchte ich aber an dieser Stelle nicht ansprechen.
Wenn das Spiel gestartet wird, landet der Spieler beim Titelbildschirm. Beim Betreten dieses Raumes wird ein Object initiiert, welcher "main_control" heißt und alle wichtigen Spielvariablen über sein Creation-Event festlegt. Ich habe ein paar Beispiele aufgezählt, damit ihr ein Gefühl kriegt, wie das aussieht:
Beim Codebeispiel wird die Zeitzählung auf null gesetzt, die Attribute des Helden geladen und die (ich nenne es mal so) Gegenstandsdatenbank geladen, damit das Spiel weiß, was ein Gegenstand für Attribute hat. Die Attribute werden in einer sogenannten "ds_map" geladen. Das ist eine Datenstruktur, die "Keys" mit entsprechenden "Werten" speichert, also sowas wie "HP" und den Wert der Lebenspunkte.
Soviel also zu den Variablen. Immer, wenn das Spiel startet, werden diese Variablen geladen und festgesetzt. Der Spieler startet also immer bei Lvl 1, etc. Nun möchte ich aber meinen Spielstand festhalten und darauf zugreifen können. Dafür eignet sich folgendes Speicherskript, welches ich beispielhaft vorstelle:
Das Spiel öffnet damit eine Ini-Datei, die im "AppData"-Ordner von Windows abgelegt wird. Über den Befehl "ini_write_real(section, key, value)" können Zahlen dort abgelegt werden, eingeteilt nach Sektionen und dann als Key-Value-Wertepaar gespeichert. Das ist bei einfachen Variablen kein Thema. Was ist aber mit unseren komplexen Datenstrukturen wie den "ds_maps"? Alles einzeln einzutippen wäre zu aufwendig. Zum Glück hat der Game-Maker die Funktion "ds_map_write". Damit wird die gesamte ds_map in einen unleserlichen Zeichenstrang umgewandelt, den "String". Dieser String kann kann unproblematisch in die Ini-File abgelegt werden. Das Ganze sieht das ungefähr so aus:
Über das Prinzip können auch Quest/Story-Fortschritte einfach abgespeichert werden. Ich werde zu diesen Zwecken ebenfalls eine Datenstruktur mit Key und Value wählen, z.B. "Switch1" und "0" bzw "1". Diese entspricht vom Prinzip der "Switch"-Liste beim RPG-Maker und kann genau wie dort auch hier genutzt werden. Wenn ein Quest aktiviert ist, stellt man den Switch für diese Quest von "0" auf "1" und das Spiel was bescheid. Statt 0 und 1 gehen natürlich auch "true" und "false" (die klassischen Booleans halt). Wie ihr bemerkt habe, habe ich die Gegenstandsattribute nicht gespeichert. Das macht Sinn, weil sie von Anfang bis Ende (hoffentlich!) konstant bleiben!
Nun starten wir das Spiel neu oder die Variablen werden neu initiiert und sind wie vor dem Speichern. Wie laden wir die abgespeicherten Daten nun? Dazu nutze ich ein Load-Skript, welches die initiierten Variablen mit den gespeicherten Variablen überschreibt.
Die Ini-Datei wird geöffnet, die Variablen neu festgelegt und der String wird rausgelesen und über die "ds_map_read"-Funktion wieder in die funktionierende Datenstruktur übersetzt. Alles schick, ein Problem ist noch da: der Spieler kann dieses Save-File selber öffnen und sich so natürlich mehr Attribute und Level zuschummeln (ach, als Kind habe ich so viele Spiele gehackt! ). Hierfür hat der Game-Maker eine Verschlüsselung. Über "base64_encode(string)" kann ein String in einen verschlüsselten String umgewandelt werden und über "base64_decode(string)" beim Laden wieder in einen leserlichen String umgewandelt werden. Bei Zahlen muss der Wert erst in einen String umgewandelt und nach dem Decoden wieder in eine Zahl zurückgewandelt werden.
Soo, ich hoffe ich konnte für ein bisschen Klarheit sorgen und euch die Speicher- und Ladefunktionen näher bringen. Ihr könnt den Code natürlich nutzen wie ihr wollt und ein bisschen rumspielen! Sagt mir bescheid, ob euch solche technischen Abhandlungen interessieren, dann schreibe ich gerne öfter über sowas.
Stay tuned und bis zum nächsten (und weniger technischen) Devlog!