Para uma mudança de ritmo, gostaria de fazer um pequeno registro de desenvolvimento. Há algum tempo, participei de um game jam e fiz este jogo – Of Mice and Bad Choices – um pequeno jogo de quebra-cabeça, onde você coloca queijo ao redor do labirinto para atrair os ratos. Foi divertido, mas evidentemente houve algumas deficiências.
Um dos principais é que o comportamento dos ratos não é intuitivo. Os jogadores mencionaram que esperariam que os ratos fossem repelidos por um queijo desagradável, e não apenas congelados. Além disso, a implementação dessa mecânica permitiria um design de quebra-cabeça muito mais rico.
Então, acho que esta é uma boa oportunidade de como os testes automatizados podem ser feitos no Godot.
Existem algumas estruturas de teste disponíveis para Godot 4, mas a que me chamou a atenção é o Godot Unit Test (GUT) . GUT é bem simples:
test_
e escrever algumas afirmações – estrutura típica de teste unitário.
Para este caso em particular, queria ter uma forma de definir cenários complexos, da mesma forma que defino os níveis do jogo – no editor do motor e não no código (desta forma, os testes ficariam mais próximos da realidade). Portanto, quero fazer estas coisas:
Então, vamos desembrulhar isso.
Vamos definir uma nova classe `MouseTestCase.` Queremos que ele herde Node2D
(como queremos colocá-lo em uma cena. E queremos que ele encontre um de seus filhos (que nós mesmos colocaremos em uma cena): um mouse e sua posição final esperada (como marcador)
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)
Agora podemos colocar isso em cena e estamos bem! Sabemos onde um mouse começa, sabemos onde ele deve terminar e em quantos passos.
Agora, vamos fazer mais deles e fazer um mapa para testar nosso comportamento repelente.
Este comportamento é um tanto complexo, portanto, queremos cobrir muitos casos ligeiramente diferentes:
O mapa resultante que define 12 casos de teste para cobrir esse comportamento é mostrado acima (imagine como seria tedioso codificar todas essas coordenadas no código).
A única coisa que resta a fazer é a função do executor de testes. A função precisa:
O código é bastante simples.
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)
Imagino que isso irá evoluir, mas a implementação atual é boa o suficiente por enquanto. Eu escrevi os testes, implementei a mecânica e os testes realmente confirmaram que a mecânica foi implementada corretamente!
Aqui, mostrei uma maneira de abordar os testes em jogos. Obviamente, há muito mais coisas para melhorar aqui, e encorajo os leitores a pegar o código e a estrutura e adaptá-los às suas necessidades.
Como sempre, o código está disponível no GitHub: https://github.com/d-lowl/of-mice-and-bad-choices Você também pode dar uma olhada no PR específico que apresenta os testes. Para ganhar pontos extras, se alguém puder fazê-los trabalhar na CI, isso seria brilhante. Saúde.