Zur Abwechslung würde ich gerne ein kleines Entwicklerprotokoll erstellen. Vor einiger Zeit habe ich an einem Game Jam teilgenommen und dieses Spiel – Of Mice and Bad Choices – gemacht, ein kurzes Puzzlespiel, bei dem man Käse um das Labyrinth legt, um die Mäuse herauszulocken. Es hat Spaß gemacht, aber es gab offensichtlich einige Mängel.
Einer der Hauptgründe ist, dass das Verhalten von Mäusen nicht intuitiv ist. Die Spieler erwähnten, dass sie damit rechnen würden, dass Mäuse von einem unbeliebten Käse abgestoßen werden und nicht nur erfrieren. Darüber hinaus würde die Implementierung dieser Mechanik ein viel reichhaltigeres Puzzle-Design ermöglichen.
Daher denke ich, dass dies eine gute Gelegenheit ist, automatisierte Tests in Godot durchzuführen.
Es gibt einige Test-Frameworks, die für Godot 4 verfügbar sind, aber dasjenige, das mich überzeugt hat, ist Godot Unit Test (GUT) . GUT ist ziemlich einfach:
test_
beginnen, und schreiben Sie einige Behauptungen – typische Unit-Test-Struktur.
Für diesen speziellen Fall wollte ich eine Möglichkeit haben, komplexe Szenarien zu definieren, genauso wie ich Levels für das Spiel definiere – im Engine-Editor und nicht im Code (auf diese Weise wären die Tests näher an der Realität). Daher möchte ich diese Dinge tun:
Also, packen wir das aus.
Definieren wir eine neue Klasse „MouseTestCase“. Wir möchten, dass sie Node2D
erbt (da wir sie in einer Szene platzieren möchten. Und wir möchten, dass sie eines ihrer Kinder findet (die wir selbst in einer Szene platzieren): eine Maus und seine erwartete Endposition (als Marker)
extends Node2D class_name MouseTestCase @export var steps_left = 0 # How many steps to simulate @export var done = false @onready var mouse: Mouse = $Mouse @onready var expected_position = SnapUtils.get_tile_map_position($TestMarker.position)
Jetzt können wir es in Szene setzen, und wir sind gut! Wir wissen, wo eine Maus anfängt, wir wissen, wo sie enden soll und in wie vielen Schritten.
Jetzt machen wir noch ein paar davon und erstellen eine Karte, um unser Abwehrverhalten zu testen.
Dieses Verhalten ist etwas komplex, daher möchten wir viele leicht unterschiedliche Fälle abdecken:
Die resultierende Karte, die 12 Testfälle zur Abdeckung dieses Verhaltens definiert, ist oben dargestellt (stellen Sie sich vor, wie mühsam es sein könnte, alle diese Koordinaten fest im Code zu codieren).
Es bleibt nur noch die Test-Runner-Funktion übrig. Die Funktion muss:
Der Code ist recht einfach.
func run_level_with_mouse_test_cases(map_path: String): var level = load(map_path) map.load_level(level) var cases = MouseTestCase.cast_all_cases(get_tree().get_nodes_in_group(MouseTestCase.MTC_GROUP_NAME)) while (cases.any(func(case): return not case.done)): map.move_mice() for case in cases: if not case.done: case.steps_left -= 1 if case.steps_left == 0: case.done = true assert_eq(case.get_mouse_position(), case.expected_position, case.get_parent().name+"/"+case.name)
Ich kann mir vorstellen, dass sich das weiterentwickeln wird, aber die aktuelle Implementierung ist vorerst gut genug. Ich habe die Tests geschrieben, die Mechanik implementiert und die Tests bestätigen tatsächlich, dass die Mechanik korrekt implementiert ist!
Hier habe ich eine Möglichkeit gezeigt, die Tests in Spielen anzugehen. Offensichtlich gibt es hier noch viel mehr zu verbessern, und ich ermutige die Leser, den Code und das Framework zu nehmen und an ihre Bedürfnisse anzupassen.
Der Code ist wie immer auf GitHub verfügbar: https://github.com/d-lowl/of-mice-and-bad-choices Sie können sich auch die spezifische PR ansehen, die das Testen vorstellt. Für Bonuspunkte wäre es großartig, wenn jemand sie dazu bringen könnte, in CI zu arbeiten. Prost.