Animação de personagem

Nesta lição final, usaremos as ferramentas de animação embutidas do Godot para fazer nossos personagens flutuarem e baterem asas. Você aprenderá a criar animações no editor e usar o código para dar vida ao seu jogo.

imagem0

Começaremos com uma introdução para usar o editor de animação.

Usando o editor de animação

A engine vem com ferramentas para criar animações no editor. Você pode então usar o código para executá-las e controlá-los em tempo de execução.

Abra a cena do jogador, selecione o nó jogador e adicione um nó AnimationPlayer.

O painel Animação aparece no painel inferior.

imagem1

Ele apresenta uma barra de ferramentas e o menu suspenso de animação na parte superior, um editor de pistas no faixa que está atualmente vazio, e opções de filtro, snap e zoom na parte inferior.

Vamos criar uma animação. Clique em Animação -> Novo.

imagem2

Chame a animação de "float".

imagem3

Depois de ter criado a animação, a linha do tempo aparece com números que representam o tempo em segundos.

imagem4

Queremos que a animação inicie a reprodução automaticamente no início do jogo. Além disso, ele deve fazer um loop.

Para fazer isso, você pode clicar no botão com um ícone "A +" na barra de ferramentas de animação e nas setas em loop, respectivamente.

imagem5

Você também pode fixar o editor de animação clicando no ícone de fixação no canto superior direito. Isso evita que ele se dobre quando você clica no visor e desmarca os nós.

|imagem 6|

Defina a duração da animação para 1.2 segundos na parte superior direita do painel.

image7

Você deve ver a faixa cinza se alargar um pouco. Ela mostra o início e o fim da animação e a linha azul vertical é o cursor de tempo.

image8

Você pode clicar e arrastar o controle deslizante no canto inferior direito para aumentar e diminuir o zoom da linha do tempo.

image9

A animação fluida

Com o nó do reprodutor de animação, você pode animar a maioria das propriedades em quantos nós precisar. Observe o ícone de chave ao lado das propriedades no Inspetor. Você pode clicar em qualquer um deles para criar um quadro-chave, um par de tempo e valor para a propriedade correspondente. O quadro-chave é inserido onde seu cursor de tempo está na linha do tempo.

Vamos inserir nossas primeiras chaves. Aqui, animaremos a translação e a rotação do nó Personagem.

Selecione o Personagem e clique no ícone de chave ao lado de Translação no Inspetor. Faça o mesmo para Graus de rotação.

image10

Duas faixas aparecem no editor com um ícone de diamante representando cada quadro-chave.

imagem11

Você pode clicar e arrastar os diamantes para movê-los no tempo. Mova a chave de translação para 0.2 segundos e a chave de rotação para 0.1 segundos.

imagem12

Mova o cursor de tempo para 0.5 segundos clicando e arrastando na linha do tempo cinza. No Inspector, defina o eixo Y de Translação para cerca de 0.65 metros e o eixo X de Graus de rotação' para 8.

imagem13

Crie um quadro-chave para ambas as propriedades e mude a chave de tradução para 0,7 segundos arrastando-o na linha do tempo.

imagem14

Nota

Uma palestra sobre os princípios da animação está além do escopo deste tutorial. Apenas observe que você não deseja cronometrar e espaçar tudo uniformemente. Em vez disso, os animadores brincam com o tempo e o espaçamento, dois princípios básicos da animação. Você quer variar e contrastar o movimento do seu personagem para fazê-lo parecer vivo.

Mova o cursor de tempo para o final da animação, em 1.2 segundos. Defina a translação Y para cerca de 0.35 e a rotação X para -9 graus. Mais uma vez, crie uma chave para ambas as propriedades.

Você pode visualizar o resultado clicando no botão play ou pressionando Shift + D. Clique no botão parar ou pressione S para parar a reprodução.

image15

Você pode ver que a engine interpola entre seus quadros-chave para produzir uma animação contínua. No momento, porém, o movimento parece muito robótico. Isso ocorre porque a interpolação padrão é linear, causando transições constantes, ao contrário de como os seres vivos se movem no mundo real.

Podemos controlar a transição entre quadros-chave usando curvas de suavisação.

Clique e arraste ao redor das duas primeiras chaves na linha do tempo para usar a seleção em retângulo.

image16

Você pode editar as propriedades de ambas as chaves simultaneamente no Inspector, onde você pode ver uma propriedade de*Suavisação*.

image17

Clique e arraste sobre a curva, puxando-a para a esquerda. Isso suavizará, ou seja, a transição será rápida inicialmente e desacelerada quando o cursor de tempo atingir o próximo quadro-chave.

image18

Reproduza a animação novamente para ver a diferença. A primeira metade já deve parecer um pouco mais animada.

Aplique uma suavização ao segundo quadro-chave na faixa de rotação.

image19

Faça o oposto para o segundo quadro-chave de translação, arrastando-o para a direita.

image20

Sua animação deve ser algo parecido com isto.

image21

Nota

As animações atualizam as propriedades dos nós animados a cada quadro, substituindo os valores iniciais. Se animarmos diretamente o nó Player, isso nos impediria de movê-lo no código. É aqui que o nó Pivô é útil: mesmo que tenhamos animado o Personagem, ainda podemos mover e girar o Pivô e camada muda sobre a animação em um script.

Se você jogar o jogo, a criatura do jogador vai flutuar agora!

Se a criatura estiver um pouco perto demais do chão, você pode mover o Pivô para cima para deslocá-lo.

Controlando a animação por código

Podemos usar o código para controlar a reprodução da animação com base na entrada do jogador. Vamos mudar a velocidade da animação quando o personagem estiver se movendo.

Abra o script do Jogador clicando no ícone de script próximo a ele.

image22

Em _physics_process(), após a linha onde verificamos o vetor direction, adicione o seguinte código.

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

Este código faz com que quando o jogador se mova, multipliquemos a velocidade de reprodução por 4. Quando eles param, nós redefinimos para o normal.

Mencionamos que o pivô pode transformar camadas em cima da animação. Podemos fazer o arco do personagem ao pular usando a seguinte linha de código. Adicione-o no final de _physics_process().

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

Animando os inimigos

Aqui está outro bom truque com animações no Godot: contanto que você use uma estrutura de nó semelhante, você pode copiá-los para cenas diferentes.

Por exemplo, as cenas Inimigo e Jogador têm um nó Pivô e Personagem, para que possamos reutilizar animações entre elas.

Abra a cena Jogador, selecione o nó do player de animação e abra a animação "float". Em seguida, clique em Animação > Copiar. Em seguida, abra Mob.tscn e abra seu player de animação. Clique em Animação > Colar. É isso. todos os monstros agora vão reproduzir a animação de flutuar.

Podemos alterar a velocidade de reprodução com base na random_speed da criatura. Abra o script Inimigo e no final da função initialize(), adicione a seguinte linha.

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

E com isso, você terminou de programar seu primeiro jogo 3D completo.

Parabéns!

Na próxima parte, vamos recapitular rapidamente o que você aprendeu e dar alguns links para continuar aprendendo mais. Mas por enquanto, aqui estão os Jogador.gd e Mob.gd completos para que você possa comparar com seu código.

Aqui está o script do Jogador.

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()

E o script do Inimigo.

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()