523 lines
24 KiB
Text
523 lines
24 KiB
Text
[#_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 `<i>` und `<b>`,
|
|
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 `<head>`, die den Stil einbindet, sodass sie so
|
|
lautet:
|
|
|
|
[source,html]
|
|
----
|
|
<link type="text/rcss" href="subtitle.rcss"/>
|
|
----
|
|
|
|
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.
|