478 lines
22 KiB
Text
478 lines
22 KiB
Text
[#_lesson_30_shipping_it]
|
|
== Lektion 30: Ausliefern
|
|
|
|
image::learn/30-shipping.png[Die fertige Lektion, 480]
|
|
|
|
Du hast einen Ordner. Darin liegen ein Skript, ein paar Bilder, ein paar Sounds und die
|
|
`games.dat`, die du in Lektion dreizehn geschrieben hast. Er läuft, weil alles, was er braucht,
|
|
da ist, wo du es hingelegt hast, und du weißt, wo das ist.
|
|
|
|
Diese Lektion macht aus diesem Ordner eine einzige Datei, die jemand, der dich nie getroffen
|
|
hat, auf seinen Rechner kopieren und spielen kann. Das meiste davon sind zwei Befehlszeilen. Der
|
|
Rest ist der Teil, den dir niemand beibringt: was in die Datei gehört, wessen Arbeit außer
|
|
deiner darin steckt und wie du herausfindest, ob sie irgendwo anders als hier funktioniert.
|
|
|
|
[#_one_file]
|
|
=== Eine Datei
|
|
|
|
Ein fertiges Singe-Spiel wird als eine einzige `.game`-Datei ausgeliefert. Eine packst du so:
|
|
|
|
----
|
|
Singe --pack MyGame MyGame.game
|
|
----
|
|
|
|
Der erste Name ist der Ordner deines Spiels, der zweite die Datei, die geschrieben werden soll.
|
|
Singe liest jede Datei im Ordner in eine Datenbank und sagt, was es getan hat:
|
|
|
|
----
|
|
>>> Packed 42 files (3819204 bytes) from MyGame into MyGame.game
|
|
----
|
|
|
|
Starte es so, wie du alles startest:
|
|
|
|
----
|
|
Singe MyGame.game
|
|
----
|
|
|
|
Das startet den ersten Eintrag in der `games.dat` darin; `--entry=2` startet den zweiten, für
|
|
eine Datei, die mehr als ein Spiel enthält. Um es zu installieren, kopiert der Spieler
|
|
`MyGame.game` neben das Programm Singe. Das mitgelieferte Menü findet es und listet es auf.
|
|
Mehr gibt es nicht: keinen Installer, kein Archiv zum Entpacken, keinen Ordner, der an die
|
|
richtige Stelle muss.
|
|
|
|
[#_what_packing_does_to_every_file_name]
|
|
=== Was das Packen mit jedem Dateinamen macht
|
|
|
|
Nichts in deinem Spiel muss sich ändern, damit es gepackt werden kann, und es lohnt sich zu
|
|
wissen, warum, denn genau das wird dich am ehesten überraschen.
|
|
|
|
Jeder Name, den dein Spiel benutzt -- `dofile`, `require`, `io.open`, `spriteLoad`,
|
|
`soundLoad`, `fontLoad`, `videoLoad`, eine Frame-Datei, eine `controls.cfg` -- geht durch ein
|
|
und dieselbe Suche. Bei einem losen Ordner ist diese Suche das gewöhnliche Dateisystem. Bei
|
|
einem gepackten Spiel wird der Name an drei Orten probiert, und der erste Treffer gewinnt:
|
|
|
|
. ein loser Ordner, der so heißt wie die Datei ohne ihre Endung -- `MyGame/` neben
|
|
`MyGame.game`;
|
|
. der Datenordner des Spiels, unter `files/`, wo alles gelandet ist, was das Spiel
|
|
schreibt;
|
|
. die Datenbank selbst.
|
|
|
|
Innerhalb der Datenbank werden Namen ohne Rücksicht auf Großbuchstaben verglichen, mit beiden
|
|
Arten von Schrägstrich und relativ zur Wurzel des Spiels. Ein führender Ordnername, der so heißt
|
|
wie das Spiel selbst, wird ignoriert, also finden `MyGame/art/spaceship.png`,
|
|
`art/spaceship.png` und `DIR .. "art/spaceship.png"` alle dieselbe Datei.
|
|
|
|
Daraus folgen zwei Regeln, und wenn du dem Buch gefolgt bist, hältst du dich schon an beide.
|
|
Benutze `DIR` für alles, was du lädst, damit ein Name nie davon abhängt, wo Singe gestartet
|
|
wurde. Und schreib nur unterhalb von `singeGetDataPath()`, wo `saveSet` die Dinge schon ablegt --
|
|
ein gepacktes Spiel wird schreibgeschützt geöffnet und nie beschrieben, also funktioniert ein
|
|
Spiel, das neben sich selbst speichert, lose und scheitert gepackt.
|
|
|
|
Zwei Namen gehen mit Absicht woandershin. Alles, was mit `Singe/` anfängt, bleibt im echten
|
|
Dateisystem, also funktioniert `dofile("Singe/Framework.singe")` weiter, und der Datenordner
|
|
ebenso. Und ein Name mit `..` darin, der versucht, aus dem Spiel herauszuklettern, führt
|
|
überhaupt nirgendwohin: `io.open` gibt `nil` zurück und eine Meldung, die sagt, dass der Name
|
|
aus dem Spiel hinausreicht, und `spriteLoad` und seine Verwandten melden die Datei als fehlend.
|
|
|
|
[#_the_loose_folder_still_wins]
|
|
=== Der lose Ordner gewinnt trotzdem
|
|
|
|
Den ersten Ort in der Liste wirst du jeden Tag benutzen. Ein Ordner, der so heißt wie die
|
|
gepackte Datei und neben ihr liegt, schlägt die gepackten Kopien Datei für Datei.
|
|
|
|
----
|
|
MyGame.game das gepackte Spiel
|
|
MyGame/ ein Ordner mit einer geänderten Datei darin
|
|
----
|
|
|
|
Leg eine einzige korrigierte `art/spaceship.png` hinein, und das gepackte Spiel läuft mit deinem
|
|
neuen Bild und allem anderen aus der Datenbank. Nichts muss neu gepackt werden, um eine Änderung
|
|
auszuprobieren, und du kannst deinen ganzen Arbeitsordner dort liegen lassen, während du das
|
|
Spiel fertig machst. So moddet auch ein Spieler eins, und das kannst du für ein Feature halten
|
|
oder nicht.
|
|
|
|
Daraus folgen zwei Dinge. Prüf dein Spiel einmal, während dieser Ordner einen anderen Namen
|
|
hat, sonst lieferst du eine Datei aus, die nur auf dem Rechner funktioniert, der den
|
|
Ordner hat. Und wisse, dass Packen nichts versteckt: `--unpack` ist eine dokumentierte Option,
|
|
und jeder kann sie ausführen. Pack dein Spiel, weil es dann eine einzige Datei ist, nicht weil
|
|
es dann eine verschlossene ist.
|
|
|
|
[#_patching_a_released_game]
|
|
=== Ein veröffentlichtes Spiel patchen
|
|
|
|
Jemand hat dein Spiel, in einem Skript steckt ein Fehler, und du willst ihm nicht noch einmal
|
|
vierzig Megabyte schicken.
|
|
|
|
----
|
|
Singe --patch MyGame.game fixes
|
|
----
|
|
|
|
`fixes` ist ein Ordner, der wie das Spiel aufgebaut ist -- dieselben Namen an denselben Stellen --
|
|
und nur die Dateien enthält, die sich geändert haben. Dateien, die der Patch nicht nennt,
|
|
bleiben unangetastet. Die ganze Änderung ist eine einzige Transaktion, also lässt ein
|
|
unterbrochener Patch das Spiel genau so, wie es war, statt halb ersetzt.
|
|
|
|
Ein Patch kann als eigene Datei reisen, gemacht mit dem Packer:
|
|
|
|
----
|
|
Singe --pack fixes MyGame-1.0.2.patch
|
|
Singe --patch MyGame.game MyGame-1.0.2.patch
|
|
----
|
|
|
|
Eine `.patch`-Datei braucht keine `games.dat`, kann nicht gestartet werden, und das Menü
|
|
ignoriert sie. Sie kann außerdem eine Tabelle `removed(path)` mitbringen, die Dateien nennt, die
|
|
gelöscht werden sollen, für das Artwork, das du durch etwas mit einem anderen Namen ersetzt
|
|
hast. So oder so prüft Singe das gepatchte Spiel danach genauso, wie es ein neues prüft, also
|
|
kann ein Patch deine `games.dat` nicht auf eine Datei zeigen lassen, die nicht mehr da ist.
|
|
|
|
Und um alles wieder herauszuholen:
|
|
|
|
----
|
|
Singe --unpack MyGame.game MyGameUnpacked
|
|
----
|
|
|
|
[#_what_the_packer_refuses]
|
|
=== Was der Packer ablehnt
|
|
|
|
Der Packer baut nicht aus allem Möglichen ein Spiel, und jede Ablehnung betrifft etwas, das
|
|
später kaputtgehen würde. Er lehnt einen Ordner ohne `games.dat` in seiner Wurzel ab. Er lehnt
|
|
eine `Framework.singe` ab, eine `controls.dat`, alles, was auf `exe`, `sh`, `bat` oder `cmd`
|
|
endet, und eine Datei ohne Endung, deren Name mit `singe` anfängt -- mit anderen Worten eine
|
|
Kopie der Engine oder ihrer Hilfsdateien, die sich in deinen Spielordner verirrt hat. Er lehnt
|
|
zwei Dateien ab, deren Namen sich nur in Großbuchstaben unterscheiden, weil die Suche sie nicht
|
|
auseinanderhalten kann. Er lehnt einen Eintrag auf oberster Ebene ab, der so heißt wie der
|
|
Ordner selbst. Und er prüft deine `games.dat`, bevor er festschreibt: Jeder Eintrag braucht ein
|
|
`SCRIPT`, und jedes `SCRIPT`, `VIDEO`, `CABINET`, `MARQUEE` und `ATTRACT`, das sie nennt, muss
|
|
eine Datei sein, die wirklich darin liegt. Ein falscher Pfad wird mit dem Titel des Eintrags
|
|
gemeldet, und nichts wird geschrieben.
|
|
|
|
Jede Beschwerde nennt die Datei, und du bekommst eine Liste davon statt nur der ersten, sodass
|
|
dir ein einziger Lauf alles sagt, was zu beheben ist.
|
|
|
|
[#_what_to_include_and_what_to_leave_out]
|
|
=== Was hineingehört und was draußen bleibt
|
|
|
|
Sieh in deinen Spielordner. Manches darin ist das Spiel, und manches ist, wie das Spiel
|
|
entstanden ist.
|
|
|
|
Ausliefern: das Skript und alles, was es lädt, die `games.dat`, das Artwork, das das Menü
|
|
zeigt (`CABINET`, `MARQUEE` und den `ATTRACT`-Clip), deine Credits- und Lizenzdateien und eine
|
|
Readme, wenn du eine hast.
|
|
|
|
Draußen lassen: die Zeichnungen, aus denen du das Artwork exportiert hast, die Aufnahmen des
|
|
Sounds, die du nicht benutzt hast, deine Notizen, Testskripte, Screenshots, alles, was `old`
|
|
oder `backup` heißt, den Ordner `data` mit deinen eigenen Spielständen und deinen eigenen
|
|
Highscores darin und alles, was ein Fremder nicht lesen soll. All das lässt sich bestens packen,
|
|
und genau das ist das Problem: Es geht mit dem Spiel hinaus, und es ist für jeden nur ein
|
|
`--unpack` entfernt.
|
|
|
|
Der zuverlässige Weg herauszufinden, was du tatsächlich ausgeliefert hast, ist, es in einen
|
|
leeren Ordner zu entpacken und nachzusehen. Mach das einmal bei jeder Veröffentlichung. Es dauert
|
|
eine Minute, und es ist die einzige Prüfung, die sieht, was du vergessen hast, statt was du im
|
|
Kopf hast.
|
|
|
|
[#_credits_and_licenses]
|
|
=== Credits und Lizenzen
|
|
|
|
Wenn irgendeine Grafik, ein Sound, Musik oder eine Schriftart in deinem Spiel von jemand anderem
|
|
gemacht wurde, lieferst du dessen Arbeit aus, und die Bedingungen, unter denen er sie hergegeben
|
|
hat, sind eine Pflicht und keine Höflichkeit. Das ist keine Formsache, und es geht nicht um
|
|
Anwälte: Ein Mensch hat dieses Ding gemacht und dafür um eine bestimmte Sache gebeten, meistens
|
|
um seinen Namen.
|
|
|
|
Führ Buch, während du arbeitest, nicht erst am Ende. Eine reine Textdatei im Spielordner mit
|
|
einer Zeile pro Stück: was die Datei ist, woher sie kommt, wer sie gemacht hat, welche Lizenz und
|
|
was diese Lizenz verlangt. Es nebenbei aufzuschreiben dauert Sekunden. Es in der Nacht vor einer
|
|
Veröffentlichung aus dem Browserverlauf zu rekonstruieren ist genau der Weg, auf dem Credits
|
|
verloren gehen.
|
|
|
|
Ein paar Dinge, die du wissen solltest, bevor du sie brauchst. „Kostenlos herunterladen“ ist
|
|
keine Lizenz; such nach den eigentlichen Bedingungen. Creative Commons `BY` heißt, dass du die
|
|
verlangte Namensnennung angeben musst, und es gibt keine Ausnahme für Spiele. `CC0` und
|
|
gemeinfreie Werke verlangen nichts, und trotzdem jemanden zu nennen kostet dich eine Zeile.
|
|
Schriftarten haben ihre eigenen Lizenzen, und manche erlauben das Einbetten, andere nicht. Musik
|
|
aus einem Video darfst du fast nie benutzen. Alles, was ein Werkzeug erzeugt hat, ist nur so
|
|
sauber wie die Bedingungen des Werkzeugs.
|
|
|
|
Dann bring die Credits irgendwo unter, wo ein Spieler sie erreichen kann, und zwar im Spiel.
|
|
Nicht nur in einer Textdatei daneben -- ein gepacktes Spiel ist eine einzige Datei, und die
|
|
Textdatei steckt darin, wo niemand nachsieht. Das Skript am Ende dieser Lektion ist dieser
|
|
Bildschirm.
|
|
|
|
Singes eigene `Singe/LICENSES` ist die Form, die du übernehmen solltest: eine Zeile pro
|
|
Bibliothek, mit ihrer Version, ihrer Lizenz und ihrer Herkunft. Die Liste deines Spiels wird
|
|
kürzer sein, und die Aufgabe ist dieselbe.
|
|
|
|
[#_a_version_number_that_means_something]
|
|
=== Eine Versionsnummer, die etwas bedeutet
|
|
|
|
Dein Spiel wird eine zweite Veröffentlichung haben. Die erste bleibt da draußen, solange es das
|
|
Internet gibt, und irgendwer wird dir einen Fehlerbericht dazu schicken, ohne eine Ahnung, welche
|
|
Version er hat.
|
|
|
|
Also such dir eine Nummer aus, setz sie dorthin, wo ein Spieler sie lesen kann, und benutze nie
|
|
eine zweimal. Drei Teile sind die übliche Form -- `1.0.2` -- und was die Teile bedeuten, zählt
|
|
weniger, als dass sie immer nur steigen. Erhöh den letzten für eine Korrektur, den mittleren für
|
|
etwas Neues, den ersten, wenn du ein anderes Spiel gemacht hast. Was immer du wählst, schreib es
|
|
in deine Readme, damit sich dein zukünftiges Ich auch daran hält.
|
|
|
|
`games.dat` hat keinen Schlüssel für die Version, also trägst du die Nummer selbst. Setz sie als
|
|
Konstante ins Skript, zeig sie auf dem Credits-Bildschirm und setz sie in den Namen dessen, was
|
|
du verschickst (`MyGame-1.0.2.game`, `MyGame-1.0.2.patch`). Dann nennt der Fehlerbericht die
|
|
Version auf dem Bildschirm, und du weißt, um welche Dateien es geht.
|
|
|
|
[#_testing_on_a_machine_that_is_not_yours]
|
|
=== Auf einem Rechner testen, der nicht deiner ist
|
|
|
|
Dein Rechner ist der schlechteste Ort überhaupt, um eine Veröffentlichung zu testen. Er hat die
|
|
Schriftarten, die du installiert hast, den Ordner, an dem du gearbeitet hast, die Speicherdatei
|
|
mit deinen Einstellungen darin und die Engine in dem Zustand, in dem ein Monat Arbeit sie
|
|
hinterlassen hat.
|
|
|
|
Der Mindesttest, der Reihe nach:
|
|
|
|
. Entpack die Veröffentlichung in einen leeren Ordner und lies die Dateiliste.
|
|
. Kopier *nur* die `.game`-Datei in einen anderen Ordner, mit einer frischen Kopie von Singe und
|
|
sonst nichts, und starte sie aus dem Menü, so wie ein Spieler es tun wird.
|
|
. Lösch den Ordner `data` dieses Ordners und starte es noch einmal. Das ist ein erster Start
|
|
auf einem neuen Rechner: keine gespeicherten Einstellungen, keine Highscores, kein angemeldetes
|
|
Konto. Ein Spiel, das erst beim zweiten Start funktioniert, ist ein häufiger Fehler, und so
|
|
findest du ihn.
|
|
. Gib es einer Person, die es nie gesehen hat, und sieh zu, ohne zu helfen.
|
|
|
|
Dann teste den Rechner genauso wie das Spiel. Er wird eine andere Bildschirmform haben, also sieh
|
|
dir dein Spiel im Fenster und im Vollbild an. Vielleicht hat er überhaupt kein 3D-Gerät, und
|
|
darum hat das Menü einen zweiten Renderer. Die Seite *System
|
|
Information* der Service-Tools listet die Engine-Version, das Betriebssystem, den Prozessor, den
|
|
Renderer, die Decoder und den Datenordner auf einem Bildschirm auf; ein Foto dieser Seite
|
|
beantwortet den größten Teil eines Fehlerberichts, und wenn du das deinen Spielern in deiner
|
|
Readme sagst, ersparst du dir eine Woche Raterei.
|
|
|
|
[#_the_script_2]
|
|
=== Das Skript
|
|
|
|
Ein Bildschirm mit Credits und Version. Er ist schnell geschrieben, und er ist der Teil des
|
|
Auslieferns, den die meisten Spiele falsch machen.
|
|
|
|
[source,lua]
|
|
----
|
|
dofile("Singe/Framework.singe")
|
|
|
|
local GAME_NAME = "Rock Dodger"
|
|
local GAME_VERSION = "1.0.2"
|
|
|
|
local CREDITS = {
|
|
"Written and drawn by <your name here>",
|
|
"",
|
|
"Music \"Night Drive\" by A. Composer",
|
|
" Creative Commons BY 4.0",
|
|
" example.org/night-drive",
|
|
"",
|
|
"Explosion freesound.org user \"thud\"",
|
|
" Creative Commons CC0",
|
|
"",
|
|
"Font FreeSansBold",
|
|
" GPL with the font exception",
|
|
"",
|
|
"Engine Singe " .. SINGE_VERSION_STRING,
|
|
" GPL version 3",
|
|
" See Singe/LICENSES for the",
|
|
" libraries Singe is built on.",
|
|
"",
|
|
"Thank you for playing.",
|
|
}
|
|
|
|
local FIRST_ROW = 4
|
|
local ROWS = 10
|
|
|
|
local scroll = 0
|
|
|
|
|
|
local function scrollBy(lines)
|
|
local most = #CREDITS - ROWS
|
|
|
|
if most < 0 then
|
|
most = 0
|
|
end
|
|
scroll = scroll + lines
|
|
if scroll < 0 then
|
|
scroll = 0
|
|
end
|
|
if scroll > most then
|
|
scroll = most
|
|
end
|
|
end
|
|
|
|
|
|
function onInputPressed(what)
|
|
if what == SWITCH_UP then
|
|
scrollBy(-1)
|
|
elseif what == SWITCH_DOWN then
|
|
scrollBy(1)
|
|
elseif what == SWITCH_BUTTON1 then
|
|
scroll = 0
|
|
end
|
|
end
|
|
|
|
|
|
function onOverlayUpdate()
|
|
local line = nil
|
|
|
|
overlayClear()
|
|
overlayPrint(2, 0, GAME_NAME .. " version " .. GAME_VERSION)
|
|
overlayPrint(2, 1, "Singe " .. SINGE_VERSION_STRING .. " id " .. (singeGetGameId() or "none"))
|
|
|
|
for row = 0, ROWS - 1 do
|
|
line = CREDITS[scroll + row + 1]
|
|
if line then
|
|
overlayPrint(2, FIRST_ROW + row, line)
|
|
end
|
|
end
|
|
|
|
overlayPrint(2, FIRST_ROW + ROWS + 1, "Saves go to:")
|
|
overlayPrint(2, FIRST_ROW + ROWS + 2, singeGetDataPath())
|
|
overlayPrint(2, 17, "Up and down scroll. Button 1 goes back to the top.")
|
|
|
|
return OVERLAY_UPDATED
|
|
end
|
|
----
|
|
|
|
Ersetze jede Zeile von `CREDITS` durch die Wahrheit über dein eigenes Spiel. Die Platzhalter
|
|
sind da, um die Form zu zeigen: was es ist, wer es gemacht hat, welche Lizenz und woher es
|
|
kommt.
|
|
|
|
[#_what_just_happened_25]
|
|
=== Was gerade passiert ist
|
|
|
|
[source,lua]
|
|
----
|
|
local GAME_VERSION = "1.0.2"
|
|
----
|
|
|
|
Ein Ort, ganz oben, in Großbuchstaben, weil sich der Wert nie ändert, während das Spiel läuft.
|
|
Alles, was die Version zeigt, liest diesen Wert, also ist das Erhöhen eine einzige Änderung
|
|
statt einer Suche.
|
|
|
|
[source,lua]
|
|
----
|
|
"Music \"Night Drive\" by A. Composer",
|
|
----
|
|
|
|
Diese Backslashes sind die Escape-Sequenz aus Lektion eins: ein doppeltes Anführungszeichen, das
|
|
zum Text gehört, statt den String zu beenden. Titel haben Anführungszeichen, und darum stößt eine
|
|
Credits-Liste gleich in ihrer ersten Zeile darauf.
|
|
|
|
[source,lua]
|
|
----
|
|
for row = 0, ROWS - 1 do
|
|
line = CREDITS[scroll + row + 1]
|
|
if line then
|
|
----
|
|
|
|
Zehn Bildschirmzeilen zeigen zehn Zeilen aus einer Liste, die länger als zehn ist. `scroll`
|
|
sagt, wie weit das Fenster in der Liste nach unten gerückt ist. Das `+ 1` gibt es, weil Lua eine
|
|
Liste ab eins zählt und die Bildschirmzeilen ab null, und das `if line then` gibt es, weil das
|
|
Ende der Liste `nil` ist -- über das Ende einer Tabelle hinaus zu lesen ist in Lua kein Fehler,
|
|
es gibt dir einfach nichts, aber nichts auszugeben wäre einer.
|
|
|
|
[source,lua]
|
|
----
|
|
overlayPrint(2, 1, "Singe " .. SINGE_VERSION_STRING .. " id " .. (singeGetGameId() or "none"))
|
|
----
|
|
|
|
Drei Fakten, die ein Fehlerbericht braucht und die ein Spieler sonst nicht finden kann: deine
|
|
Version in der Zeile darüber, die Version der Engine und die ID des Spiels.
|
|
`SINGE_VERSION_STRING` ist eine globale Variable, die die Engine setzt, bevor dein Skript läuft.
|
|
`or "none"` deckt den Fall ab, dass das Spiel keinen Eintrag in `games.dat` hat, sodass die
|
|
Zeile etwas ausgibt, statt an einem `nil` abzustürzen.
|
|
|
|
[source,lua]
|
|
----
|
|
overlayPrint(2, FIRST_ROW + ROWS + 2, singeGetDataPath())
|
|
----
|
|
|
|
Wohin dieses Spiel schreibt. Bei einem gepackten Spiel ist das der einzige Ort, an dem überhaupt
|
|
etwas geschrieben wird, und es ist der Ordner, den ein Spieler löscht, um sauber neu anzufangen.
|
|
Ihn anzuzeigen macht aus „Wo sind meine Spielstände?“ eine Frage, auf die du nie antworten
|
|
musst.
|
|
|
|
Wenn dieser Pfad rechts über den Bildschirm hinausläuft, ist nichts kaputt: `overlayPrint` lässt
|
|
die Zeichen weg, die über den Rand gehen würden. Das Standard-Overlay ist sechzig Zeichenzellen
|
|
breit.
|
|
|
|
[#_try_it_30]
|
|
=== Probier es aus
|
|
|
|
. *Pack es.* Leg dieses Skript in deinen Spielordner, führ `--pack` aus und starte die
|
|
`.game`-Datei. Dann entpack sie in einen leeren Ordner und lies die Liste dessen, was
|
|
herauskam.
|
|
. *Überschreib eine Datei.* Leg neben der gepackten Datei einen Ordner an, der so heißt wie sie
|
|
ohne die Endung, und leg eine geänderte Datei hinein. Starte das gepackte Spiel und sieh zu,
|
|
wie deine lose Datei gewinnt.
|
|
. *Mach einen Patch.* Ändere die Version in `1.0.3`, leg nur das Skript in einen Ordner
|
|
`fixes`, pack ihn als `.patch` und spiel ihn in das Spiel ein, das du schon gemacht hast.
|
|
Prüf die Version auf dem Bildschirm.
|
|
. *Stell den Packer auf die Probe.* Benenn deine `games.dat` kurz um und versuch zu packen.
|
|
Dann benenn sie zurück, lass einen ihrer `SCRIPT`-Namen auf eine Datei zeigen, die es nicht
|
|
gibt, und versuch es noch einmal. Zwei verschiedene Ablehnungen, beide bevor irgendetwas
|
|
geschrieben wird.
|
|
. *Schreib die echten Credits.* Geh den Ordner deines Spiels Datei für Datei durch und schreib
|
|
eine Zeile für jede, die du nicht selbst gemacht hast. Wenn du dich nicht erinnern kannst,
|
|
woher etwas kommt, ist das die Antwort auf die Frage, ob du es ausliefern solltest.
|
|
|
|
[#_break_it_on_purpose_29]
|
|
=== Mach es absichtlich kaputt
|
|
|
|
Starte Singe einmal *innerhalb* deines Spielordners, sodass die Engine ihre Hilfsdateien dort
|
|
entpackt, und pack ihn dann:
|
|
|
|
----
|
|
Singe --pack MyGame MyGame.game
|
|
----
|
|
|
|
----
|
|
!!! MyGame has Framework.singe: Singe/Framework.singe
|
|
!!! MyGame has sh file: Menu.sh
|
|
----
|
|
|
|
Nichts wird geschrieben. Jede Zeile nennt eine Datei, die er nicht nimmt, und warum, und du
|
|
bekommst sie vielleicht in der anderen Reihenfolge oder noch andere dazu. In deinem Spiel liegt
|
|
eine Kopie des eigenen Hilfsordners der Engine, und daneben das Startskript, das die Engine
|
|
geschrieben hat, weil du Singe irgendwann von dort aus gestartet hast. Ein Spiel darf nie eins
|
|
von beiden mitbringen: Die Hilfsdateien gehören zu dem Singe, das der Spieler gerade benutzt,
|
|
nicht zu deinem Spiel, und eine gepackte `Framework.singe` würde statt seiner geladen. Unter
|
|
Windows nennt die zweite Zeile stattdessen `Menu.bat`.
|
|
|
|
Lösch den Ordner `Singe` und `Menu.sh` (oder `Menu.bat`) aus deinem Spiel, halte deinen Spielordner als einen Ordner neben dem
|
|
Programm Singe statt um es herum, und pack noch einmal. Das ist derselbe Aufbau, den Lektion
|
|
dreizehn beschrieben hat, und dieser Fehler ist das, was passiert, wenn er verrutscht.
|
|
|
|
[#_what_you_learned_30]
|
|
=== Was du gelernt hast
|
|
|
|
* `Singe --pack folder name.game` macht aus einem Spielordner eine einzige Datei, und wer diese
|
|
Datei neben das Programm Singe kopiert, hat sie installiert.
|
|
* Jeder Dateiname in einem gepackten Spiel wird zuerst in einem losen Ordner gesucht, dann im
|
|
Datenordner, dann in der Datenbank.
|
|
* Ein loser Ordner, der so heißt wie die gepackte Datei, überschreibt sie Datei für Datei, und
|
|
so arbeitest du weiter -- und so lieferst du eine kaputte Veröffentlichung aus, wenn du
|
|
vergisst, dass er da ist.
|
|
* Lade mit `DIR` und schreib nur unterhalb von `singeGetDataPath()`, sonst funktioniert dein
|
|
Spiel lose und scheitert gepackt.
|
|
* `--patch` ersetzt Dateien in einem veröffentlichten Spiel in einer einzigen Transaktion;
|
|
`--unpack` schreibt alles wieder heraus.
|
|
* Der Packer lehnt Engine-Dateien ab, Namenskonflikte und eine `games.dat`, die auf Dateien
|
|
zeigt, die er nicht hat.
|
|
* Entpack deine eigene Veröffentlichung und lies, was darin ist, bevor du sie irgendwohin
|
|
schickst.
|
|
* Die Grafiken und Sounds anderer Leute kommen mit Bedingungen, und sie zu erfüllen heißt: ein
|
|
Credits-Bildschirm im Spiel.
|
|
* Eine Versionsnummer steigt immer nur, und sie gehört auf den Bildschirm.
|
|
* Teste auf einem Rechner, der nicht deiner ist, und fang ganz ohne Ordner `data` an.
|
|
|
|
[#_next_time_30]
|
|
=== Beim nächsten Mal
|
|
|
|
Eine Datei, installiert, mit Credits und nummeriert. Die letzte Lektion steckt sie in eine
|
|
Holzkiste mit einem Münzschlitz und ohne Tastatur, und das ändert mehr an deinem Spiel, als du
|
|
denkst.
|