Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Кодування переміщення гравця
Настав час коду! Ми будемо використовувати дії введення, які створили в останній частині, для переміщення персонажа.
Примітка
У цьому проєкті ми будемо дотримуватися правил іменування Godot.
GDScript: Класи (вузли) використовують PascalCase, змінні та функції використовують snake_case, а константи використовують ALL_CAPS (Дивіться Посібник зі стилю GDScript ).
C#: Класи, експортовані змінні та методи використовують PascalCase, приватні поля використовують _camelCase, локальні змінні та параметри використовують camelCase (Дивіться Настанови по стилю C# ). Будьте обережні, вводячи назви методів саме під час підключення сигналів.
Клацніть правою кнопкою миші вузол Player і виберіть Приєднати скрипт, щоб додати до нього новий скрипт. У спливаючому вікні встановіть Шаблон на Пустий перед натисканням кнопки Створити. Ми встановили для нього значення Empty, тому що ми хочемо написати власний код для руху гравців.

Почнемо з властивостей класу. Ми збираємося визначити швидкість руху, прискорення падіння, що являє собою гравітацію, і швидкість, яку ми будемо використовувати для переміщення персонажа.
extends CharacterBody3D
# How fast the player moves in meters per second.
@export var speed = 14
# The downward acceleration when in the air, in meters per second squared.
@export var fall_acceleration = 75
var target_velocity = Vector3.ZERO
using Godot;
public partial class Player : CharacterBody3D
{
// Don't forget to rebuild the project so the editor knows about the new export variable.
// How fast the player moves in meters per second.
[Export]
public int Speed { get; set; } = 14;
// The downward acceleration when in the air, in meters per second squared.
[Export]
public int FallAcceleration { get; set; } = 75;
private Vector3 _targetVelocity = Vector3.Zero;
}
Це загальні властивості для рухомого тіла. target_velocity — це 3D-вектор, який поєднує швидкість із напрямком. Тут ми визначаємо його як властивість, оскільки хочемо оновлювати та повторно використовувати його значення в кадрах.
Примітка
Значення сильно відрізняються від 2D-коду, тому що відстані вимірюються в метрах. У той час як в 2D, тисяча одиниць (пікселів) може відповідати тільки половині ширини вашого екрану, в 3D, це кілометр.
Давайте закодуємо рух. Ми починаємо з обчислення вектора напрямку введення за допомогою глобального об’єкта Input у _physics_process().
func _physics_process(delta):
# We create a local variable to store the input direction.
var direction = Vector3.ZERO
# We check for each move input and update the direction accordingly.
if Input.is_action_pressed("move_right"):
direction.x += 1
if Input.is_action_pressed("move_left"):
direction.x -= 1
if Input.is_action_pressed("move_back"):
# Notice how we are working with the vector's x and z axes.
# In 3D, the XZ plane is the ground plane.
direction.z += 1
if Input.is_action_pressed("move_forward"):
direction.z -= 1
public override void _PhysicsProcess(double delta)
{
// We create a local variable to store the input direction.
var direction = Vector3.Zero;
// We check for each move input and update the direction accordingly.
if (Input.IsActionPressed("move_right"))
{
direction.X += 1.0f;
}
if (Input.IsActionPressed("move_left"))
{
direction.X -= 1.0f;
}
if (Input.IsActionPressed("move_back"))
{
// Notice how we are working with the vector's X and Z axes.
// In 3D, the XZ plane is the ground plane.
direction.Z += 1.0f;
}
if (Input.IsActionPressed("move_forward"))
{
direction.Z -= 1.0f;
}
}
Тут замість _process() ми будемо робити всі обчислення за допомогою віртуальної функції _physics_process(). Він розроблений спеціально для коду, пов’язаного з фізикою, наприклад переміщення кінематичного або твердого тіла. Він оновлює вузол з використанням фіксованих інтервалів часу.
Дивись також
Щоб дізнатися більше про різницю між _process() та _physics_process() дивіться Idle і Physics Processing.
Починаємо з ініціалізації змінної direction з Vector3.ZERO. Потім ми перевіряємо, чи натискає гравець один, чи кілька, вводів move_*, і відповідно оновлюємо компоненти вектора x та z. Вони відповідають осям площини землі.
Ці чотири умови дають нам вісім можливостей і вісім можливих напрямків.
Якщо гравець натисне, скажімо, W і D одночасно, вектор матиме довжину приблизно 1,4. Але якщо вони натиснуть одну клавішу, вона матиме довжину 1. Ми хочемо, щоб довжина вектора була постійною, а не рухався швидше по діагоналі. Для цього ми можемо викликати його метод normalized().
func _physics_process(delta):
#...
if direction != Vector3.ZERO:
direction = direction.normalized()
# Setting the basis property will affect the rotation of the node.
$Pivot.basis = Basis.looking_at(direction)
public override void _PhysicsProcess(double delta)
{
// ...
if (direction != Vector3.Zero)
{
direction = direction.Normalized();
// Setting the basis property will affect the rotation of the node.
GetNode<Node3D>("Pivot").Basis = Basis.LookingAt(direction);
}
}
Тут ми нормалізуємо вектор тільки в тому випадку, якщо напрямок має довжину більше нуля, а це означає, що гравець натискає дві клавіші напрямку.
Ми обчислюжмо напрямок в якому дивиться $Pivot створюючи Basis який дивиться в напрямку direction.
Потім ми оновлюємо швидкість. Ми повинні обчислити швидкість руху землі та швидкість падіння окремо. Обов’язково поверніться на одну вкладку назад, щоб рядки були всередині функції _physics_process(), але поза умовою, яку ми щойно написали вище.
func _physics_process(delta):
#...
if direction != Vector3.ZERO:
#...
# Ground Velocity
target_velocity.x = direction.x * speed
target_velocity.z = direction.z * speed
# Vertical Velocity
if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
target_velocity.y = target_velocity.y - (fall_acceleration * delta)
# Moving the Character
velocity = target_velocity
move_and_slide()
public override void _PhysicsProcess(double delta)
{
// ...
if (direction != Vector3.Zero)
{
// ...
}
// Ground velocity
_targetVelocity.X = direction.X * Speed;
_targetVelocity.Z = direction.Z * Speed;
// Vertical velocity
if (!IsOnFloor()) // If in the air, fall towards the floor. Literally gravity
{
_targetVelocity.Y -= FallAcceleration * (float)delta;
}
// Moving the character
Velocity = _targetVelocity;
MoveAndSlide();
}
Функція CharacterBody3D.is_on_floor() повертає true, якщо тіло зіткнулося з підлогою в цьому кадрі. Ось чому ми застосовуємо силу тяжіння до Player лише тоді, коли він знаходиться в повітрі.
Для вертикальної швидкості ми віднімаємо прискорення падіння, помножене на дельта-час кожного кадру. Цей рядок коду змушуватиме нашого персонажа падати в кожному кадрі, якщо він не буде на підлозі або не стикається з нею.
Фізичний рушій може виявляти взаємодії зі стінами, підлогою або іншими тілами під час заданого кадру тільки в тому випадку, якщо відбуваються рухи і зіткнення. Ми скористаємося цією властивістю пізніше, щоб закодувати стрибок.
В останньому рядку ми викликаємо CharacterBody3D.move_and_slide(), який є потужним методом класу CharacterBody3D, що дозволяє плавно переміщувати персонажа. Якщо він вдаряється об стіну посеред руху, рушій спробує згладити це для вас. Для цього використовується значення velocity, що належить класу CharacterBody3D
І це весь код, який вам потрібен для переміщення персонажа по підлозі.
Ось повний код player.gd для довідки.
extends CharacterBody3D
# How fast the player moves in meters per second.
@export var speed = 14
# The downward acceleration when in the air, in meters per second squared.
@export var fall_acceleration = 75
var target_velocity = Vector3.ZERO
func _physics_process(delta):
var direction = Vector3.ZERO
if Input.is_action_pressed("move_right"):
direction.x += 1
if Input.is_action_pressed("move_left"):
direction.x -= 1
if Input.is_action_pressed("move_back"):
direction.z += 1
if Input.is_action_pressed("move_forward"):
direction.z -= 1
if direction != Vector3.ZERO:
direction = direction.normalized()
# Setting the basis property will affect the rotation of the node.
$Pivot.basis = Basis.looking_at(direction)
# Ground Velocity
target_velocity.x = direction.x * speed
target_velocity.z = direction.z * speed
# Vertical Velocity
if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
target_velocity.y = target_velocity.y - (fall_acceleration * delta)
# Moving the Character
velocity = target_velocity
move_and_slide()
using Godot;
public partial class Player : CharacterBody3D
{
// How fast the player moves in meters per second.
[Export]
public int Speed { get; set; } = 14;
// The downward acceleration when in the air, in meters per second squared.
[Export]
public int FallAcceleration { get; set; } = 75;
private Vector3 _targetVelocity = Vector3.Zero;
public override void _PhysicsProcess(double delta)
{
var direction = Vector3.Zero;
if (Input.IsActionPressed("move_right"))
{
direction.X += 1.0f;
}
if (Input.IsActionPressed("move_left"))
{
direction.X -= 1.0f;
}
if (Input.IsActionPressed("move_back"))
{
direction.Z += 1.0f;
}
if (Input.IsActionPressed("move_forward"))
{
direction.Z -= 1.0f;
}
if (direction != Vector3.Zero)
{
direction = direction.Normalized();
// Setting the basis property will affect the rotation of the node.
GetNode<Node3D>("Pivot").Basis = Basis.LookingAt(direction);
}
// Ground velocity
_targetVelocity.X = direction.X * Speed;
_targetVelocity.Z = direction.Z * Speed;
// Vertical velocity
if (!IsOnFloor()) // If in the air, fall towards the floor. Literally gravity
{
_targetVelocity.Y -= FallAcceleration * (float)delta;
}
// Moving the character
Velocity = _targetVelocity;
MoveAndSlide();
}
}
Тестування руху нашого гравця
Ми розмістимо наш гравець на основній сцені, щоб перевірити його. Для цього нам потрібно створити екземпляр програвача, а потім додати камеру. На відміну від 2D, у 3D ви нічого не побачите, якщо у вікні перегляду немає камери, спрямованої на щось.
Збережіть свою сцену Гравець і відкрийте Головну сцену. Щоб зробити це, ви можете натиснути вкладку Головна у верхній частині редактора.

Якщо ви закрили сцену раніше, перейдіть до док-станції FileSystem і двічі клацніть main.tscn, щоб знову відкрити її.
Щоб створити екземпляр Player, клацніть правою кнопкою миші вузол Main і виберіть Instantiate Child Scene.

У спливаючому вікні двічі клацніть player.tscn. Символ має з’явитися в центрі вікна перегляду.
Додавання камери
Далі додамо камеру. Як ми робили з Pivot нашого Player, ми збираємося створити базову установку. Ще раз клацніть правою кнопкою миші на вузлі Main і виберіть Add Child Node. Створіть новий Marker3D і назвіть його CameraPivot. Виберіть CameraPivot і додайте до нього дочірній вузол Camera3D. Ваше дерево сцени має виглядати приблизно так.

Зверніть увагу на прапорець Попередній перегляд, який з’являється у верхньому лівому куті тривимірного перегляду, коли ви вибрали Камеру. Ви можете натиснути її, щоб переглянути проекцію камери в грі.

Ми збираємося використовувати Pivot для повороту камери, ніби вона крані. Давайте спочатку розділимо 3D-вигляд, щоб мати можливість вільно переміщатися по сцені і бачити, що бачить камера.
На панелі інструментів прямо над вікном перегляду, натисніть на Перегляд, а потім 2 Панелі перегляду. Ви також можете натиснути Ctrl + 2 (Cmd + 2 на macOS).


У нижньому поданні виберіть свій Camera3D та ввімкніть попередній перегляд камери, натиснувши прапорець.

У верхньому поданні переконайтеся, що вибрано Camera3D, і перемістіть камеру навколо одиниць 19 по осі Z (перетягніть синю стрілку).

Ось де відбувається магія. Виберіть CameraPivot і поверніть його на -45 градусів навколо осі X (використовуючи червоне коло). Ви побачите, як камера рухається, ніби її прикріпили до крана.

Ви можете запустити сцену, натиснувши F6 (Cmd + R на macOS) та натискаючи клавіші зі стрілками, щоб перемістити персонажа.

Ми можемо бачити деякий порожній простір навколо персонажа завдяки перспективній проекції. У цій грі ми збираємося використовувати ортографічну проекцію, щоб краще обрамити ігрову зону та полегшити гравцеві читання відстаней.
Знову виберіть камеру і в Інспекторі встановіть для Projection значення Orthogonal і Size на 19. Персонаж тепер повинен виглядати плоскішим, а земля повинна заповнити фон.
Примітка
Коли в Godot 4 використовується ортогональна камера, якість направленних тіней залежить від значення дальності Far камери. Чим більше значення Far, тим далі камера буде бачити. Водночас більше значення Far також знижує якість тіней так як рендер тіней має покрити більшу відстань.
Якщо направлені тіні виглядають занадто розмотими після переходу на ортогональну камеру, зменшіть значення дальності Far камери на менше, таке як 100. Не зменшуйте значення Far занадто, інакше обʼєкти розташовані на відстані почнуть зникати.

Протестуйте вашу сцену, ви повинні бути в змозі рухатися в усих 8ми напрямках і не провалюватись крізь підлогу!
Зрештою, у нас є як рух гравця, так і вид. Далі ми попрацюємо над монстрами.