Условия победы и поражения

Игра становится игрой, когда у неё есть цель и проигрыш: победить можно, проиграть тоже.

Суть: каждый кадр или по событию проверяй условия конца — здоровье на нуле значит поражение, цель достигнута значит победа.

Без условий конца игрок не понимает, к чему стремиться. Нужны два момента: победа (собрал все монеты, дошёл до выхода, набрал очки) и поражение (здоровье кончилось, упал в пропасть, вышло время). Когда условие срабатывает, игра останавливается и показывает соответствующий экран: «Победа!» или «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), на который реагирует экран конца. Бери >= вместо ==, чтобы не проскочить порог, и всегда давай рестарт.

Проверьте себя
1. Когда лучше проверять условие проигрыша (здоровье на нуле)?
AКаждый кадр в _process
BПо событию — сразу после получения урона
CТолько при выходе из игры
DОдин раз в _ready
2. Почему для победы лучше писать coins >= goal, а не coins == goal?
AТак быстрее
BЕсли за раз пришло несколько монет, строгое равенство можно проскочить, а >= сработает надёжно
C== не существует в GDScript
DМежду ними нет разницы