В мире разработки 3D игр на движке Godot Engine создание эффективного контроллера движения игрового персонажа является ключевым аспектом. В этой статье я расскажу о том, как создать свой собственный movement controller для вашей 3D игры на собственном опыте.
Для начала нажмите правой кнопкой мыши по папке 'res://' и выберите 'Create New', затем 'Scene'. Это позволит нам удобно организовать наш проект и начать работу над контроллером движения.
Учитывая, что мы разрабатываем 3D игру, в появившемся окне выберите '3D Scene' и дайте ей название 'Main'. Это будет нашей основной сценой.
Прекрасно, мы сделали первый шаг. Теперь перейдем ко второму. Нам необходимо создать еще одну сцену для удобства, но уже выбрав 'Node', а именно 'CharacterBody3D'. Назовем эту сцену 'player'. В эту сцену нам нужно добавить 'CollisionShape3D', 'MeshInstance3D', 'Node3D', а также 'Camera3D'. Тем самым создадим такую иерархию:
"Но что такое Node3D? Это голова нашего персонажа, которую мы будем использовать в будущем. Давайте назовем ее 'Head' и переместим в соответствующее место, где будет расположена голова нашего персонажа.
Теперь перейдем к выбору необходимой коллизии в CollisionShape3D. Лучшим выбором для нас будет CapsuleShape3D. Повторим ту же операцию и с MeshInstance3D:
Отлично, теперь нажмем правой кнопкой мыши по нашему CharacterBody3D (player) и выберем 'Attach Script'. В качестве шаблона выберем 'CharacterBody3D: Basic Movement'.
Мы уже можем запустить нашу игру и персонаж будет двигаться, но только по стрелочкам. Как это исправить? Наводимся на Project -> Project Settings -> Input Map и добавляем нужные нам клавиши.
Теперь немного изменим наш код:
Давайте рассмотрим наш скрипт более детально:
- В строках 3-8 мы объявляем и инициализируем переменные, которые нам понадобятся для управления движением. Это происходит в функции _ready(), которая запускается один раз при старте нашей игры. Можно это делать и вне функции, но я считаю такую инициализацию удобной. Обратите внимание, что мы указываем типы переменных, например, 'var direction: Vector3', хотя это не обязательно.
- Наши основные операции выполняются в строке 10 в функции _physics_process(delta). Эта функция вызывается постоянно в течение игры и принимает аргумент delta, который представляет собой время, прошедшее с последнего кадра. Мы будем использовать эту переменную для создания более плавного падения нашего персонажа под воздействием гравитации.
- В строках 13 мы создаём функцию move() которую будем вызывать в _physics_process(delta) для большей красоты кода.
- В строках 14-15 мы проверяем, находится ли наш персонаж на земле, используя встроенную функцию is_on_floor(). Если наш персонаж находится на земле, то никаких дополнительных действий не происходит. В противном случае, если персонаж не на земле, мы начинаем плавное падение.
- В строках 17-18 у нас реализована механика прыжка. Здесь мы проверяем, была ли нажата клавиша прыжка (обычно это клавиша 'Пробел') и находится ли наш игрок на земле. Если да, то наш персонаж выполняет прыжок вверх. Однако в моей игре прыжкок не предусмотрен, поэтому я просто удалю эти строки из скрипта.
- Наконец дошли до самого сочного. В строках 20-29 у нас реализована самая главная механика - механика передвижения:
- Для начала мы получаем вектор ввода от игрока используя Input.get_vector(). Если мы выведем его в консоль, то вывод будет такой: (1 или 0 или -1, 1 или 0 или -1).
- После этого мы определяем направления движения объекта:
transform.basis нужен нам для опредения поворта и мастабирования объекта.
Vector3(input_dir.x, 0, input_dir.y) Это вектор, представляющий направление движения от игрока. input_dir.x и input_dir.y ввод от игрока по осям X и Y соответственно. Значение по оси Z устанавливается в 0.
normalized() нужен нам для нормализации вектора, приводя его к единичной длине(=1). Это требуется для того, чтобы скорость игрока не зависила от направления движения. - Если персонаж двигается, то мы плавно увеличиваем скорость, иначе устанавливаем её на 0. Мы могли бы упростить эту строку без использования линейной интерполяции(lerp), однако я предпочитаю, чтобы скорость в нашей игре увеличивалась плавно.
- В этом участке кода мы перемещаем персонажа по осям x и z, а метод move_and_slide() применяет все наши изменения к velocity.
Отлично, мы завершили настройку передвижения. Теперь осталось добавить контроллер для мыши:
- Объявляем перменные head, camera и sensitivity, для управления камерой.
- _unhandled_input(event) будет вызываться самостоятельно, для обработки всех ранее неперехваченных вводов.
- if event is InputEventMouseMotion: - Это условие проверяет, является ли событие, которое произошло, событием движения мыши.
- head.rotate_y(-event.relative.x * sensitivity) здесь мы поворачиваем ту самую голову персонажа вокруг оси Y.
- camera.rotate_x(-event.relative.y * sensitivity) поворачиваем камеру персонажа для избежания багов.
- camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-90), deg_to_rad(90)) - Эта строка ограничивает угол обзора камеры в пределах от -90 градусов до 90 градусов, что предотвращает переворачивание камеры.
- Всё работает, но теперь нам нужно скрыть курсор. Для этого добавим следующую строку в функцию _ready(): Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
Из-за введения вращения камеры, нам следует внести изменения в скрипт передвижения:
Здесь мы добавили "head" к transform.basis, поскольку мы вращаем именно его, а не CharacterBody3D.
Отлично! Сегодня мы сделали большой шаг в разработке нашей игры, создав персонажа и настроив для него mouse и movement controller. , Следующие статьи предоставят нам возможность погрузиться в такие механики, как crouch, slide, run и stamina. Желаем вам удачи в вашем творчестве и разработке игры!