Нажми — и увидишь, что делает команда
- ⏱ Сколько займёт
- 30–50 минут, 5 шагов. Каждый шаг заканчивается результатом на экране.
- 💻 Что нужно
- Компьютер с Windows или macOS. Понадобится Godot — бесплатный, поставим на первом шаге. С телефона этот квест не проходится.
- ⏸ Если прервёшься
- Ничего не потеряется: прогресс сохраняется на этом устройстве. Вернёшься — продолжишь с того же шага.
- 🎓 Чему научишься
- оружие и пули в 3D · направление объекта · свой игровой режим
Гарпуномёт в кадре
Под водой, в двадцати метрах, стоят колонны. Между ними шевелится что-то большое.
Тео стучит по шлему: «Слушай внимательно, повторять не буду. В храме ты один. Гарпун стреляет туда, куда ты смотришь. В колчане их двадцать — промахнулся, значит одним меньше».
Дальше — твои правила. Сколько жизней, сколько врагов, чем всё кончится — решаешь ты.
Это записано в самом Godot — движок посчитал физику и нарисовал каждый кадр. К концу квеста такая же игра будет запускаться у тебя.
Читать текстом
Половину этого ты уже делал в 2D. Посмотрим, что изменилось.
Гарпуномёт — ребёнок головы, значит смотрит туда же, куда камера. Muzzle — просто пустая точка на конце ствола: гарпуны будут рождаться именно там, а не в центре героя.
Модель оружия бесплатно возьми в наборе Kenney Blaster Kit на kenney.nl.
Выстрел
extends Node3D # gun.gd — гарпуномёт: выстрел, патроны, создание пуль const BULLET := preload("res://prefabs/harpoon3d.tscn") @export var ammo: int = 10 @onready var muzzle := $Muzzle # дуло func _physics_process(_delta: float) -> void: if Input.is_action_just_pressed("shoot") and ammo > 0: shoot() func shoot() -> void: ammo -= 1 var b = BULLET.instantiate() b.global_transform = muzzle.global_transform # сначала поворот и место get_tree().current_scene.add_child(b) # и только потом — в мир
@onready — «найди этого ребёнка, когда сцена соберётся». Без него узел ещё не существует в момент запуска. global_transform копирует гарпуну положение и поворот дула — он вылетит точно туда, куда смотрит ствол.Порядок этих двух строк решает всё. В момент
add_child у гарпуна срабатывает _ready() — и он тут же считает, куда лететь. Зададим поворот позже — гарпун уже разогнался не туда и полетит в одну и ту же сторону мира, куда бы ты ни целился.Сначала заведи клавишу: Проект → Настройки проекта → «Карта действий» (Input Map) → добавь действие shoot и назначь ему левую кнопку мыши. Без этого Godot напишет «The InputMap action "shoot" doesn't exist».
Сравни с 2D-выстрелом из пятого квеста: та же схема — preload, instantiate, add_child. Добавилось только одно: гарпуну теперь важен поворот, а не только точка.
Куда летит гарпун
extends RigidBody3D # bullet.gd — гарпун: разгоняется один раз, дальше его несёт физика @export var speed: float = 30.0 @export var lifetime: float = 3.0 # через сколько секунд исчезнет func _ready() -> void: linear_velocity = -global_transform.basis.z * speed await get_tree().create_timer(lifetime).timeout queue_free()
await с queue_free() — это уже знакомая уборка из шестого квеста.Сцена гарпуна — новая сцена, сохрани как res://prefabs/harpoon3d.tscn. Корень Bullet · RigidBody3D, внутри: MeshInstance3D с CylinderMesh, CollisionShape3D и HitZone · Area3D со своей CollisionShape3D. У корня в Инспекторе поставь Gravity Scale = 0 — иначе гарпун полетит по дуге и воткнётся в пол.
Убери queue_free() — и через минуту в сцене будут тысячи пуль. Игра начнёт тормозить, а потом зависнет. Всё, что появляется во время игры, обязано уметь исчезать.

Кто придумывает правила игры
Человек, который решает «три жизни или пять», «сколько врагов» и «чем всё кончится», называется геймдизайнер.
Он почти не пишет код и не рисует. Его работа — придумать правило, а потом проверить на живых людях, что играть интересно. Это называется плейтест.
Правило простое: если тестировщик скучает — виноват не тестировщик. Значит, правило нужно менять.
Попадание и мишени
extends Area3D # hit_zone.gd — вешается на ребёнка гарпуна func _on_body_entered(body: Node3D) -> void: if body.is_in_group("targets"): body.queue_free() get_parent().queue_free()
HitZone внутри сцены гарпуна — и там же, во вкладке «Узел», подключи сигнал body_entered, иначе функция никогда не вызовется. Мишеням не забудь добавить группу targets: выдели узел → вкладка Узел → Группы.Мишень — это обязательно физическое тело: StaticBody3D (стоит намертво) или RigidBody3D (падает и катится), а внутри — MeshInstance3D и CollisionShape3D. Голый меш Area3D просто не заметит.
Поставь по миру пять-шесть мишеней: ящики, бочки, летающие шары. Стрельба по неподвижным предметам быстро надоедает — попробуй подвесить парочку и раскачать.
Охота на время

- 1Дерево героя — сюда встанет гарпуномётНа кадре у Player есть камера, а оружия нет. Добавь рядом с Camera3D узел Node3D по имени Gun — он ребёнок головы, значит смотрит туда же, куда глаза. Внутрь Gun положи модель и пустой Marker3D по имени Muzzle: это точка на конце ствола, откуда будут вылетать гарпуны.
- 2Свиток с зелёным плюсом — скрипт оружиюВыдели Gun и жми: res://scripts/gun.gd. Скриптов в квесте три, и живут они на разных узлах: gun.gd на оружии, bullet.gd на корне отдельной сцены гарпуна, hit_zone.gd на его Area3D. Один узел — один скрипт; перепутаешь хозяина — не заработает ничего.
- 3Компас осей: вперёд — это минус ZСиний шарик Z смотрит на тебя, поэтому «вперёд» у любого объекта — минус его Z. Отсюда и строка linear_velocity = -global_transform.basis.z * speed. Порядок важен: сначала гарпун копирует поворот дула, и только потом получает скорость. Сделаешь наоборот — он будет улетать в одну и ту же сторону мира, куда бы ты ни целился.
- 4Мишени расставляй по всему мируЯщики, бочки, шары — близко, далеко, высоко и за укрытием, иначе стрелять надоест через минуту. Ставь их прямо на землю, как этот холм. И помни: всё, что появляется во время игры, обязано уметь исчезать — без таймера жизни у гарпуна через минуту в сцене будут тысячи снарядов, и игра начнёт тормозить.
- 5Группы — в соседней вкладке этого докаВыдели мишень и добавь ей группу targets: вкладка «Группы» живёт в том же правом доке, что и «Сигналы» на кадре, — панель узкая, вкладки прокручиваются стрелками в её правом верхнем углу. Слово в группе и слово в коде должны совпасть буква в букву: is_in_group("targets").
Это настоящий экран Godot, вкладка 3D: слева дерево объёмной сцены, в центре мир с землёй, холмом и героем-ящиком, а на нём — гизмо из трёх стрелок. Нажимай на кружки по порядку: метка покажет место, строка ниже объяснит, зачем оно.
Читать текстом
И главное, что стоит унести с собой: ты почти ничего не учил заново. Сигналы, префабы, таймеры, группы — всё это было в платформере. Движок один, приёмы одни, меняется только измерение.
Поднимай флаг.