15 KiB
title | layout | root | idx | description | redirect_from | submit_vuln | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
GUIs (Formspecs) | default | ../.. | 4.5 | Lerne, wie man GUIs mit formspecs anzeigt | /de/chapters/formspecs.html |
|
Einleitung
In diesem Kapitel werden wir lernen, wie man einen formspec erstellt und ihn dem Benutzer anzeigt. Ein formspec ist der Spezifikationscode für ein Form. In Minetest sind Forms Fenster wie das Spielerinventar und können eine eine Vielzahl von Elementen wie Beschriftungen, Schaltflächen und Felder enthalten.
Beachten Sie, dass Sie, wenn Sie keine Benutzereingaben benötigen, zum Beispiel wenn Sie nur Informationen für den Spieler bereitstellen möchten, Sie die Verwendung von Heads Up Display (HUD)-Elemente anstelle von Formularen verwenden sollten, da unerwartete Fenster das Spielgeschehen stören können.
Reale oder Legacy-Koordinaten
In älteren Versionen von Minetest waren die formspecs inkonsistent. Die Art und Weise, wie verschiedene Elemente auf unerwartete Art und Weise positioniert wurden; es war schwierig, die Platzierung der Elemente vorherzusagen und auszurichten. Minetest 5.1.0 enthält eine Funktion Koordinaten, die dieses Problem durch die Einführung eines konsistenten Koordinatensystem. Die Verwendung von realen Koordinaten wird dringend empfohlen, und deshalb dieses Kapitel ausschließlich diese verwenden.
Die Verwendung einer formspec_version von 2 oder höher aktiviert reale Koordinaten.
Anatomie eines formspecs
Elemente
Formspec ist eine domänenspezifische Sprache mit einem ungewöhnlichen Format. Sie besteht aus einer Reihe von Elementen mit der folgenden Form:
type[param1;param2]
Der Elementtyp wird deklariert und dann werden alle Parameter
in eckigen Klammern angegeben. Mehrere Elemente können miteinander verbunden werden, oder
auf mehrere Zeilen verteilt werden, etwa so:
foo[param1]bar[param1]
bo[param1]
Elemente sind Elemente wie Textfelder oder Schaltflächen oder können Metadaten sein wie wie Größe oder Hintergrund sein. Sie sollten nachschlagen in der lua_api.txt für eine Liste aller möglichen Elemente.
Header
Der Header eines formspec enthält Informationen, die zuerst erscheinen müssen. Diese umfasst die Größe des formspec, die Position, den Anker und ob das spielweite Thema angewendet werden soll.
Die Elemente im Header müssen in einer bestimmten Reihenfolge definiert werden, sonst wird ein Fehler angezeigt. Diese Reihenfolge ist im obigen Absatz angegeben und, wie immer, in der Lua-API-Referenz dokumentiert.
Die Größe wird in formspec-Slots angegeben - eine Maßeinheit, die ungefähr etwa 64 Pixel entspricht, jedoch abhängig von der Bildschirmdichte und den Skalierungs Einstellungen des Clients ist. Hier ist ein formspec mit der Größe "2,2":
formspec_version[4]
size[2,2]
Beachten Sie, dass wir die formspec-Sprachversion ausdrücklich definiert haben müssen. Ohne dies wird stattdessen das Altsystem verwendet - was die Verwendung der konsistenten Elementpositionierung und anderer neuer Funktionen verhindert.
Die Elemente position und Anker werden verwendet, um das formspec auf dem Bildschirm zu platzieren.
Die Position legt fest, wo auf dem Bildschirm das formspec sein wird, und ist standardmäßig auf
die Mitte (0,5,0,5
). Der Anker legt fest, wo auf dem formspec die Position ist,
so dass Sie das formspec mit dem Rand des Bildschirms ausrichten können. Das formspec
kann auf diese Weise links vom Bildschirm platziert werden:
formspec_version[4]
size[2,2]
position[0,0.5]
anchor[0,0.5]
Dadurch wird der Anker an den linken mittleren Rand des formspec-Feldes gesetzt, und die Position dieses Ankers auf der linken Seite des Bildschirms.
Ratespiel
Der beste Weg, etwas zu lernen, ist, etwas zu machen, also lasst uns ein Ratespiel machen. Das Prinzip ist einfach: Der Mod entscheidet sich für eine Zahl, und die Spieler errät die Zahl. Der Mod sagt dann, ob die erratene Zahl höher oder niedriger ist als die tatsächliche Zahl.
Zunächst erstellen wir eine Funktion, die den formspec-Code erzeugt. Es ist gute Praxis, dies zu tun, da es die Wiederverwendung an anderer Stelle erleichtert.
guessing = {}
function guessing.get_formspec(name)
-- TODO: Anzeige, ob die letzte Schätzung höher oder niedriger war
local text = "Ich denke an eine Zahl... Raten Sie mal!"
local formspec = {
"formspec_version[4]",
"size[6,3.476]",
"label[0.375,0.5;", minetest.formspec_escape(text), "]",
"field[0.375,1.25;5.25,0.8;nummer;Nummer;]",
"button[1.5,2.3;3,0.8;raten;Raten]"
}
-- table.concat ist schneller als String-Verkettung - `..`
return table.concat(formspec, "")
end
Im obigen Code platzieren wir ein Feld, eine Beschriftung und eine Schaltfläche. Ein Feld erlaubt die Eingabe von Text Texteingabe, und eine Schaltfläche dient zum Absenden des Forms. Sie werden feststellen, dass die Elemente sorgfältig positioniert sind, um Padding und Abstände hinzuzufügen, was später erklärt wird.
Als Nächstes wollen wir dem Spieler erlauben, den formspec anzuzeigen. Der beste Weg, dies zu tun
ist die Verwendung von show_formspec
:
function guessing.show_to(name)
minetest.show_formspec(name, "guessing:game", guessing.get_formspec(name))
end
minetest.register_chatcommand("game", {
func = function(name)
guessing.show_to(name)
end,
})
Die Funktion show_formspec
akzeptiert einen Spielernamen, den Namen der formspec und die
formspec selbst. Der formspec-Name sollte ein gültiger Itemname sein, d.h. im Format
Modname:Gegenstandsname
.
Padding und Abstände
Padding ist der Abstand zwischen dem Rand des formspec und seinem Inhalt oder zwischen nicht verwandten Elementen, dargestellt in Rot. Abstand ist der Abstand zwischen zusammenhängenden Elementen, der blau dargestellt wird.
Ein Padding von 0,375
und ein Abstand von 0,25
sind üblich.
Empfang von Formspec-Übermittlungen
Wenn show_formspec
aufgerufen wird, wird der formspec an den Client gesendet, um angezeigt zu werden.
Damit formspecs nützlich sind, müssen Informationen vom Client zum Server zurückgeschickt werden.
Die Methode dafür heißt formspec field submission, und für show_formspec
wird diese
Übermittlung über einen globalen Callback empfangen:
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "guessing:game" then
return
end
if fields.guess then
local pname = player:get_player_name()
minetest.chat_send_all(pname .. " riet " .. fields.number)
end
end)
Die in minetest.register_on_player_receive_fields
angegebene Funktion wird
jedes Mal aufgerufen, wenn ein Benutzer ein Formular absendet. Die meisten Callbacks müssen den der Funktion übergebenen Formularnamen prüfen
an die Funktion übergebenen Formularnamen überprüfen und beenden, wenn es sich nicht um das richtige Form handelt; einige
müssen jedoch möglicherweise für mehrere Formulare oder für alle Formulare funktionieren.
Der Parameter fields
der Funktion ist eine Tabelle mit den vom Benutzer übermittelten Werten
Benutzer übermittelten Werte, die durch Zeichenketten indiziert sind. Benannte Elemente erscheinen in dem Feld unter ihrem eigenen
Namen, aber nur, wenn sie für das Ereignis, das die Übermittlung verursacht hat, relevant sind.
Ein Schaltflächenelement erscheint beispielsweise nur dann in Feldern, wenn die betreffende Schaltfläche
gedrückt wurde.
{% include notice.html notice=page.submit_vuln %}
Der formspec wird also an den Client gesendet, und der Client sendet Informationen zurück. Der nächste Schritt besteht darin, den Zielwert irgendwie zu generieren und zu speichern, und die die formspec auf der Grundlage von Schätzungen zu aktualisieren. Dies geschieht mit Hilfe eines Konzepts namens "contexts".
Contexts
In vielen Fällen möchten Sie, dass minetest.show_formspec Informationen an den Callback weitergeben, die nicht an den Client gesendet werden sollen. Dies könnte beinhalten dass ein Chat-Befehl aufgerufen wurde, oder worum es in dem Dialog geht. In diesem Fall, der Zielwert, der gespeichert werden muss.
Ein Context ist eine pro-Spieler-Tabelle zum Speichern von Informationen, und die Contexts für alle Online-Spieler werden in einer dateilokalen Variablen gespeichert:
local _contexts = {}
local function get_context(name)
local context = _contexts[name] or {}
_contexts[name] = context
return context
end
minetest.register_on_leaveplayer(function(spieler)
_contexts[spieler:get_player_name()] = nil
end)
Als nächstes müssen wir den Show-Code ändern, um den Context zu aktualisieren zu aktualisieren, bevor der Formspec angezeigt wird:
function guessing.show_to(name)
local context = get_context(name)
context.target = context.target or math.random(1, 10)
local fs = guessing.get_formspec(name, context)
minetest.show_formspec(name, "guessing:game", fs)
end
Wir müssen auch den Code für die Generierung von Formularen ändern, um den Context zu verwenden:
function guessing.get_formspec(name, context)
local text
if not context.guess then
text = "I'm thinking of a number... Make a guess!"
elseif context.guess == context.target then
text = "Hurray, you got it!"
elseif context.guess > context.target then
text = "Too high!"
else
text = "Too low!"
end
Beachten Sie, dass es gute Praxis ist, wenn get_formspec
den Context nur liest und nicht
überhaupt nicht zu aktualisieren. Dies kann die Funktion einfacher machen, und auch leichter zu testen.
Und schließlich müssen wir den Handler aktualisieren, um den Context mit der Vermutung zu aktualisieren:
if fields.guess then
local name = spieler:get_player_name()
local context = get_context(name)
context.guess = tonumber(fields.number)
guessing.show_to(name)
end
Formspec-Quellen
Es gibt drei verschiedene Möglichkeiten, wie ein Formspec an den client übermittelt werden kann:
- show_formspec: Bei der oben beschriebenen Methode werden die Felder durch
register_on_player_receive_fields
empfangen. - Node Meta Formspecs: der Node enthält in seinen Metadaten eine Formularvorgabe,
und der Client zeigt es sofort an, wenn der Spieler mit der rechten Maustaste klickt. Felder werden
durch eine Methode in der Node-Definition namens
on_receive_fields
empfangen. - Player Inventory Formspecs: der formspec wird irgendwann an den Client gesendet und dann
sofort angezeigt, wenn der Spieler auf "i" drückt. Felder werden empfangen durch
register_on_player_receive_fields
empfangen.
Node Meta Formspecs
minetest.show_formspec
ist nicht die einzige Möglichkeit, einen formspec anzuzeigen; Sie können auch
formspecs zu den Metadaten eines Nodes hinzufügen. Zum Beispiel,
wird dieses bei Truhen verwendet, um ein schnelleres Öffnen zu ermöglichen -
man muss nicht darauf warten, dass der Server dem Spieler den formspec für die Truhe schickt.
minetest.register_node("meinemod:rechtsclick", {
description = "Rechtsclicke me!",
tiles = {"mymod_rechtsclick.png"},
groups = {cracky = 1},
after_place_node = function(pos, placer)
-- Diese Funktion wird ausgeführt, wenn das Kisten-Node platziert wird.
-- Der folgende Code setzt den formspec für Kiste.
-- Meta ist eine Möglichkeit, Daten in einem Node zu speichern.
local meta = minetest.get_meta(pos)
meta:set_string("formspec",
"formspec_version[4]" ..
"size[5,5]" ..
"label[1,1;Dies wird beim Rechtsklick angezeigt]" ..
"field[1,2;2,1;x;x;]")
end,
on_receive_fields = function(pos, formname, fields, spieler)
if fields.quit then
return
end
print(fields.x)
end
})
Auf diese Weise eingestellte formspecs lösen nicht denselben Callback aus. Um
Formulareingaben für meta formspecs zu erhalten, müssen Sie einen
on_receive_fields
-Eintrag bei der Registrierung des Nodes enthalten.
Diese Art von Callback wird ausgelöst, wenn Sie die Eingabetaste
in einem Feld drücken, was mit minetest.show_formspec
unmöglich ist;
allerdings kann diese Art von Formular nur durch Rechtsklick auf einen
Node. Sie kann nicht programmatisch ausgelöst werden.
Spieler Inventar Formspecs
Der formspec für das Spielerinventar wird angezeigt, wenn der Spieler auf i drückt.
Der globale Callback wird verwendet, um Ereignisse von diesem formspec zu empfangen, und der
formname ist ""
.
Es gibt eine Reihe von verschiedenen Mods, die es ermöglichen, das das Spielerinventar anzupassen. Die offiziell empfohlene Mod ist Simple Fast Inventory (sfinv), und ist in Minetest Game enthalten. Ich als Übersetzer emphele jedoch eher i3 oder unified inventory
Sie sind dran
- Erweitern Sie das Ratespiel, um die höchste Punktzahl jedes Spielers zu ermitteln, wobei die höchste Punktzahl angibt, wie viele Ratschläge nötig waren.
- Erstellen Sie einen Node namens "Inbox", in dem Benutzer ein formspec öffnen und Nachrichten hinterlassen können.
Dieser Node sollte den Namen des Placers als
owner
in der Meta speichern, und sollteshow_formspec
verwenden, um verschiedene formspecs für verschiedene Spieler anzuzeigen.