[#_lesson_21_subtitles] == Lektion 21: Untertitel image::learn/21-subtitles.png[Die fertige Lektion, 480] Irgendwann spielt jemand dein Spiel, der nicht hören kann. Wenn in deinem Spiel gesprochen wird und es keine Untertitel hat, kann diese Person es nicht spielen. Nicht „es ist schwerer für sie“ -- sie kommt zur ersten Szene, in der eine Figur das sagt, was dem Spieler erklärt, was er tun soll, und hört auf. Das ist schon alles, und es ist der Grund, warum es diese Lektion gibt, statt nur einen Absatz in Lektion neunzehn. Der andere Grund ist praktisch. Untertitel nachträglich in ein fertiges Spiel einzubauen ist eine Qual. Du musst jede gesprochene Zeile finden, herausfinden, auf welchem Frame sie liegt, und dabei entdecken, dass die Hälfte deiner Zeiten falsch ist, weil dein Video mitten im Projekt neu kodiert wurde. Machst du es, während du das Spiel schreibst, ist es ein Nachmittag. Singe gibt dir ein System, das den schweren Teil erledigt, und diese Lektion zeigt, wie man es benutzt. [#_it_is_not_ordinary_text_drawing] === Es ist nicht einfach gezeichneter Text Du könntest Untertitel selbst machen. Eine Liste von Zeilen mit Start- und Endzeiten führen, in `onOverlayUpdate` auf die Uhr sehen und die aktuelle Zeile mit `fontToSprite` zeichnen. Das haben schon Leute gemacht. Hier ist, warum du es nicht tun solltest. Untertitel richten sich nach *der Disc*, nicht nach einer Uhr. Wenn `srtLoad` deine Datei liest, rechnet es jeden Zeitstempel sofort in eine Frame-Nummer der Disc um, mit der Bildrate der Disc, die geladen ist. Ab diesem Moment sieht die Engine überhaupt nicht mehr auf eine Uhr; bei jedem gezeichneten Frame fragt sie, welcher Cue den Frame abdeckt, auf dem die Disc gerade steht. Diese eine Entscheidung bringt dir das, was ein selbst gebautes System nie richtig hinbekommt: *Ein Untertitel übersteht einen Sprung.* Schick die Disc irgendwohin -- `discSearch`, `discSkipForward`, ein Einzelschritt, eine Verzweigung in ein anderes Segment -- und im nächsten gezeichneten Frame steht die richtige Zeile da, vorwärts wie rückwärts, ohne dass etwas neu geladen oder eine Uhr zurückgesetzt wird. In einem verzweigten Spiel wie in Lektion sechzehn, in dem die Entscheidungen des Spielers die Disc ständig herumspringen lassen, ist das der Unterschied zwischen funktioniert und funktioniert fast. Es zeichnet auch an der richtigen Stelle. Das Untertitel-Dokument liegt über dem Bild, über deinem Overlay, unter jeder GUI im Bildschirmraum, und es folgt dem Bild, wohin die Einstellungen des Spielers es auch setzen -- ein verkleinertes Bild, ein verschobenes, ein gedrehter Automat, ein Loch in einem Bezel. Es nimmt keine Mauseingaben an, also geht ein Lightgun-Schuss direkt hindurch. Das alles selbst richtig hinzubekommen ist eine Woche, die du nicht eingeplant hattest. [#_write_the_file] === Schreib die Datei Untertitel kommen in eine SubRip-Datei, die auf `.srt` endet und reiner Text ist, den du in jedem Editor tippen kannst. Es ist eines der einfachsten Formate, die es gibt. Leg neben deinem Skript eine Datei namens `subtitles.srt` an: ---- 1 00:00:01,000 --> 00:00:04,000 They told us the cave was empty. 2 00:00:04,200 --> 00:00:08,000 They were wrong about a great many things. 3 00:00:08,500 --> 00:00:13,000 Keep your light on the floor. ---- Ein Cue besteht aus vier Dingen. Einer Nummer auf einer eigenen Zeile. Einer Zeile mit zwei Zeitstempeln und dazwischen einem Pfeil aus zwei Bindestrichen und einem Größer-als-Zeichen. Einer oder mehreren Zeilen Text. Einer Leerzeile zum Abschluss. Ein Zeitstempel ist `HH:MM:SS,mmm` -- Stunden, Minuten und Sekunden, durch Doppelpunkte getrennt, dann ein Komma, dann Millisekunden. Das Komma ist kein Tippfehler, und ein Punkt geht nicht. `00:00:04,200` ist vier Sekunden und eine Fünftelsekunde nach dem Start. Die Cue-Nummern werden gelesen und weggeworfen, also müssen sie weder in der richtigen Reihenfolge noch überhaupt richtig sein. Nummerier sie trotzdem; irgendwann wirst du Cue einundvierzig suchen. Cue zwei zeigt, wie ein Cue mit zwei Zeilen aussieht. Die Zeilen werden so gezeigt, wie du sie geschrieben hast, also entscheidest *du*, wo eine lange Zeile umbricht, und das ist wichtiger, als du denkst, und kommt weiter unten dran. Der Parser ist mit Absicht nachsichtig bei den Dingen, die in einer von Hand bearbeiteten Datei schiefgehen. Windows-Zeilenenden werden ohne Änderung geladen, und Leerzeichen am Anfang und Ende jeder Zeile werden entfernt. Ein Cue, dessen Zeitstempel sich nicht lesen lassen, wird übersprungen, und der Rest der Datei wird weiter geladen, was freundlich ist, bis du dich fragst, wo Zeile zwölf geblieben ist. Ein paar Dinge werden nicht unterstützt: Die Tags `` und ``, die manche Werkzeuge schreiben, werden als Text gezeigt, statt befolgt zu werden, und die Positionskoordinaten, die manche Werkzeuge an die Zeitzeile anhängen, werden ignoriert. Der Abschnitt über Untertitel in der Referenz hat die vollständige Liste. [#_load_it_and_switch_it_on] === Lade sie und schalte sie ein Drei Aufrufe, und die Reihenfolge zählt. [source,lua] ---- if srtLoad(DIR .. "subtitles.srt") then srtPosition(80) srtEnable(true) end ---- `srtLoad` liest die Datei und antwortet `true` oder `false`. Es antwortet `false` und hat dann nichts geladen, in drei Fällen: Die Datei lässt sich nicht lesen, nichts darin lässt sich verstehen, oder *es gibt keine Disc*. In allen drei Fällen gibt es eine Warnung mit dem Dateinamen auf der Konsole aus. Das ist gutes Design, und du solltest es nutzen: Eine fehlende Untertiteldatei ist es nicht wert, deswegen ein Spiel abzuschießen, aber ein Spiel stillschweigend ohne Untertitel auszuliefern ist es wert, bemerkt zu werden. Der Fall „keine Disc“ ist der, der dich erwischen wird. Die Zeitstempel müssen zu Frame-Nummern mit der Bildrate der Disc werden, und bevor eine Disc offen ist, gibt es keine Bildrate, die man benutzen könnte. Lade Untertitel also *nach* der Disc, und in einem gewöhnlichen Skript heißt das: aus dem Hauptteil des Skripts, wo auch alles andere läuft, was du auf der obersten Ebene geschrieben hast. Daraus folgt, dass eine `.srt`, die für ein Video mit einer Bildrate geschnitten wurde, bei einem Video mit einer anderen an der falschen Stelle landet. Stimm die Zeiten deiner Datei auf das Video ab, das du wirklich ausliefern wirst. `srtEnable(true)` schaltet die geladenen Cues ein. Ein Skript fängt immer mit ausgeschalteten Untertiteln an, also muss ein Spiel, das welche lädt, sie einschalten. Schaltest du sie wieder aus, verschwindet, was gerade zu sehen ist, und schaltest du sie wieder ein, erscheint, was den Frame abdeckt, auf dem die Disc gerade steht -- kein Suchen, kein Zustand, den du dir merken musst. Ob Untertitel an sind, ist eine Einstellung, keine Entscheidung, die du für den Spieler triffst. Setz sie auf deine Optionsseite aus Lektion zwanzig, speichere sie mit `saveSet` aus Lektion zwölf und gib `srtEnable`, was der Spieler zuletzt gewählt hat. [#_where_they_sit] === Wo sie sitzen [source,lua] ---- srtPosition(70) ---- `srtPosition` ist ein Prozentwert von oben nach unten im Bild, von `1` bis `95`, und der Standard ist `80`. Er gilt für den Cue, der gerade zu sehen ist, und für jeden Cue danach. Eine Zahl außerhalb des Bereichs wird ignoriert, statt auf den Bereich begrenzt zu werden, also lässt ein Tippfehler die Position genau, wo sie war, und sagt nichts. Prüf deine also nach. Achtzig Prozent liegt tief, und das mit Absicht: Der untere Teil des Bildes ist meistens der langweiligste Teil der Einstellung. Schieb sie nach oben, wenn der untere Teil *nicht* langweilig ist -- wenn dein Spiel eine Punkteanzeige am unteren Rand zeichnet oder wenn das Lightgun-Fadenkreuz des Spielers dort unten wohnt. [#_taking_one_down] === Einen wegnehmen [source,lua] ---- srtClear() ---- `srtClear` nimmt sofort vom Bildschirm, was gerade dort steht, und lässt die geladenen Cues und den eingeschalteten Zustand in Ruhe. Die Zeile, die zu sehen war, bleibt weg, bis die Disc den nächsten Cue erreicht. Benutz es, wenn eine Szene früher endet und du nicht willst, dass die letzte Dialogzeile über der nächsten Einstellung hängt. Es gibt noch einen Aufruf, der die Untertitel-Mechanik mitbenutzt, und vielleicht hast du ihn schon benutzt: [source,lua] ---- overlayBanner("Extra life!", 20) ---- `overlayBanner` zeigt etwa dreißig gezeichnete Frames lang eine kurze Nachricht über dem Bild und nimmt sie dann wieder weg. Es wird vom selben Dokument gezeichnet wie die Untertitel, also sieht ein Banner aus wie ein Untertitel, und solange eines zu sehen ist, nimmt es den Platz jedes Untertitels ein, der zurückkommt, wenn die Zeit des Banners abgelaufen ist. Sein eigenes Höhenargument ist von `srtPosition` unabhängig und steht standardmäßig auf `47`, der Mitte des Bildes. Nachrichten mit mehr als sechzig Zeichen werden komplett ignoriert, also scheint eine lange überhaupt nicht zu funktionieren. [#_subtitles_already_inside_the_video] === Untertitel, die schon im Video stecken Manche Videodateien bringen ihre eigenen Untertitelspuren mit. Wenn deine das tut, brauchst du überhaupt keine `.srt`: [source,lua] ---- if discGetSubtitleTracks() > 0 then srtLoadTrack(0) else srtLoad(DIR .. "subtitles.srt") end srtEnable(true) ---- `srtLoadTrack` nimmt eine Spurnummer von `0` bis `discGetSubtitleTracks() - 1`, und alles nach dem Laden ist gleich. `discGetSubtitleLanguage(track)` sagt dir, mit welcher Sprache eine Spur beschriftet ist, als Code aus drei Buchstaben wie `eng` oder `fra`, damit ein Spiel dem Spieler anbieten kann, was sein Video zufällig mitbringt. Zwei Warnungen. Eine Spur zu lesen geht einmal durch die ganze Videodatei, ohne Bilder zu dekodieren, also dauert eine Disc in Spielfilmlänge einen Moment: Mach das, während etwas anderes auf dem Bildschirm ist, nicht zwischen zwei Frames des Spiels. Und Untertitelspuren mit *Bildern* der Wörter statt der Wörter selbst -- die Art, die DVDs und Blu-rays benutzen -- können nicht gelesen werden. Sie werden mitgezählt, damit die Nummerierung zu dem passt, was andere Videoplayer anzeigen, aber `srtLoadTrack` antwortet für sie `false`. [#_the_whole_script_3] === Das ganze Skript Dieses hier braucht ein Video. Du hast eins: `Singe/menuBackground.mkv`, das die Engine in deinen Arbeitsordner entpackt hat, als du zum ersten Mal irgendetwas gestartet hast. Sag Singe mit `--framefile`, dass es das als Disc benutzen soll; das schaltet auch die Disc ein: ---- Singe --framefile=Singe/menuBackground.mkv subtitles ---- [source,lua] ---- dofile("Singe/Framework.singe") overlaySetResolution(discGetWidth(), discGetHeight()) local HUD_X = 16 local STEP = 5 local TOP_LIMIT = 10 local LOW_LIMIT = 95 local hudFont = fontLoad("Singe/FreeSansBold.ttf", 18) local loaded = false local showing = false local height = 80 if discGetSubtitleTracks() > 0 then loaded = srtLoadTrack(0) end if not loaded then loaded = srtLoad(DIR .. "subtitles.srt") end if loaded then srtPosition(height) showing = true srtEnable(true) end discPlay() function onInputPressed(what) if not loaded then return end if what == SWITCH_BUTTON1 then showing = not showing srtEnable(showing) if showing then overlayBanner("Subtitles on", 20) else overlayBanner("Subtitles off", 20) end elseif what == SWITCH_UP then height = math.max(TOP_LIMIT, height - STEP) srtPosition(height) elseif what == SWITCH_DOWN then height = math.min(LOW_LIMIT, height + STEP) srtPosition(height) elseif what == SWITCH_LEFT then srtClear() discSearch(0) discPlay() end end function onOverlayUpdate() overlayClear() fontSelect(hudFont) colorForeground(255, 255, 255) if loaded then if showing then fontPrint(HUD_X, 16, "Frame " .. discGetFrame() .. " subtitles ON at " .. height .. "%") else fontPrint(HUD_X, 16, "Frame " .. discGetFrame() .. " subtitles OFF") end fontPrint(HUD_X, 40, "Fire toggles them, up and down move them, left rewinds") else fontPrint(HUD_X, 16, "No subtitles loaded.") fontPrint(HUD_X, 40, "Run with --framefile and check subtitles.srt is beside the script.") end return OVERLAY_UPDATED end function onShutdown() fontUnload(hudFont) end ---- Drück Feuer zum Umschalten, hoch und runter, um die Zeilen zu verschieben, und links, um an den Anfang zurückzuspulen. Auf das Zurückspulen solltest du achten: Die Disc springt zurück auf Frame null, und gleich im nächsten Frame ist der richtige Cue da, ohne dass du irgendetwas dafür tust. [#_what_just_happened_17] === Was gerade passiert ist [source,lua] ---- if discGetSubtitleTracks() > 0 then loaded = srtLoadTrack(0) end if not loaded then loaded = srtLoad(DIR .. "subtitles.srt") end ---- Nimm die eigenen Untertitel des Videos, wenn es welche hat, fall auf die Datei neben dem Skript zurück, wenn nicht, und wisse am Ende, was davon -- wenn überhaupt etwas -- funktioniert hat. Beide Aufrufe antworten mit einem Boolean, also besteht der Rückfall aus zwei `if`-Anweisungen und keinen Sonderfällen. `menuBackground.mkv` hat keine Untertitelspur, also bekommst du in Wirklichkeit die Datei, die du getippt hast. [source,lua] ---- function onInputPressed(what) if not loaded then return end ---- `return` ohne etwas dahinter verlässt die Funktion sofort. Ohne geladene Untertitel gibt es für keine dieser Tasten etwas zu tun, und früh auszusteigen ist klarer, als den ganzen Rumpf in ein `if` zu packen. `srtEnable` aufzurufen, wenn nichts geladen ist, schadet nicht, aber Code zu schreiben, der nur Sinn ergibt, wenn er nichts tut, ist eine Angewohnheit, die man sich besser nicht zulegt. [source,lua] ---- elseif what == SWITCH_LEFT then srtClear() discSearch(0) discPlay() ---- Das `srtClear` ist nicht nötig. Die Engine zeigt im nächsten gezeichneten Frame sowieso den richtigen Cue für Frame null. Es steht da, damit du es löschen und zusehen kannst, was passiert: Die alte Zeile bleibt für einen Sekundenbruchteil stehen, während die Disc dorthin kommt, wo sie hinwill. Bei einem langsamen Sprung sieht man dieses Aufblitzen der falschen Zeile, und vorher zu löschen ist der Weg, es zu vermeiden. [#_making_text_readable_over_a_moving_picture] === Text über einem bewegten Bild lesbar machen Das System bringt die Wörter auf den Bildschirm. Ob jemand sie lesen kann, ist dein Problem, und das gilt für jede Zeile Text, die du über Video zeichnest -- eine Aufforderung aus Lektion achtzehn, eine Punktzahl, eine Auswahl an einer Verzweigung -- nicht nur für Untertitel. *Bleib weg von den Rändern.* Fernseher schneiden seit siebzig Jahren die Ränder von Bildern ab, und Arcade-Monitore sind schlimmer, nicht besser. Geh davon aus, dass die äußeren fünf Prozent des Bildes auf jeder Seite auf dem Bildschirm von irgendjemandem gar nicht zu sehen sind. Halte alles, was ein Spieler lesen muss, innerhalb davon. Darum hört `srtPosition` bei `95` auf und nicht bei `100`. *Kontrast ist keine Farbwahl, er ist eine Garantie.* Weißer Text über einem Bild ist weißer Text über dem, was das Bild gerade zufällig zeigt, und in zwei Sekunden ist das vielleicht ein Schneefeld. Der mitgelieferte Untertitelstil löst das so, wie Filmuntertitel es tun: ein dunkles Panel, das sich eng um den Text legt, plus ein Schatten von einem Pixel. Wenn du deinen eigenen Text über Video zeichnest, mach das eine oder das andere -- ein `overlayBox` dahinter oder eine dunkle Kopie des Textes, um ein Pixel versetzt. Verlass dich nicht auf das Bild. *Brich deine Zeilen selbst um.* Zwei kurze Zeilen liest man schneller als eine lange, und eine Zeile, die umbricht, wo du es nicht ausgesucht hast, bricht meistens an der falschen Stelle. Peil ungefähr vierzig Zeichen pro Zeile an und nicht mehr als zwei Zeilen auf einmal. Brich an einer natürlichen Pause um: `They were wrong about / a great many things` liest sich; `They were wrong / about a great many things` stolpert. *Lass sie lange genug stehen.* Eine angenehme Lesegeschwindigkeit liegt irgendwo bei fünfzehn Zeichen pro Sekunde, also will eine Zeile mit vierzig Zeichen ungefähr drei Sekunden. Sieh dir Cue eins in der Datei oben an: zweiunddreißig Zeichen, drei Sekunden. Wenn eine Dialogzeile kurz und schnell ist, ist es freundlicher, den Cue noch einen Moment über das Ende des Gesprochenen hinaus stehen zu lassen, als ihn wegzureißen. *Setz sie dorthin, wo die Action nicht ist.* Wenn das Wichtige in der Einstellung am unteren Bildrand ist, schieb die Untertitel für diese Szene mit `srtPosition` nach oben. Du kannst es so oft aufrufen, wie du willst, und der Spieler merkt nie, dass du es getan hast. [#_styling_them] === Sie gestalten Die Zeilen werden von einem Dokument gezeichnet, derselben Art, die du in Lektion zwanzig kennengelernt hast, und zwei Dateien in deinem Ordner `Singe` entscheiden, wie sie aussehen: * `Singe/subtitle.rml` -- das Dokument. Es enthält ein einziges Element, `#slot`, in das die Engine den aktuellen Cue oder das aktuelle Banner schreibt. * `Singe/subtitle.rcss` -- der Stil. `.cue` ist der Block, der die Höhe trägt, die `srtPosition` verlangt hat, und `.cue .text` ist der Textbalken darin: weiß, fett, auf einem dunklen Panel mit runden Ecken, das sich eng um den Text legt, mit einem Schatten. Öffne `subtitle.rcss` und lies sie -- es sind ungefähr vierzig Zeilen. Dann lass sie in Ruhe, denn eine Änderung dort hält nicht. Bei jedem Start legt Singe seine eigene Kopie von allem im Ordner `Singe` zurück, was verändert wurde, und sagt das: ---- >>> Updated File: Singe/subtitle.rcss ---- Dieser Ordner gehört der Engine. Dein Spiel bringt stattdessen seine eigene Kopie mit. Kopier beide Dateien aus `Singe` in deinen Ordner `subtitles`, neben das Skript. Dann öffne deine Kopie von `subtitle.rml` und ändere die eine Zeile in ihrem ``, die den Stil einbindet, sodass sie so lautet: [source,html] ---- ---- Ein Name in einem Dokument zählt ab dem eigenen Ordner des Dokuments, außer ein Name, der mit `Singe/` anfängt, denn der meint immer den Ordner der Engine. Der Link, so wie du ihn kopiert hast, `Singe/subtitle.rcss`, holt immer noch den Stil der Engine. Der neue holt deinen. Wenn Singe eine `subtitle.rml` neben deinem Skript findet, zeichnet es die Untertitel damit statt mit seiner eigenen. Häng `-p` an den Befehl an, und Singe beschreibt unterwegs Zeile für Zeile, was es gerade tut: ---- Singe -p --framefile=Singe/menuBackground.mkv subtitles ---- Wenn der erste Cue erscheint, ist eine dieser Zeilen diese: ---- Using the game's own subtitles/subtitle.rml ---- Jetzt ändere `font-size`, `color`, `background-color`, `border-radius` oder `font-effect` in deiner `subtitle.rcss`, speichere und starte es noch einmal. Die Engine setzt immer nur den Inhalt von `#slot` und die Höhe von `.cue`, also gehört alles andere in deinen beiden Dateien dir, und nichts, was Singe beim Start tut, rührt sie an. Wenn `-p` sagt, dass es deine verwendet, und sich das Aussehen nicht geändert hat, lies den Link noch einmal: Eine Kopie, in der noch `Singe/` steht, verwendet still und leise den Stil der Engine. Weil `overlayBanner` dasselbe Dokument benutzt, gestaltest du mit den Untertiteln auch deine Banner um, und das ist meistens, was du willst, und gut zu wissen, bevor du dich fragst, warum. Eine Grenze: Das Untertitel-Dokument ist ein Dokument, und Dokumente brauchen eine GPU. Auf einem Rechner ohne liest `srtLoad` deine Datei trotzdem und antwortet trotzdem ehrlich, und es wird nichts gezeichnet. Eine Warnung sagt das einmal. [#_try_it_21] === Probier es aus . *Füg einen vierten Cue* zu `subtitles.srt` hinzu, zwischen elf und dreizehn Sekunden. Speichere und starte es. Dann stell ihn außer der Reihe, als Cue `99`, an den Anfang der Datei, und überzeug dich, dass es trotzdem funktioniert. . *Gib einem Cue einen falschen Zeitstempel* -- schreib `00:00:05.500` mit einem Punkt statt eines Kommas -- und starte es. Sieh zu, welche Zeile verschwindet, und bemerke, dass überhaupt nichts dazu gesagt wird. Eine Datei, die lädt, ist noch keine Datei, die stimmt. . *Gestalte sie um.* Ändere in deiner eigenen `subtitle.rcss` -- der Kopie neben deinem Skript, nicht der in `Singe` -- `.cue .text` auf gelben Text auf einem völlig durchsichtigen Hintergrund und füg dann einen kräftigeren Schatten hinzu. Dann spiel es über dem Video ab und entscheide ehrlich, ob es noch lesbar ist. . *Mach die Zeitsteuerung absichtlich kaputt.* Ändere jeden Zeitstempel in der Datei so, dass er zwei Sekunden zu spät ist, und sieh zu, wie ein korrekt funktionierendes System nutzlose Untertitel liefert. Genau das macht ein neu kodiertes Video mit dir. . *Setz den Umschalter in ein Menü.* Nimm die Pausenseite aus Lektion zwanzig, füg eine Checkbox hinzu und verbinde sie mit `srtEnable`. Dann speichere die Wahl mit `saveSet`, damit sie beim nächsten Mal noch an ist. Das ist die Version, die deine Spieler wirklich wollen. [#_break_it_on_purpose_20] === Mach es absichtlich kaputt Starte das Skript ohne die Option `--framefile`: ---- Singe subtitles ---- Das Fenster geht auf, schwarz, mit deiner Ersatznachricht darauf, und die Konsole sagt: ---- Warning: No subtitles were loaded from subtitles/subtitles.srt. ---- Kein Absturz und kein Fehlerdialog, weil `srtLoad` mit `false` geantwortet und dein Skript damit umgegangen ist. Das ist der wertvollste Fehlschlag in dieser Lektion, denn er ist der, der passiert, ohne dass es jemand merkt. Es gibt keine Disc, also gibt es keine Bildrate, also gibt es nichts, in das man die Zeitstempel umrechnen könnte, also wird nichts geladen. Hättest du `srtLoad(...)` einfach so hingeschrieben und die Antwort weggeworfen, wäre dein Spiel einwandfrei gelaufen und hätte keine Untertitel gezeigt, und du hättest es erst erfahren, als es dir jemand gesagt hat. Benutz die Antwort. Jedes Mal. [#_what_you_learned_21] === Was du gelernt hast * Ein Spiel mit Sprache und ohne Untertitel ist ein Spiel, das manche Leute überhaupt nicht spielen können. * Untertitel gleich beim Schreiben einzubauen ist ein Nachmittag; sie nachzurüsten nicht. * Untertitel sind eine `.srt`-Datei: eine Nummer, zwei Zeitstempel mit `-->` dazwischen, der Text und eine Leerzeile. * `srtLoad` rechnet die Zeitstempel beim Laden in Frame-Nummern der Disc um, und darum übersteht ein Cue jeden Sprung. * Die Datei muss nach der Disc geladen werden, und das Laden antwortet `false`, wenn es nicht klappt, auch dann, wenn es keine Disc gibt. Prüf die Antwort. * `srtEnable` schaltet sie ein, und ein Skript fängt immer mit ausgeschalteten an. * `srtPosition` setzt sie als Prozentwert von oben im Bild, `1` bis `95`. * `srtClear` nimmt weg, was gerade zu sehen ist, und `overlayBanner` benutzt dasselbe Dokument. * `srtLoadTrack` liest Untertitel aus der Videodatei selbst, wenn sie welche hat. * Singe schreibt seinen Ordner `Singe` bei jedem Start neu, also kopierst du, um Untertitel umzugestalten, `subtitle.rml` und `subtitle.rcss` neben dein Skript, lässt den Link der Kopie auf dein eigenes Stylesheet zeigen und änderst dieses. * Text über einem bewegten Bild braucht einen sicheren Rand, seinen eigenen Kontrast, Zeilen, die du selbst umgebrochen hast, und Zeit, um gelesen zu werden. [#_next_time_21] === Beim nächsten Mal Das war das Letzte zum Thema Text. Teil vier geht weiter, ohne dass du ihn der Reihe nach lesen musst, und die nächste Lektion öffnet eine Tür, die zwanzig Lektionen lang zu war: `overlayGetWidth` und `overlayGetHeight` sind zwei Zahlen, und eine Szene hat drei. Lektion zweiundzwanzig ist die dritte.