Гид по технологиям

Добавление усилений и коллекционных предметов в Godot

7 min read Game Dev Обновлено 30 Dec 2025
Усиления и коллекции в Godot
Усиления и коллекции в Godot

Человек играет в портативную игру на Nintendo Switch

Добавление усилений и коллекционных предметов улучшает вовлечённость игрока: коллекции дают ощущение прогресса, а усиления — динамику и риск/награду. В Godot это удобно реализовать благодаря Area2D, сигналам и простому созданию таймеров.

Обзор подхода и цели статьи

Цель: дать полный набор практических шагов и шаблонов для добавления в вашу 2D-игру:

  • простых коллекционных предметов (например, монеты за 20 очков);
  • временных усилений с таймером (например, 10 секунд, во время которых игрок уничтожает врагов при контакте);
  • отображения счётчика коллекций в UI;
  • рекомендаций по архитектуре, тестам и локализации.

Ключевые понятия

  • Area2D — узел для определения области, которая вызывает события при пересечении.
  • CollisionShape2D — форма коллизии (круг, прямоугольник).
  • Сигналы — механизм Godot для уведомления об событиях (enter/exit).

Начальная сцена и движение игрока

Создайте 2D-сцену, добавьте корневой узел CharacterBody2D, вложите Sprite2D и CollisionShape2D (RectangleShape2D). Ниже — минимальный и рабочий пример скрипта для движения игрока по стрелкам, с нормализацией вектора скорости:

extends CharacterBody2D

var speed := 200

func _physics_process(delta):
    var velocity := Vector2.ZERO

    if Input.is_action_pressed('ui_right'):
        velocity.x += 1
    if Input.is_action_pressed('ui_left'):
        velocity.x -= 1
    if Input.is_action_pressed('ui_down'):
        velocity.y += 1
    if Input.is_action_pressed('ui_up'):
        velocity.y -= 1

    velocity = velocity.normalized() * speed
    move_and_collide(velocity * delta)

Подсказка: для ограничения скорости на диагонали важна нормализация. Если у вас платформер с гравитацией — используйте velocity.y с учётом гравитации и move_and_slide.

Простая сцена с игроком и врагом

Создание коллекционных предметов (монеты)

  1. Создайте новую сцену “Coin.tscn” с корнем Area2D.
  2. Добавьте Sprite2D для визуала и CollisionShape2D (CircleShape2D) для зоны сбора.
  3. Включите сигнал body_entered у Area2D и подключите его к сцене игрока или к глобальному менеджеру.

Простой скрипт обработки на уровне игрока (Player.gd):

extends CharacterBody2D

var score := 0

func _on_coin_body_entered(body):
    if body == self:
        # Удаляем монету и добавляем очки
        var coin = get_tree().get_current_scene().get_node_or_null("Coin")
        # Лучше удалять сам узел монеты через очередность удаления внутри её обработчика
        score += 20

Лучший и более надёжный вариант — подписывать обработчик сигнала в самом скрипте монеты: монета при столкновении вызывает queue_free() на себе и эмиттит сигнал или вызывает глобальный менеджер для начисления очков.

Пример скрипта “Coin.gd” для самой монеты:

extends Area2D

signal collected(by)

func _ready():
    connect("body_entered", self, "_on_body_entered")

func _on_body_entered(body):
    if body.is_in_group("player"):
        emit_signal("collected", body)
        queue_free()

А в Player.gd:

func _on_coin_collected(by):
    score += 20

Преимущество: монета сама отвечает за удаление, а начисление очков остаётся в логике игрока или менеджера очков.

Коллекционные предметы с узлами игрока и врага

Создание усилений (power-ups)

Задача: при сборе усиления на игроке активируется состояние на N секунд (в примере — 10 с). В это время контакт с врагом уничтожает врага.

Рекомендуемая схема:

  • Усиление — Area2D, при пересечении отправляет уведомление и удаляется.
  • Игрок держит флаг power_up_active и таймер.
  • В обработчике столкновения с врагом поведение зависит от флага.

Пример скрипта в Player.gd:

extends CharacterBody2D

var power_up_active := false
var power_up_time := 10.0

func _on_powerup_collected():
    power_up_active = true
    # Запускаем таймер — с ожиданием без блокировки
    await get_tree().create_timer(power_up_time).timeout
    power_up_active = false

func _on_enemy_body_entered(enemy):
    if power_up_active:
        # уничтожаем врага: лучше вызывать метод врага, а не удалять через get_parent
        if enemy.has_method("die"):
            enemy.die()
        else:
            enemy.queue_free()
    else:
        # Игрок умирает — реагируем через систему жизней или перезагрузку сцены
        queue_free()

Примечание: прямой вызов get_parent().get_node(“Enemy”).queue_free() хрупок — лучше взаимодействовать с конкретным экземпляром врага (параметр body/ enemy), либо вызвать метод менеджера врагов.

Варианты активации усиления

  • Мгновенное действие при сборе (например, однократный взрыв).
  • Временное состояние с таймером (как в примере).
  • Поддерживаемые стеки: одинаковые усиления продлевают время или усиливают эффект — решайте по дизайну.

Отображение числа собранных предметов

Создайте Label в UI (CanvasLayer) и обновляйте её при изменении счёта.

Пример Label.gd:

extends Label

func _ready():
    text = "Collectibles: 0"

В Player.gd храните ссылку на Label и обновляйте её каждый кадр или при событии изменения счета:

var ui_label: Label = null

func _ready():
    ui_label = get_tree().get_current_scene().get_node("Label")
    _update_ui()

func _update_ui():
    ui_label.text = "Collectibles: %d" % (score / 20)

func _on_coin_collected(by):
    score += 20
    _update_ui()

Совет: обновляйте UI только при изменении данных, чтобы не тратить ресурсы.

Метка счёта коллекционных предметов в сцене с игроком

Архитектурные и проектные рекомендации

  • Используйте группы (player, enemy, collectible), чтобы проще фильтровать объекты при столкновениях.
  • Делегируйте удаление объектов им самим (queue_free в теле объекта), а начисление очков — менеджеру или игроку через сигнал.
  • Явно проверяйте тип/группу объекта в сигнале body_entered: if body.is_in_group(“player”)
  • Для сложных эффектов используйте FSM (Finite State Machine) у игрока: Normal, PoweredUp, Stunned.

Дополнительные функции и идеи

  • Несколько уровней усилений: скорость I → скорость II → неуязвимость.
  • Комбинации усилений: два типа ускорителя дают уникальный эффект.
  • Ограничение по частоте: кулдаун между сборами одного типа усиления.
  • Различные типы коллекций: очки, жизни, ключи для открытия дверей.

Краткая методология разработки усилений и коллекций

  1. Прототип: реализуйте базовую монету и простое усиление на 10 секунд.
  2. Тестирование: добавьте 5–10 экземпляров на тестовой карте.
  3. Баланс: подстройте время усиления и количество очков исходя из геймплея.
  4. Полировка: звуки, эффекты частиц, подсказки в интерфейсе.
  5. Сохранение: решите, сохраняются ли сборы между сессиями.

Чек-листы по ролям

Разработчик:

  • Узлы организованы (Player, Enemy, Collectibles, Powerups)
  • Сигналы подключены через код или редактор
  • Нет прямых get_node на фиксированные имена для инстансов
  • Обработка удаления через queue_free выполнена в объекте

Дизайнер:

  • Ясная визуальная дифференциация монет и усилений
  • Описание эффектов усилений в гейм-доке
  • Подумать о звуках и анимациях при сборе

Тестировщик:

  • Сбор монет увеличивает счёт на 20
  • Усиление длится ровно 10 секунд
  • Контакт с врагом в режиме усиления уничтожает врага
  • Повторное взятие усиления продлевает/стекает (в зависимости от дизайна)

Тест-кейсы и критерии приёмки

Критерии приёмки:

  • При сборе одной монеты очки увеличиваются на 20.
  • Метка UI отражает правильное количество коллекций (score / 20).
  • Усиление включается при сборе и отключается спустя 10 секунд.
  • Контакт с врагом при активном усилении уничтожает врага; без усиления — убивает игрока.

Тест-кейсы:

  • Сбор 3 монет подряд: ожидаем score == 60 и UI показывает 3.
  • Сбор усиления и контакт с врагом в течение 10 секунд: враг удаляется.
  • Сбор усиления, ожидание 11 секунд, контакт с врагом: игрок умирает.

Альтернативные подходы и когда они уместны

Подход с сигналами и Area2D хорош для простых проектов. Если у вас сложный проект с большим количеством врагов и предметов, рассмотрите:

  • ECS (Entity Component System) архитектуру для масштабируемости.
  • Менеджеры (managers) для централизованного управления очками и состояниями.
  • Использование Signals -> Global (через singleton) для счёта и достижений.

Когда предложенный подход не подойдёт:

  • MMO/многопользователь — синхронизация состояний потребует сетевого кода.
  • Сложные физические взаимодействия — используйте RigidBody2D и зоны триггеров отдельно.

Ментальные модели и эвристики

  • “Single Responsibility”: каждый объект отвечает за свою одну задачу — монета удаляется и сообщает о сборе, игрок начисляет очки.
  • “Fail-safe”: никогда не полагайтесь на фиксированные пути get_node(“Scene/Name”) для динамических экземпляров.
  • “Design for test”: делайте таймеры и длительности настраиваемыми через экспортированные переменные.

Snippet-справочник (cheat sheet)

Создать Area2D с сигналом:

# В скрипте монеты
signal collected(by)

func _ready():
    connect("body_entered", self, "_on_body_entered")

func _on_body_entered(body):
    if body.is_in_group("player"):
        emit_signal("collected", body)
        queue_free()

Запуск таймера без явного Timer-узла:

await get_tree().create_timer(10.0).timeout

Проверка группы:

if body.is_in_group("enemy"):
    # обработка

Решение конфликтов и откат изменений (инцидентный план)

Если после добавления усилений появляются баги (например, враги массово умирают), выполните:

  1. Откатить изменённую ветку к коммиту перед добавлением усилений.
  2. Включить изменения по частям: сначала только монеты, затем одно усиление.
  3. Логировать события collision и вызовы die() для анализа.

Локализация и UX для русскоязычной аудитории

  • Текст UI: используйте gettext и .po файлы для перевода (tr(“Collectibles”))
  • Формат дат/чисел не критичен для вещей, описанных здесь, но подписывайте подсказки и сообщения понятным языком.

Полезные шаблоны структуры сцены

MainScene (Node2D)

  • Player (CharacterBody2D) [group: player]
  • Enemies (Node2D)
    • Enemy1 (CharacterBody2D) [group: enemy]
  • Collectibles (Node2D)
    • Coin1 (Area2D)
    • Coin2 (Area2D)
  • Powerups (Node2D)
    • Powerup1 (Area2D)
  • UI (CanvasLayer)
    • Label (Label)

Сводка

Добавление коллекционных предметов и усилений в Godot — это сочетание простых узлов Area2D, корректного использования сигналов и аккуратной архитектуры взаимодействия. Начните с прототипа, следуйте принципу единой ответственности, тестируйте игровые эффекты и делайте визуальную и звуковую обратную связь удовлетворяющей.

Важно

  • Проверяйте, к какому телу применяется сигнал (body) и используйте группы для фильтрации.
  • Не удаляйте узлы через get_parent().get_node на фиксированные имена — работайте с экземпляром body.

Ключевые действия

  • Реализуйте монету как Area2D, которая эмиттит collected и вызывает queue_free().
  • Реализуйте усиление как Area2D, меняющее состояние игрока и используя create_timer для времени действия.

1-строчный глоссарий

  • Area2D: область в 2D-пространстве, отслеживающая пересечения.
  • queue_free(): помечает узел для удаления в следующем кадре.
  • Signal: уведомление об событии между узлами.

Если нужно, могу подготовить готовые сцены (.tscn) и полные скрипты для копирования в ваш проект.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

High Power Mode на MacBook Pro
macOS

High Power Mode на MacBook Pro

Подключение игрового контроллера к Android
Android.

Подключение игрового контроллера к Android

Голосовые покупки на Amazon Echo — безопасность и советы
Умный дом

Голосовые покупки на Amazon Echo — безопасность и советы

Стереопара Echo: как объединить два динамика
Инструкции

Стереопара Echo: как объединить два динамика

Подключение Flask к CouchDB
Backend

Подключение Flask к CouchDB

Оцифровка CD и DVD: образы дисков и виртуальные приводы
Руководство

Оцифровка CD и DVD: образы дисков и виртуальные приводы