Анімація персонажа

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

image0

Почнемо з вступу до використання редактора анімації.

Використання редактора анімації

Рушій поставляється з інструментами для створення анімації в редакторі. Потім ви можете використовувати код для відтворення та керування нею під час виконання.

Відкрийте сцену гравця, виберіть вузол гравця та додайте вузол AnimationPlayer.

Внизу з'явиться панель Анімація.

image1

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

Давайте створимо анімацію. Натисніть Анімація -> Новий.

image2

Назвіть анімацію "float".

image3

Після створення анімації з'явиться часова шкала з числами, що представляють час у секундах.

image4

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

Для цього можна натиснути кнопку з іконкою "A+" на панелі інструментів анімації і кнопку зі стрілками.

image5

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

Зображення

Установіть тривалість анімації на 1.2 секунди у верхньому правому куті панелі.

image7

Ви повинні побачити, що сіра полоса трохи розширилася. Вона показує вам початок і кінець вашої анімації, а вертикальна синя позначка - ваш курсор часу.

image8

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

image9

Анімація плавання

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

Давайте вставимо наші перші ключі. Тут ми оживимо як переміщення, так і обертання вузла Character.

Виберіть Character і клацніть значок ключа поруч із пунктом Translation у Інспекторі. Зробіть те ж саме для "Rotation Degrees*.

image10

У редакторі з'являться дві доріжки зі значком ромбика, що представляє кожен ключовий кадр.

image11

Ви можете натиснути і перетягнути ромбики, щоб перемістити їх у часі. Перемістіть ключ переміщення на 0.2 секунди, а ключ обертання на 0.1 секунди.

image12

Перемістіть курсор часу на 0.5 секунди, клацаючи та перетягуючи по сірій шкалі часу. У Інспекторі встановіть в Translation вісь Y приблизно на 0.65 метри, а в Rotation Degrees вісь X на 8.

image13

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

image14

Примітка

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

Перемістіть курсор часу в кінець анімації на 1.2 секунди. Встановіть переміщення по осі Y на 0.35 і обертання по осі X -9 градусів. Ще раз створіть ключ для обох властивостей.

Ви можете переглянути результат, натиснувши кнопку відтворення або клавіші Shift + D. Натисніть кнопку зупинки або S, щоб зупинити відтворення.

image15

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

Ми можемо контролювати перехід між ключовими кадрами за допомогою кривих пом'якшення.

Клацніть і обведіть перші два ключа на часовій шкалі, щоб вибрати їх.

image16

Властивості обох ключів можна редагувати одночасно в Інспекторі, де можна побачити властивість Easing.

image17

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

image18

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

Застосуйте пом'якшення до другого ключового кадру на треку обертання.

image19

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

image20

Анімація повинна виглядати приблизно так.

image21

Примітка

Анімація оновлює властивості анімованих вузлів кожного кадру, перевизначаючи початкові значення. Якби ми анімували безпосередньо вузол Player, це завадило б нам переміщати його в коді. Ось де вузол Pivot стає в нагоді: незважаючи на те, що ми анімували Character, ми все ще можемо переміщати та обертати Pivot вверх з анімацією у скрипті.

Якщо ви запустите гру персонаж гравця тепер буде плавати!

Якщо він знаходиться трохи заблизько до підлоги, ви можете перемістити Pivot вгору, щоб компенсувати це.

Керування анімацією в коді

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

Відкрийте скрипт Player, клацнувши значок скрипта поруч із ним.

image22

В _physics_process(), після рядка, де ми перевіряємо direction вектор, додайте наступний код.

func _physics_process(delta):
    #...
    #if direction != Vector3.ZERO:
        #...
        $AnimationPlayer.playback_speed = 4
    else:
        $AnimationPlayer.playback_speed = 1

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

Ми згадали, що Pivot може переміщатися вверх з анімацією. Ми можемо зробити дугу персонажу при стрибку, використовуючи наступний рядок коду. Додайте його в кінці _physics_process().

func _physics_process(delta):
    #...
    $Pivot.rotation.x = PI / 6 * velocity.y / jump_impulse

Анімація монстрів

Ось ще один приємний трюк з анімацією в Godot: поки ви використовуєте подібну структуру вузлів, ви можете скопіювати її на різні сцени.

Наприклад, сцени Mob і Player мають вузол Pivot і Character, тому ми можемо повторно використовувати анімацію між ними.

Відкрийте сцену Mob, виберіть вузол та відкрийте анімацію плавання. Далі натисніть Анімація -> Копіювати. Потім відкрийте Player.tscn та відкрийте його АnimationРlayer. Натисніть кнопку Анімація -> Вставити. Ось і все; всі монстри тепер будуть відтворювати анімацію плавання.

Ми можемо змінити швидкість відтворення на основі швидкості монстра random_speed. Відкрийте скрипт Mob і в кінці функції initialize() додайте наступний рядок.

func initialize(start_position, player_position):
    #...
    $AnimationPlayer.playback_speed = random_speed / min_speed

І на цьому ви закінчили кодування своєї першої повної 3D-гри.

Вітаю!

У наступній частині ми швидко підсумуємо те, що ви дізналися, і дамо деякі посилання для подальшого навчання. А на даний момент ви можете перевірити свій код звірившись з повними зразками Player.gd і Mob.gd.

Ось скрипт Player.

extends KinematicBody

# Emitted when the player was hit by a mob.
signal hit

# How fast the player moves in meters per second.
export var speed = 14
# The downward acceleration when in the air, in meters per second per second.
export var fall_acceleration = 75
# Vertical impulse applied to the character upon jumping in meters per second.
export var jump_impulse = 20
# Vertical impulse applied to the character upon bouncing over a mob in meters per second.
export var bounce_impulse = 16

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)
        $AnimationPlayer.playback_speed = 4
    else:
        $AnimationPlayer.playback_speed = 1

    velocity.x = direction.x * speed
    velocity.z = direction.z * speed

    # Jumping
    if is_on_floor() and Input.is_action_just_pressed("jump"):
        velocity.y += jump_impulse

    velocity.y -= fall_acceleration * delta
    velocity = move_and_slide(velocity, Vector3.UP)

    for index in range(get_slide_count()):
        var collision = get_slide_collision(index)
        if collision.collider.is_in_group("mob"):
            var mob = collision.collider
            if Vector3.UP.dot(collision.normal) > 0.1:
                mob.squash()
                velocity.y = bounce_impulse

    $Pivot.rotation.x = PI / 6 * velocity.y / jump_impulse


func die():
    emit_signal("hit")
    queue_free()


func _on_MobDetector_body_entered(_body):
    die()

І скрипт Mob.

extends KinematicBody

# Emitted when the player jumped on the mob.
signal squashed

# Minimum speed of the mob in meters per second.
export var min_speed = 10
# Maximum speed of the mob in meters per second.
export var max_speed = 18

var velocity = Vector3.ZERO


func _physics_process(_delta):
    move_and_slide(velocity)


func initialize(start_position, player_position):
    look_at_from_position(start_position, player_position, Vector3.UP)
    rotate_y(rand_range(-PI / 4, PI / 4))

    var random_speed = rand_range(min_speed, max_speed)
    velocity = Vector3.FORWARD * random_speed
    velocity = velocity.rotated(Vector3.UP, rotation.y)

    $AnimationPlayer.playback_speed = random_speed / min_speed


 func squash():
    emit_signal("squashed")
    queue_free()


func _on_VisibilityNotifier_screen_exited():
    queue_free()