Кодування переміщення гравця

Настав час коду! Ми будемо використовувати дії введення, які створили в останній частині, для переміщення персонажа.

Правою клавішею мишки клацніть на вузлі Player в виберіть Долучити скрипт, щоб додати новий скрипт до нього. У спливаючому вікні встановіть Шаблон Empty (порожній) і натисніть Створити.

image0

Почнемо з властивостей класу. Ми збираємося визначити швидкість руху, прискорення падіння, що являє собою гравітацію, і швидкість, яку ми будемо використовувати для переміщення персонажа.

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

Це загальні властивості для рухомого тіла. 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

Тут ми збираємося зробити всі розрахунки за допомогою віртуальної функції _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)

Тут ми нормалізуємо вектор тільки в тому випадку, якщо напрямок має довжину більше нуля, а це означає, що гравець натискає дві клавіші напрямку.

В даному випадку ми також отримуємо вузол 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)

Для вертикальної швидкості віднімаємо прискорення падіння 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)

Тестування руху нашого гравця

Ми збираємося поставити нашого гравця на головну сцену Main, щоб перевірити його. Для цього нам потрібно налаштувати гравця, а потім додати камеру. На відміну від 2D, в 3D ви нічого не побачите, якщо у вашому світі немає камери.

Збережіть сцену Player та відкрийте головну сцену Main. Щоб зробити це, ви можете натиснути на вкладку Main у верхній частині редактора.

image1

Якщо ви закрили сцену раніше, перейдіть до панелі Файлова система та двічі клацніть Main.tscn, щоб знову відкрити її.

Щоб створити екземпляр гравця Player, клацніть правою кнопкою мишки на вузлі Main та виберіть пункт Створити екземпляр дочірньої сцени.

image2

У спливаючому вікні двічі клацніть Player.tscn. Персонаж повинен появитися в центрі вікна перегляду.

Додавання камери

Тепер давайте додамо камеру. Як і в випадку з Pivot нашого гравця Player, ми збираємося створити базову конструкцію. Клацніть правою кнопкою мишки на головному вузлі Main ще раз і виберіть, на цей раз, Додати дочірній вузол. Створіть новий Position3D, назвіть його CameraPivot і додайте йому вузол Camera в якості нащадка. Ваше дерево сцени має виглядати так.

image3

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

image4

Ми збираємося використовувати Pivot для повороту камери, ніби вона крані. Давайте спочатку розділимо 3D-вигляд, щоб мати можливість вільно переміщатися по сцені і бачити, що бачить камера.

На панелі інструментів прямо над вікном перегляду, натисніть на Перегляд, а потім 2 Панелі перегляду. Ви також можете натиснути Ctrl + 2 (Cmd + 2 на macOS).

image5

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

Зображення

У верхньому вікні перемістіть камеру на 19 одиниць по осі Z (синій).

image7

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

image8

Ви можете запустити сцену, натиснувши F6 та скористатися клавішами зі стрілками для переміщення персонажа.

image9

Ми можемо бачити деякий порожній простір навколо персонажа завдяки перспективній проекції. У цій грі ми збираємося використовувати ортографічну проекцію, щоб краще обрамити ігрову зону та полегшити гравцеві читання відстаней.

Знову виберіть камеру і в Інспекторі встановіть для Projection значення Orthogonal і Size на 19. Персонаж тепер повинен виглядати більш плоским, а земля повинна заповнити фон.

image10

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