Нажми — и увидишь, что делает команда
- ⏱ Сколько займёт
- 30–50 минут, 5 шагов. Каждый шаг заканчивается результатом на экране.
- 💻 Что нужно
- Компьютер с Windows или macOS. Понадобится Godot — бесплатный, поставим на первом шаге. С телефона этот квест не проходится.
- ⏸ Если прервёшься
- Ничего не потеряется: прогресс сохраняется на этом устройстве. Вернёшься — продолжишь с того же шага.
- 🎓 Чему научишься
- ловушки · патруль врага · переход между уровнями
Из уровня можно вылететь
Люк открылся, но на палубе не легче: крабы. Панцирные, злые, ходят туда-сюда по своим тропам.
«Они не думают, — говорит Клык. — Они ходят по кругу. Кто не заметил тропу — тот покормил краба».
Пройти мимо можно. Нужно только выучить их маршрут — и уйти на нижнюю палубу, пока они на другом конце.
Это записано в самом Godot — движок посчитал физику и нарисовал каждый кадр. К концу квеста такая же игра будет запускаться у тебя.
Читать текстом
До сих пор в твоём платформере нельзя было ошибиться. Сегодня это меняется.
Сегодня добавим три вещи: шипы, которые перезапускают уровень, врага с маршрутом патруля и портал на второй уровень. Три Area2D — и уровень оживает.
extends Area2D func _on_body_entered(body: Node2D) -> void: if body.name == "Player": get_tree().reload_current_scene() # уровень начинается заново
if body.name == "Player" обязательна: без неё уровень будет перезапускаться от касания монеты, врага и вообще чего угодно. get_tree() — «дай мне всё дерево игры».Повесь этот скрипт на шипы — и обязательно подключи сигнал body_entered: выдели шипы → вкладка «Узел» (рядом с Инспектором) → двойной клик по body_entered → «Подключить», как у монеты в третьем квесте. Без этого функция не вызовется ни разу.
Форму столкновения делай чуть меньше картинки — иначе игрок будет умирать, пролетая рядом, и злиться.
Враг, который ходит сам
extends CharacterBody2D @export var min_x: float = 0.0 # левая граница патруля @export var max_x: float = 0.0 # правая граница патруля @export var speed: float = 50.0 # скорость врага var direction: String = "left" func _physics_process(delta: float) -> void: if direction == "left": velocity.x = -speed if position.x <= min_x: direction = "right" else: velocity.x = speed if position.x >= max_x: direction = "left" move_and_slide()
Границы min_x и max_x выставь прямо в Инспекторе: посмотри координату X врага в крайних точках маршрута и впиши.
position.x <= min_x развернёт его вправо, но тут же сработает position.x >= max_x — и он развернётся обратно. Так и будет дёргаться на месте.HitZone типа Area2D и повесь на него тот же spike.gd — не забыв подключить сигнал body_entered и у этой зоны тоже: сигналы подключаются каждому узлу отдельно. Один скрипт работает и на шипах, и на враге. Это нормальная практика: хороший код пишут один раз и вешают на десять объектов.Портал на второй уровень
extends Area2D @export_file("*.tscn") var level_path: String = "res://scenes/level_2.tscn" func _on_body_entered(body: Node2D) -> void: if body.name == "Player": get_tree().change_scene_to_file(level_path)
@export_file даёт в Инспекторе кнопку выбора файла — не придётся вручную писать путь и ошибаться в букве. change_scene_to_file закрывает текущий уровень и открывает новый.Скрипт вешается на портал-Area2D — и снова подключи сигнал body_entered во вкладке «Узел».
Второй уровень делается копией первого: Сцена → Сохранить сцену как… → путь res://scenes/level_2.tscn (папка scenes у тебя уже есть с первого квеста). Переставь в копии платформы — вот и второй уровень.
Этот скрипт работает неправильно. Уровень перезапускается сам собой, даже когда игрок стоит на месте. Нажми на строку, в которой беда.

Враг, который ничего не соображает
Первые враги в играх не думали вообще: ходили по кругу и разворачивались у стены. В Super Mario гриб именно такой — и это любимый враг миллионов.
Секрет в том, что игрок должен понять правило за три секунды. Понял — чувствует себя умным. Не понял — считает игру нечестной.
Поэтому в хороших играх сложные враги появляются позже, а первый всегда предсказуем.
Разгон вместо рывка
@export var speed: float = 200.0 # максимальная скорость @export var accel: float = 1000.0 # разгон (новая переменная) func _physics_process(delta: float) -> void: if not is_on_floor(): velocity.y += gravity * delta var x_input := Input.get_axis("ui_left", "ui_right") if x_input != 0.0: # ← новое velocity.x += x_input * accel * delta # ← новое velocity.x = clamp(velocity.x, -speed, speed) # ← новое else: # ← новое velocity.x = move_toward(velocity.x, 0, accel * delta) if is_on_floor() and Input.is_action_just_pressed("ui_accept"): velocity.y = jump_force move_and_slide()
velocity.x — гравитация, прыжок и move_and_slide() из второго квеста остаются на своих местах. Сотрёшь их — герой перестанет падать и прыгать. clamp не даёт скорости вырасти выше максимума, move_toward плавно гасит её, когда клавишу отпустили.Здесь вся функция целиком — сверь со своей и замени только середину. Разница чувствуется сразу: игра перестаёт быть «дёрганой». Именно эти строки отличают приятное управление от деревянного.
Собери опасный уровень

- 1Дерево — сюда встанут шипы, краб и порталПКМ по Level → Area2D: так рождаются и шипы, и портал — сквозь них проходят, а они это замечают. Краб другой: CharacterBody2D, он ходит по коду. Давай узлам понятные имена — Spikes, Enemy, Portal: через десяток «Node2D3» ты сам не вспомнишь, где что.
- 2Свиток — прикрепить скриптОдин и тот же spike.gd вешают и на шипы, и на зону HitZone внутри краба: хороший код пишут один раз, а вешают на десять объектов. На кадре свиток с красным крестиком, потому что у выделенного Player скрипт уже есть; на узле без скрипта на свитке будет зелёный плюс.
- 3Линейки вьюпорта — отсюда берут маршрутЧисла 0, 500, 1000 сверху и слева — это координаты сцены. Поставь краба в левый конец тропы и посмотри его Position X — получишь min_x; перетащи в правый конец — max_x. Position лежит ниже видимого края Инспектора, в разделе Transform: крути колесом прямо внутри панели.
- 4Поля из @export — ручки твоего крабаНа кадре это Speed и Jump Force героя. У краба на этом же месте появятся min_x, max_x и speed: левая граница тропы, правая и скорость шага. Оставишь нули — краб стартует влево и уползёт с карты навсегда, потому что условие «дошёл до границы» не сработает уже никогда.
- 5Запуск — проверь все три опасностиТреугольник или F5. По очереди: краб доходит до края тропы и разворачивается, шипы перезапускают уровень, портал открывает второй. Уровень перезапускается сам собой, хотя ты стоишь на месте, — значит, в spike.gd нет проверки if body.name == "Player", и шипы срабатывают от монеты или от самого краба.
Это настоящий экран Godot 4.7 на русском — такой же откроется у тебя. Слева панель «Сцена» с деревом узлов и «Файловая система», в центре 2D-вьюпорт с линейками, справа «Инспектор». Нажимай на кружки по порядку: метка покажет место, строка ниже объяснит, зачем оно. Отмечай пройденное — прогресс сохранится.
Читать текстом
Задание Гильдии: сделай врага, который патрулирует по вертикали — летающего. Подсказка: поменяй в коде velocity.x на velocity.y, position.x на position.y. Гравитацию убирать не надо — в enemy.gd её и не было, поэтому летающий краб просто зависнет там, куда ты его поставишь.
Поднимай флаг.