Кодування переміщення гравця¶
Настав час коду! Ми будемо використовувати дії введення, які створили в останній частині, для переміщення персонажа.
Правою клавішею мишки клацніть на вузлі Player в виберіть Долучити скрипт, щоб додати новий скрипт до нього. У спливаючому вікні встановіть Шаблон Empty (порожній) і натисніть Створити.
Почнемо з властивостей класу. Ми збираємося визначити швидкість руху, прискорення падіння, що являє собою гравітацію, і швидкість, яку ми будемо використовувати для переміщення персонажа.
extends KinematicBody
# 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 velocity = Vector3.ZERO
public class Player : KinematicBody
{
// 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 = 14;
// The downward acceleration when in the air, in meters per second squared.
[Export]
public int FallAcceleration = 75;
private Vector3 _velocity = Vector3.Zero;
}
Це загальні властивості для рухомого тіла. 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(float 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 += 1f;
}
if (Input.IsActionPressed("move_left"))
{
direction.x -= 1f;
}
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 += 1f;
}
if (Input.IsActionPressed("move_forward"))
{
direction.z -= 1f;
}
}
Тут ми збираємося зробити всі розрахунки за допомогою віртуальної функції _physics_process()
. Функція _process()
дозволяє оновлювати вузол кожен кадр, _physics_process()
розроблена спеціально для коду, пов'язаного з фізикою, на кшталт переміщення кінематичного або жорсткого тіла.
Дивись також
Щоб дізнатися більше про різницю між _process()
та _physics_process()
дивіться Idle and Physics Processing.
Починаємо з ініціалізації змінної direction
з Vector3.ZERO
. Потім ми перевіряємо, чи натискає гравець один, чи кілька, вводів move_*
, і відповідно оновлюємо компоненти вектора x
та z
. Вони відповідають осям площини землі.
Ці чотири умови дають нам вісім можливостей і вісім можливих напрямків.
У випадку, якщо гравець натискає, скажімо, W і D одночасно, вектор буде мати довжину близько 1.4
. Але при натисканні однієї клавіші, довжина буде 1
. Нам треба, щоб довжина вектора була однаковою. Для цього можна скористатися його методом normalize()
.
#func _physics_process(delta):
#...
if direction != Vector3.ZERO:
direction = direction.normalized()
$Pivot.look_at(translation + direction, Vector3.UP)
public override void _PhysicsProcess(float delta)
{
// ...
if (direction != Vector3.Zero)
{
direction = direction.Normalized();
GetNode<Spatial>("Pivot").LookAt(Translation + direction, Vector3.Up);
}
}
Тут ми нормалізуємо вектор тільки в тому випадку, якщо напрямок має довжину більше нуля, а це означає, що гравець натискає дві клавіші напрямку.
В даному випадку ми також отримуємо вузол Pivot і викликаємо його метод look_at()
. Цей метод займає позицію в просторі, щоб подивитися на глобальні координати і напрямок вгору. В цьому випадку можна використовувати константу Vector3.UP
.
Примітка
Локальні координати вузла, наприклад translation
, відносні предка. Глобальні координати відносні основних осей світу, які ви можете побачити в вікні перегляду.
У 3D положення вузла містить властивість translation
. Додавши до неї direction
, ми отримаємо позицію за однин метр від Player.
Потім оновлюємо швидкість. Ми повинні розрахувати швидкість руху по землі і швидкість падіння окремо. Обов'язково поверніться на один відступ, щоб рядки були всередині функції, але за межами умови, яку ми щойно написали.
func _physics_process(delta):
#...
if direction != Vector3.ZERO:
#...
# Ground velocity
velocity.x = direction.x * speed
velocity.z = direction.z * speed
# Vertical velocity
velocity.y -= fall_acceleration * delta
# Moving the character
velocity = move_and_slide(velocity, Vector3.UP)
public override void _PhysicsProcess(float delta)
{
// ...
// Ground velocity
_velocity.x = direction.x * Speed;
_velocity.z = direction.z * Speed;
// Vertical velocity
_velocity.y -= FallAcceleration * delta;
// Moving the character
_velocity = MoveAndSlide(_velocity, Vector3.Up);
}
Для вертикальної швидкості віднімаємо прискорення падіння fall_acceleration
помножене на дельту часу delta
кожного кадру. Зверніть увагу на використання оператора -=
, який є скороченням для variable = variable - ...
.
Цей рядок коду призведе до того, що наш персонаж падатиме кожен кадр. Це може здатися дивним, якщо він вже на підлозі. Але ми повинні робити так, щоб персонаж стикався з землею в кожному кадрі.
Фізичний рушій може виявляти взаємодії зі стінами, підлогою або іншими тілами під час заданого кадру тільки в тому випадку, якщо відбуваються рухи і зіткнення. Ми скористаємося цією властивістю пізніше, щоб закодувати стрибок.
На останньому рядку ми викликаємо KinematicBody.move_and_slide()
. Це потужний метод класу KinematicBody
, який дозволяє плавно переміщати персонажа. Якщо він наполовину залізе в стіну під час руху, рушій буде намагатися згладити це для вас.
Функція приймає два параметри: нашу швидкість і напрямок вгору. Вона переміщує персонажа і повертає залишок швидкості після застосування зіткнень. При зануренні в підлогу, або стіну, функція зменшить, або скине, швидкість в цьому напрямку. У нашому випадку зберігання повернутого значення функції запобігає накопиченню персонажем вертикального імпульсу, який в іншому випадку міг би стати настільки великим, що персонаж через деякий час рухався би над землею.
І це весь код, який вам потрібен для переміщення персонажа по підлозі.
Ось повний код Player.gd
для перевірки.
extends KinematicBody
# 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 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()
$Pivot.look_at(translation + direction, Vector3.UP)
velocity.x = direction.x * speed
velocity.z = direction.z * speed
velocity.y -= fall_acceleration * delta
velocity = move_and_slide(velocity, Vector3.UP)
public class Player : KinematicBody
{
// How fast the player moves in meters per second.
[Export]
public int Speed = 14;
// The downward acceleration when in the air, in meters per second squared.
[Export]
public int FallAcceleration = 75;
private Vector3 _velocity = Vector3.Zero;
public override void _PhysicsProcess(float 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 += 1f;
}
if (Input.IsActionPressed("move_left"))
{
direction.x -= 1f;
}
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 += 1f;
}
if (Input.IsActionPressed("move_forward"))
{
direction.z -= 1f;
}
if (direction != Vector3.Zero)
{
direction = direction.Normalized();
GetNode<Spatial>("Pivot").LookAt(Translation + direction, Vector3.Up);
}
// Ground velocity
_velocity.x = direction.x * Speed;
_velocity.z = direction.z * Speed;
// Vertical velocity
_velocity.y -= FallAcceleration * delta;
// Moving the character
_velocity = MoveAndSlide(_velocity, Vector3.Up);
}
}
Тестування руху нашого гравця¶
Ми збираємося поставити нашого гравця на головну сцену Main, щоб перевірити його. Для цього нам потрібно налаштувати гравця, а потім додати камеру. На відміну від 2D, в 3D ви нічого не побачите, якщо у вашому світі немає камери.
Збережіть сцену Player та відкрийте головну сцену Main. Щоб зробити це, ви можете натиснути на вкладку Main у верхній частині редактора.
Якщо ви закрили сцену раніше, перейдіть до панелі Файлова система та двічі клацніть Main.tscn
, щоб знову відкрити її.
Щоб створити екземпляр гравця Player, клацніть правою кнопкою мишки на вузлі Main та виберіть пункт Створити екземпляр дочірньої сцени.
У спливаючому вікні двічі клацніть Player.tscn. Персонаж повинен появитися в центрі вікна перегляду.
Додавання камери¶
Тепер давайте додамо камеру. Як і в випадку з Pivot нашого гравця Player, ми збираємося створити базову конструкцію. Клацніть правою кнопкою мишки на головному вузлі Main ще раз і виберіть, на цей раз, Додати дочірній вузол. Створіть новий Position3D, назвіть його CameraPivot і додайте йому вузол Camera в якості нащадка. Ваше дерево сцени має виглядати так.
Зверніть увагу на галочку Попередній перегляд, яка відображається у верхньому лівому куті, коли вибрано камеру. Ви можете натиснути її, щоб глянути на гру через камеру.
Ми збираємося використовувати Pivot для повороту камери, ніби вона крані. Давайте спочатку розділимо 3D-вигляд, щоб мати можливість вільно переміщатися по сцені і бачити, що бачить камера.
На панелі інструментів прямо над вікном перегляду, натисніть на Перегляд, а потім 2 Панелі перегляду. Ви також можете натиснути Ctrl + 2 (Cmd + 2 на macOS).
У нижньому вікні виберіть камеру та увімкніть попередній перегляд камери, поставивши галочку.
Зображення
У верхньому вікні перемістіть камеру на 19
одиниць по осі Z (синій).
Here's where the magic happens. Select the CameraPivot and rotate it -45
degrees around the X axis (using the red circle). You'll see the camera move as
if it was attached to a crane.
Ви можете запустити сцену, натиснувши F6 та скористатися клавішами зі стрілками для переміщення персонажа.
Ми можемо бачити деякий порожній простір навколо персонажа завдяки перспективній проекції. У цій грі ми збираємося використовувати ортографічну проекцію, щоб краще обрамити ігрову зону та полегшити гравцеві читання відстаней.
Знову виберіть камеру і в Інспекторі встановіть для Projection значення Orthogonal і Size на 19
. Персонаж тепер повинен виглядати плоскішим, а земля повинна заповнити фон.
При цьому у нас є як рух гравця, так і вид на місце. Далі ми будемо працювати над монстрами.