translatedTitles.Article
DG
Dmitriy Gorin
11/17/25

Godot. Урок 1. Введение. Узлы и сцены. Базовое перемещение и камера

С вами

Дмитрий Горин, ментор по Godot

- Разработчик графических подсистем в АТОМ и инди-разработчик игр

- Участвую экспертом в мероприятиях и хакатонах, посвященных разработке игр

- Веду образовательную деятельность при университете РГРТУ им. В.Ф. Уткина (группа ВК)


Рад, что вы приступаете к разработке прототипа шутера на интенсиве Практик•ON. Удачи вам на этом пути! Вступить в чат Практик•ON для прохождения интенсива можно по ссылке: https://t.me/+gthG03srlcFlMzky. Вопросы по движку и программе можно задавать в соответствующем топике Godot чата Практик•ON.

Тема обозначена в названии статьи, так что приступаем.



Предисловие

Godot представляет собой легковесный, не требующий установки игровой движок с возможностью использовать не только встроенный Python-подобный GDscript, но и C#. Проект на Godot представляет из себя совокупность «сцен», кода, ресурсов (таких как спрайты, модели, звуки и т. п.) и файлы импорта для этих ресурсов. По этой причине проекты занимают мало места и работает всё крайне быстро на довольно слабом железе. Скачать актуальную версию движка можно по ссылке. На момент написания этого урока последней версией является 4.5.


Запуск

После скачивания и распаковки архива можно приступать к работе. Открываете exe файл движка, и перед вами предстает менеджер проектов (рис. 1).


Рис. 1. Менеджер проектов.


По умолчанию проектов, конечно же, нет. Можно попробовать посмотреть проекты из библиотеки ассетов, но это не будет рассматриваться в рамках этого курса. Для создания своего проекта достаточно нажать на кнопку “Новый проект” и у вас откроются первоначальные настройки (рис. 2). Напишите название проекта (на английском), нажмите “Создать проект”, выберите режим отрисовки “Совместимость”. В пункте “метаданные контроля версий” выберите “Git”, если вы его знаете и используете, или “Пусто”, если не используете.


Рис. 2. Окно создания нового проекта.


Как только настройка готова, нажмите “Создать и редактировать”.


Перед вами предстает интерфейс движка с пустым проектом (рис.3).


Рис.3. Интерфейс игрового движка.


Интерфейс программы прост и не перегружен:

  1. Структура игровой сцены
  2. Окно файлов проекта
  3. Окно параметров выбранного объекта на сцене (Инспектор) и его сигналов (Узел)
  4. Переключение режима главного окна
  5. Вьюпорт
  6. Кнопки запуска/паузы/остановки проекта


Узлы и сцены

Игровая сцена и объекты на ней представляют дерево взаимосвязанных узлов, которые предоставляют отдельные функции, возможности для ваших объектов. Каждый узел на сцене можно настраивать и программировать. Для наглядности, давайте рассмотрим это на примере создания персонажа в мире будущей игры.


Для начала необходимо создать корневой узел игровой сцены. Обычно это стандартный узел – Node3D. Для его создания достаточно нажать в окне сцены “3D сцена” (рис. 4).


Рис. 4. Выбор корневого узла.


После этого у вас создастся узел Node3D и основное окно переключится в режим 3D. Можно сразу же переименовать узел в “Scene”, дважды кликнув по имени.


Зажав центральную кнопку (колесо) мыши, можно покрутить камеру вьюпорта вокруг созданной основы мира, но на этом возможности не ограничиваются! Покрутив колесо, меняется приближение камеры, а при зажатом Shift, вместо перемещения камеры вокруг, вы получите перемещения камеры влево-вправо и вверх-вниз.


Если вам и этого мало, то на зажатой правой кнопке мыши (ПКМ) вы поворачиваете камеру вьюпорта от её точки расположения, и в этот момент можно перемещаться как в игре, с помощью WASD клавиш. Если не устраивает скорость перемещения, она регулируется вращением колеса мыши.


Наигравшись с камерой вьюпорта, добавьте следующий узел, который будет корневым для персонажа – CharacterBody3D. Его переименуем в “Player”. Узел “Player” называется дочерним для “Scene”.


Как было написано ранее, каждый узел выполняет какой-то функционал, и CharacterBody3D специально существует для физических тел, управляемых пользователем. Вы увидите у узла “Player” предупреждение, что требуется дополнительный узел (рис. 5).


Рис. 5. Предупреждение узла.


Решение описано в предупреждении – добавляем CollisionShape3D как дочерний узел для “Player”. С помощью него мы можем задать форму тела будущего героя. У этого узла также будет предупреждение, что форма не задана. Выберите узел CapsuleShape3D и справа, в “Инспекторе” задайте прямоугольную форму CapsuleShape3D (рис. 6).


Рис. 6. Создание формы у героя.


Предупреждение пропадет. При повторном нажатии на уже созданный ресурс формы, появятся дополнительно параметры уже самой формы (рис. 7).


Рис. 7. Редактирование формы.

Капсула считается стандартом для коллизии персонажей в современных игровых движках:

  • Неплохо подходит к форме человека;
  • Удобное поведение при движении на стыках, наклонных поверхностях;
  • Вычисляется быстро.


Теперь можно дать и некоторую внешность нашему персонажу, добавив “Player” дочерний узел MeshInstance3D. Пока пусть вид будет соответствовать форме: обычная капсула. В инспекторе у MeshInstance3D можете создать меш – полигональную сетку, описывающую 3D модель (рис. 8).


Рис. 8. Создание меша для модели.


Чтобы положение нашего персонажа считалось относительно не центра капсулы, а с низа, условных ног, стоит перенести повыше CollisionShape3D и MeshInstance3D. Если вы не меняли настройки капсул в них, то поднять на 1 вверх, т.е. потянуть по оси Y за зеленую стрелочку вверх. Можно выделить одновременно 2 объекта, зажав Ctrl, и перетащить их. Для привязки к сетке при перемещении, можно зажать Ctrl для точного установленного значения, либо установите в Инспекторе свойство “Transform → Position → y” в 1.0 (аним. 1).


Аним. 1. Расположение коллизии и меша игрока.


Был сделан заметный прогресс, который не хочется бесследно потерять. Сохраните созданную игровую сцену, выбрав сверху экрана “Сцена → Сохранить сцену”, или комбинацией Ctrl + S. По умолчанию название сохраняемой сцены берется из названия корневого узла. Я назову “TestScene”. Кидаем прямо в корень папки проекта, не стесняемся. Потом там приберемся.


Для удобства выделения и перемещения нашего персонажа во вьюпорте с помощью мыши, его можно сгруппировать. Для этого выберите узел, чьи дочерние узлы будут сгруппированы (у нас это “Player”) и нажмите на иконку “сгруппировать” (рис. 9). У узла “Player” появится иконка.


Рис. 9. Группировка узлов.


Пусть наш 3D мир и будет крутиться вокруг игрока, но не состоять же только из него? Было бы неплохо добавить на игровую сцену другие объекты, и по хорошему, которые тоже имеют коллизию. Предлагаю добавить пол и кубик на этом полу, которые будут представлять из себя StaticBody3D. По отработанному на игроке сценарии собираем 2 объекта этого типа: “Wall” и “Cube” (рис. 10). Нет смысла делать полноценный огромный мир на этом этапе.


Рис. 10. Простая сцена с 3-мя объектами.


Данная сцена уже выглядит достаточно интересно, чтобы её запустить. Но не тут-то было…


Камера, свет, World Environment

Если вы запустите вашу заготовленную сцену, то увидите лишь серую пустоту. Чтобы увидеть прекрасный и жизнерадостный мир, требуется транслятор 3D мира на наши плоские экраны – Camera3D!


Можно добавить камеру на сцену, закрепив её на игрока. В инспекторе вы увидите, что в эту камеру видно. Поставим её на уровне глаз, повыше, и поставим куб в области видимости камеры (рис. 11).


Рис. 11. Установка камеры на персонажа.


Не торопитесь запускать. Пусть вам и показывают солнечный погожий день во вьюпорте и камере – это всё демоверсия предпросмотр. Если отключите кнопки предпросмотра окружения и света, увидите реальную картину (рис. 12).


Рис. 12. Выключенный предпросмотр освещения и окружения.


Добавьте собственное окружение WorldEnvironment на сцену и настройте фоновые облака (рис. 13).


Рис. 13. Стандартный сгенерированный фон неба.

Добавьте направленный свет DirectionalLight3D. Этот тип освещения светит на весь мир и в одном направлении, которое рекомендую вам настроить инструментами вращения во вьюпорте. Можете и переместить положение этого узла, но это ни на что не влияет в отображении (рис. 14). Не забудьте включить тени в Инспекторе в “Light3D → Shadows”.


Рис. 14. Настроенный свет и освещение.


Запустите проект, нажав на соответствующую кнопку в правой верхней части интерфейса. Если это будет первый запуск, вам предложат выбрать, какую сцену запустить. Выбираете текущую, и произойдет запуск проекта. Наконец, свет отражается от наших объектов и попадает прямо к нам в камеру (рис. 15).


Рис. 15. Запущенная игровая сцена


Теперь, когда сцена настроена, самое время переходить к её программированию!


Скрипты

Для программирования на движке Godot можно использовать GDScript, C# и С++, но самый надежный вариант - использовать GDScript, являющийся языком программирования по умолчанию и разработанным специально для этого движка. Этот язык использует синтаксис, похожий на Python, но со своими особенностями. Для ознакомления с основами языка, особенно если вы программировали ранее на каком-либо из языков, рекомендую посмотреть основы языка GDScript из документации. Не обязательно знать всё из здесь перечисленного. Для начала будет достаточно знать: переменные, константы, условия, циклы и функции. Будет так же не лишним ознакомиться с руководством по стилю кода, принятому в GDScript.


Для демонстрации программирования на движке, сделаем управление по клавишам для нашего персонажа и движение камеры мышью. Для этого выберем наш узел “Player” на сцене и “ПКМ → Прикрепить скрипт”. Появится окно создания скрипта (рис. 16).

Рис. 16. Окно создание скрипта


  • Язык GDScript остается по умолчанию. Не трогать.
  • Наследует CharacterBody3D. Означает доступ к функциям, разработанным для этого узла. Не трогать.
  • Имя класса указывается, когда будете разрабатывать свои собственные классы. Не трогать.
  • Шаблон - подготовленный разработчиками движка код. В данном случае код для управления персонажа в платформере нам пригодится, хоть и потребует доработок.
  • Встроенный скрипт: свойство скрипта, которое делает его закрепленным за единственным узлом. Оставить выключенным.
  • Путь - без комментариев. Оставить с названием по умолчанию (берется из названия программируемого узла, но в стиле snake_case вместо PascalCase).

Нажимаете создать, и на основном окне движка вы перемещаетесь во вкладку “Scripts” с частично написанным кодом:

Pythonextends CharacterBody3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5


func _physics_process(delta: float) -> void:
 # Add the gravity.
 if not is_on_floor():
  velocity += get_gravity() * delta
 # Handle jump.
 if Input.is_action_just_pressed("ui_accept") and is_on_floor():
  velocity.y = JUMP_VELOCITY
 # Get the input direction and handle the movement/deceleration.
 # As good practice, you should replace UI actions with custom gameplay actions.
 var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
 var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
 if direction:
  velocity.x = direction.x * SPEED
  velocity.z = direction.z * SPEED
 else:
  velocity.x = move_toward(velocity.x, 0, SPEED)
  velocity.z = move_toward(velocity.z, 0, SPEED)
 move_and_slide()

Пояснения к коду:

  • Слово extends означает, возможности какого узла мы будем использовать в данном коде.
  • Константа SPEED хранит значение скорости перемещения по 3D миру в секунду, JUMP_VELOCITY – силу прыжка.
  • Функция _physics_process(delta) – функция, вызываемая каждый кадр физики. В отличии от простого _process(delta), которая вызывается каждый отрисованный кадр.
  • velocity – внутренняя переменная CharacterBody3D, которая отвечает за вектор скорости персонажа в трех осях, поэтому это переменная-вектор, состоящая из 3х переменных: x, y и z.
  • Класс Input – отвечает за ввод в программе.
  • Функция is_action_pressed() возвращает факт нажатия «действия» (о действиях будет описано ниже). Является частью класса Input. Вариант с just для регистрации факта нажатия, но не удержания действия.
  • В созданную переменную direction, если простыми словами, помножили направление объекта на направление, которое вы задали клавишами, и привели его к единичной длине для равной скорости перемещения в любую сторону.
  • Функция move_and_slide() – это функция CharacterBody3D, которая перемещает объект по игровому миру, учитывая столкновения и скольжения c другими телами. Перемещение зависит от значения, что мы установим в переменную velocity.

При написании кода учитывайте, что отступы крайне важны и сделаны исключительно с помощью табуляции.


Кода выше достаточно для перемещения героя, но не для управления камерой. Вы можете запустить проект, но без привычного управления мышью крайне неудобно. Исправим это, добавив между константами и функцией следующий код:

Pythonconst MOUSE_SENSITIVITY = 0.002
@onready var camera: Camera3D = $Camera3D


func _ready() -> void:
 Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)


func _unhandled_input(event: InputEvent) -> void:
 if event is InputEventMouseMotion:
  rotate_y(-event.relative.x * MOUSE_SENSITIVITY)
  camera.rotation.x = clamp(camera.rotation.x - event.relative.y * MOUSE_SENSITIVITY, -PI / 2, PI / 2)

Пояснения к коду:

  • Добавили onready переменную, которая будет хранить ссылку на узел камеры, для быстрого и удобного доступа к её функциям и свойствам. Ключевое слово onready указывает на то, что значение в эту переменную будет записано в момент, когда узел окажется на сцене и будет таким образом “готов” для работы.
  • Функция _ready() вызывается движком и позволяет как и в предыдущем случае добавлять команды в момент, когда узел окажется на сцене и будет готов к работе. В нем на данный момент только настроим захват мыши во время запуска приложения.
  • Функция _unhandled_input(event) вызывается движком в момент, когда любой ввод с клавиатуры или мыши не был перехвачен каким-либо ещё объектом. Для обработки именно движений мыши, сверим, что пришедшее событие event является InputEventMouseMotion. Если это так, используем смещение мыши в этот кадр из переменной relative, и применяем к повороту камеры.
  • Во время движения мыши, поворот производится по разным осям разным частям управляемого героя и разными функциями. Поворот по оси y (влево-вправо) будет проводиться для всего тела героя, поэтому достаточно вызвать rotate_y(angle), а вот поворот по оси x (вверх-вниз) будет проводиться только для камеры, а не всего персонажа. Для удобного задания рамки камере, поворот производится через прямое указание camera.rotation.x.
  • Для ограничения поворота камеры по высоте в пределах ±90 градусов (Pi/2 в радианах), используется функция clamp(value, min, max).


Благодаря этому коду вы получите привычное управление для 3D игр с видом от первого лица.


P.S. Это вас пока не коснется, но почитайте на досуге про проблемы углов Эйлера. Когда вы с ними встретитесь, решение уже будет у вас наготове.


Действия (actions)

Действия ввода (input actions) – это совокупность входных сигналов, будь это нажатие клавиши клавиатуры, мыши или даже игрового контроллера, объединенных под одним названием. Таким образом вы можете настраивать управление в вашей игре, а также менять его в процессе игры и мгновенно добавлять управление с геймпада, и не только. Для того, чтобы посмотреть список действий, необходимо перейти в меню «Проект → Настройки проекта… → Список действий → Показать встроенные действия», и перед вами окажется список, который вы можете редактировать под себя (рис. 17).


Рис. 17. Список действий


Заметьте, что название действия в функцию передается в двойных кавычках, то есть это строковый тип данных.


Если всё сделано правильно, можно сохранить код, сцену, и запустить проект, нажав на соответствующую кнопку в правой верхней части интерфейса. В нём вы сможете перемещать вашего персонажа в 4 стороны по нажатию стрелок (если не меняли “действия”), совершать прыжок и перемещение камеры мышью.

hub.comments