6.7. Spielemenüs
Die Gemeinsamkeit fast aller Spiele ist die Möglichkeit eigene Optionen festzulegen.
6.7.1. Control Nodes
In Godot können Menüs mit Control
Nodes erstellt werden.
Control
Nodes sind im Gegensatz zu Node2D
und Node3D
nur UI elemente, wie Buttons,
Dropdowns, Label und Container.
Besonders Container
sind wichtige UI elemente, da sie bestimmen, wo Elemente im UI angezeigt und
angeordnet werden.
Container
sind parent Nodes von andern UI elementen, die innerhalb eines Containers angelegt
werden.
Hier ein Beispiel für einen VBox Container
(vertikales Alignment) im Scene tool:
Diese Konfiguartion sieht dann im Scene Manager so aus:
Das Main Menu implementiert diese Control Node:
6.7.2. Optionen
Die Szene, in der die Konfigurationsmöglichkeit für Videooptionen erstellt wurde, wird im
folgenden options.tscn
genannt.
Diese Szene beinhaltet Control Nodes und Skripts, mit denen das Spiel angepasst werden kann.
Das Skript config.gd
beinhaltet Möglichkeiten zum laden und speichern von Optionen:
10extends Node
11
12# Savefile für Optionen
13const SAVEFILE = "user://options.save"
14
15# Dictionary mit geladenen Optionen
16var save_data = get_default_config()
17
18func load_data():
19 var config = ConfigFile.new()
20 var err = config.load(SAVEFILE)
21 if err != OK:
22 # Config file ist wahrscheinlich noch nicht erstellt
23 print("Could not read config file, defaulting.")
24 # Schreibe default config in Datei
25 save()
26 return
27
28 # Config wurde ausgelesen, nun wird das dictionary angepasst
29 for key in save_data.keys():
30 save_data[key] = config.get_value("Options", key)
31
32func save():
33 var config = ConfigFile.new()
34 for key in save_data.keys():
35 config.set_value("Options", key, save_data.get(key))
36 config.save(SAVEFILE)
37
38static func get_default_config():
39 return {
40 "window_mode": 1,
41 "vsync_on": false,
42 "display_fps": false,
43 }
Die Methode load_data
holt die Config Datei SAVEDATA
und liest es als Konfiguration ein.
Config Dateien haben INI
ähnliche formate und besitzen Sektionen und Key-Value Paare:
1[Options]
2window_mode=1
3vsync_on=false
4display_fps=false
Dieses Beispiel ist das momentan verwendete Config File, welches Videooptionen speichert.
6.7.2.1. Anwendung
Natürlich reicht das Speichern dieser Konfiguration nicht aus, um diese Optionen auch im Spiel
anwenden zu können.
In Godot gibt es den DisplayServer
, welcher Display optionen anwenden kann.
In der Methode apply_config
wird dieser genutzt, um die Konfigurationen, welche mit
der Refresh Rate oder dem Fenstermodus zu tun haben, anzuwenden:
27func apply_config():
28 match save_data["window_mode"]:
29 0: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN)
30 1: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
31 match save_data["vsync_on"]:
32 true: DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED)
33 false: DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
6.7.2.2. Verwendung in anderen Nodes
Nicht nur das Optionsmenü braucht Zugriff auf die Konfiguration.
Wenn das Spiel gestartet wird, soll das Main Menu die gespeicherten Konfigurationen anwenden.
Dafür wird das config.gd
Skript im main_menu.gd
geladen:
10func _ready():
11 var conf = preload("res://Scripts/config.gd").new()
12 conf.load_data()
13 conf.apply_config()
14 # -- snip --
Die _ready
Funktion wird aufgerufen, wenn die Node das erste Mal die Szene betritt.
Beim MainMenu ist das direkt, wenn das Spiel gestartet wird.
Die Option display_fps wurde nicht in der apply_config
Methode behandelt, da der
DisplayServer
keine Funktion besitzt, die FPS zu zeigen.
Deshalb wurde ähnlich wie im Main Menu im HUD (HUD.gd
) das Config file gelesen und ausgewertet:
1extends CanvasLayer
2
3onready var fps_label = $FPS_Label
4var save_data = {}
5
6func load_config():
7 var config = preload("res://Scripts/config.gd").new()
8 config.load_data()
9 save_data = config.save_data
10
11func _ready():
12 load_config()
13
14func _process(delta):
15 if save_data.get("display_fps", false):
16 fps_label.text = str(Engine.get_frames_per_second())
17 else:
18 fps_label.text = ""
19
20func _on_pause_unpaused():
21 load_config()
In diesem Skript wird wieder in der _ready
Methode die Konfiguration gelesen
und dann anschließend in der _process
Methode die FPS in dem Label fps_label
ausgegeben.
Die Konfiguration wird in _on_pause_unpaused
neu eingelesen, welche aufgerufen wird,
wenn das Pause Menü den aktiven Spielprozess wieder startet.
Wenn also die Konfiguration im Pause Menü angepasst wurde, wird das HUD darüber
informiert und kriegt die neuen Daten.
Hier noch ein Beispiel für das FPS Label:
(Die labels VSync: Off und VSync: ON sind nicht teil des Screenshots. Das Label rechts oben, welches die FPS anzeigt soll hier dargestellt werden. Die VSync Labels sind ausschließlich zu verdeutlichung, dass sich die FPS dem Bildschirm anpassen können, gedacht.)
6.7.3. Pause Menü
Das Pause Menü hat eine Eigenschaft, dass es die Szene pausieren kann.
In Godot ist jedes Element in einer Szene abgeleitet von Node
.
Nodes haben das Attribut “pause”, welches einen boolean Wert liefert,
ob der Prozess pausiert ist.
Bei direkter Änderung dieses Wertes wird also die Node pausiert.
By default wird für jede Child Node der pause
Wert der Parent Node vererbt, bedeutet im
Umkehrschluss, dass wenn die Root Node
pausiert wird, der ganze SceneTree
pausiert ist.
22func toggle_pause():
23 var new_pause_state = not get_tree().paused
24 # Pause state wird umgeschalten
25 get_tree().paused = new_pause_state
26 # Pause Menü soll sichtbar/unsichtbar werden
27 visible = new_pause_state
28 if visible:
29 resume_button.grab_focus()
30 else:
31 emit_signal("unpaused")
Das Problem an pausierten Elementen ist, dass Input ignoriert wird und dass das Pause Menü
ebenso Teil des SceneTree
s ist.
Unter der process
Kathegorie im Node Inspector lässt sich allerdings dieses Verhalten umstellen.
Wenn die Variable Mode
auf Always
statt Inherit
geschaltet wird, kann auch während die
Node pausiert ist noch Input entgegengenommen und verarbeitet werden.
6.7.4. Game Over
Wenn der Spieler keine Leben mehr hat, soll das Spiel zuende sein und ein Game Over Bilschirm zu sehen sein.
Der Spieler emitted bei seinem Tod das Signal killed
, welches hierfür verwendet werden kann.
Dazu muss die Game Over Szene sich an diesem Event registrieren und kann dann eigene Funktionen
ausführen.
Ähnlich wie beim Pause Menü wird auch hier der Spielfluss unterbrochen, indem die Szene pausiert
wird.
10func _on_player_killed():
11 toggle_pause()
toggle_pause
is hier keine Funktion in pause.gd
sondern in game_over.gd
, funktioniert aber
gleich: Pausiere den SceneTree und schalte auf visible.