Чтобы сменить темп, я хотел бы сделать небольшой журнал разработки. Некоторое время назад я участвовал в геймджеме и сделал эту игру — Of Mice and Bad Choices — короткую головоломку, в которой вы раскладываете сыр по лабиринту, чтобы выманить мышей. Было весело, но явно были некоторые недостатки.
Одним из основных является то, что поведение мышей неинтуитивно. Игроки отметили, что они ожидают, что нелюбимый сыр отпугнет мышей, а не просто заморозит их. Кроме того, реализация этой механики позволит создать гораздо более богатый дизайн головоломки.
Итак, я думаю, что это хорошая возможность провести автоматическое тестирование в Godot.
Для Godot 4 доступно несколько фреймворков тестирования, но меня привлекла Godot Unit Test (GUT) . GUT довольно прост:
test_
, и напишите несколько утверждений — типичная структура модульного теста.
В данном конкретном случае мне хотелось иметь возможность определять сложные сценарии так же, как я определяю уровни для игры – в редакторе движка, а не в коде (так тесты были бы ближе к реальности). Следовательно, я хочу сделать следующие вещи:
Итак, давайте развернем это.
Давайте определим новый класс «MouseTestCase». Мы хотим, чтобы он унаследовал Node2D
(поскольку мы хотим разместить его на сцене. И мы хотим, чтобы он нашел один из своих дочерних элементов (которые мы сами разместим на сцене): мышь и его ожидаемая конечная позиция (как Маркер)
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)
Теперь мы можем выпустить это на сцену, и у нас все хорошо! Мы знаем, где начинается мышь, знаем, где она должна оказаться и через сколько шагов.
Теперь давайте сделаем их еще несколько и составим карту, чтобы проверить наше отталкивающее поведение.
Такое поведение довольно сложное, поэтому мы хотим охватить множество несколько разных случаев:
Результирующая карта, определяющая 12 тестовых примеров для покрытия этого поведения, показана выше (представьте, насколько утомительным может быть жесткое кодирование всех этих координат в коде).
Единственное, что осталось сделать, это функцию запуска тестов. Функция должна:
Код довольно прост.
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)
Я предполагаю, что это будет развиваться, но текущая реализация на данный момент достаточно хороша. Я написал тесты, реализовал механику, и тесты фактически подтверждают, что механика реализована правильно!
Здесь я показал один из способов подхода к тестам в играх. Очевидно, что здесь есть еще много вещей, которые нужно улучшить, и я призываю читателей взять код и структуру и адаптировать их к своим потребностям.
Как всегда, код доступен на GitHub: https://github.com/d-lowl/of-mice-and-bad-choices . Вы также можете просмотреть конкретный PR , посвященный тестированию. Что касается бонусных баллов, если кто-то сможет заставить их работать в CI, это было бы великолепно. Ваше здоровье.