Условия победы и поражения
Игра становится игрой, когда у неё есть цель и проигрыш: победить можно, проиграть тоже.
Суть: каждый кадр или по событию проверяй условия конца — здоровье на нуле значит поражение, цель достигнута значит победа.
Без условий конца игрок не понимает, к чему стремиться. Нужны два момента: победа (собрал все монеты, дошёл до выхода, набрал очки) и поражение (здоровье кончилось, упал в пропасть, вышло время). Когда условие срабатывает, игра останавливается и показывает соответствующий экран: «Победа!» или «Game Over» с кнопкой рестарта.
Удобнее всего проверять эти условия там же, где живёт состояние игры. Здоровье упало до нуля — эмитим сигнал game_over. Счётчик собранных монет достиг цели — эмитим level_complete. А слушают эти сигналы экраны конца игры.
extends Node # GameState
signal game_over
signal level_complete
var health: int = 100
var coins: int = 0
var coins_to_win: int = 10
func take_damage(amount: int) -> void:
health = max(health - amount, 0)
if health == 0:
get_tree().paused = true
game_over.emit()
func collect_coin() -> void:
coins += 1
if coins >= coins_to_win:
get_tree().paused = true
level_complete.emit()Заметь get_tree().paused = true — это ставит игру на паузу, чтобы враги и таймеры замерли, пока висит экран конца.
Стоит подумать и о том, как ощущается момент конца для игрока. Сухое «Game Over» без объяснения расстраивает; гораздо лучше показать, сколько очков набрано, как далеко удалось дойти, и сразу предложить «Заново». Хорошие концовки мотивируют попробовать ещё раз, а не закрыть игру. Технически всё это — те же сигналы и экраны, но с заботой о чувствах игрока: похвалить за победу, мягко подбодрить после поражения, не заставлять кликать сто раз, чтобы начать снова. Геймдизайн — это не только правила, но и эмоции, которые они вызывают.
Как работает под капотом
Условие конца — это обычная булева проверка, которая выполняется в нужный момент: после получения урона или после сбора монеты. Когда проверка истинна, игра меняет своё «глобальное состояние» на «закончена», ставит паузу и сообщает об этом сигналом. Дальше слушатель показывает экран. По сути это маленький автомат игры: ИДЁТ → ПОБЕДА или ИДЁТ → ПОРАЖЕНИЕ.
def check_game(health, coins, coins_to_win):
if health <= 0:
return "ПОРАЖЕНИЕ"
if coins >= coins_to_win:
return "ПОБЕДА"
return "ИДЁТ"
# Сыграем сценарий
health, coins, goal = 100, 0, 5
events = [("монета", 1), ("урон", 40), ("монета", 1), ("монета", 3), ("урон", 70)]
for kind, value in events:
if kind == "монета":
coins += value
else:
health -= value
status = check_game(health, coins, goal)
print(f"HP={health:4} монет={coins} -> {status}")
if status != "ИДЁТ":
breakТа же логика на Python ▶. Каждое событие меняет здоровье или очки, после чего проверяются условия конца. Игра идёт, пока не выполнено условие победы или поражения. Поменяй порядок событий и увидишь другой исход.
Частые ошибки
Первая ошибка — проверять условие конца каждый кадр в _process, хотя достаточно проверки только при изменении (после урона, после монеты): лишняя нагрузка и риск показать экран дважды. Вторая — забыть поставить паузу: враги продолжают бегать под экраном Game Over и могут изменить состояние. Третья — не дать рестарт: игрок упёрся в Game Over и вынужден перезапускать всю программу. Четвёртая — проверять победу строгим равенством coins == goal: если за раз пришло сразу несколько монет, можно проскочить число; используй coins >= goal.
Best practices
Проверяй условия конца по событию (после изменения здоровья/очков), а не каждый кадр. Сообщай об исходе сигналом (game_over, level_complete) и показывай экран в слушателе — состояние и интерфейс остаются разделёнными. Ставь игру на паузу на экране конца. Используй >= и <= вместо строгого равенства для целей и порогов. Всегда давай кнопку рестарта.
Итоги: игре нужны условия победы и поражения. Проверяй их по событию (урон, монета), а не каждый кадр. При срабатывании ставь паузу через get_tree().paused и эмить сигнал (game_over/level_complete), на который реагирует экран конца. Бери >= вместо ==, чтобы не проскочить порог, и всегда давай рестарт.