Projetando a cena inimigo

Nesta parte, você vai programar os monstros, que chamaremos de inimigos. Na próxima lição, vamos fazê-los surgir aleatoriamente ao redor da área jogável.

Vamos projetar os próprios monstros em uma nova cena. A estrutura do nó vai ser semelhante à cena Player.

Crie uma cena com, mais uma vez, um nó KinematicBody como sua raiz. Nomeie-o Inimigo. Adicione um nó Spatial como filho dele, nomeie-o Pivô. E arraste e solte o arquivo mob.glb do painel Sistema de Arquivos no Pivô para adicionar o modelo 3D do monstro à cena. Você pode renomear o nó Inimigo recém-criado para Personagem.

imagem0

Precisamos de uma forma de colisão para que nosso corpo funcione. Clique com o botão direito do mouse sobre o nó Inimigo, a cena raiz, e clique em Adicionar nó Filho(a).

imagem1

Adicione um CollisionShape.

imagem2

No Inspetor, atribua um BoxShape à propriedade Shape.

imagem3

Devemos mudar seu tamanho para adequá-lo melhor ao modelo 3D. Você pode fazer isso interativamente clicando e arrastando os pontos laranja.

A caixa deve tocar o chão e ser um pouco mais fina do que o modelo. Os motores físicos funcionam de tal forma que se a esfera do jogador tocar até mesmo o canto da caixa, ocorrerá uma colisão. Se a caixa for um pouco grande demais em comparação com o modelo 3D, você pode morrer a uma distância do monstro, e o jogo será injusto para os jogadores.

imagem4

Note que minha caixa é mais alta do que o monstro. Está tudo bem neste jogo porque estamos olhando para a cena de cima e usando uma perspectiva fixa. As formas de colisão não têm que combinar exatamente com o modelo. É o modo como você sente o jogo quando o testa que deve ditar sua a forma e tamanho.

Removendo monstros fora da tela

Vamos fazer os monstros surgirem em intervalos regulares de tempo no nível do jogo. Se não tivermos cuidado, sua contagem pode aumentar até o infinito, e não queremos isso. Cada instância do Inimigo tem tanto uma memória quanto um custo de processamento, e não queremos pagar por isso quando o inimigo está fora da tela.

Depois que um monstro sai da tela, não precisamos mais dele, então podemos excluí-lo. Godot tem um nó que detecta quando os objetos saem da tela, VisibilityNotifier, e vamos usá-lo para destruir nossos inimigos.

Nota

Quando você continua instanciando um objeto nos jogos, há uma técnica que você pode usar para evitar o custo de criar e destruir instâncias o tempo todo chamadas de "pooling". Ela consiste em pré-criar um conjunto de objetos e reutilizá-los repetidamente.

Ao trabalhar com o GDScript, você não precisa se preocupar com isso. A principal razão para utilizar as "pools" é evitar travamentos com linguagens "garbage-collected" como C# ou Lua. O GDScript usa uma técnica diferente para gerenciar a memória, contagem de referência, que não tem essa ressalva. Você pode aprender mais sobre isso aqui Gerenciamento de memória.

Selecione o nó Inimigo e adicione um VisibilityNotifier como filho dele. Outra caixa, rosa desta vez, aparece. Quando essa caixa sair completamente da tela, o nó emitirá um sinal.

imagem5

Redimensione-a usando os pontos laranja até cobrir todo o modelo 3D.

|imagem 6|

Programando a movimentação dos inimigos

Vamos implementar o movimento do monstro. Vamos fazer isso em duas etapas. Primeiro, escreveremos um script do Inimigo que define uma função para inicializar o monstro. Em seguida, vamos programar o mecanismo de surgimento aleatório na cena Principal e chamar a função a partir daí.

Anexe um script ao Inimigo.

image7

Aqui está o código de movimento para começarmos. Definimos duas propriedades, min_speed e max_speed, para definir uma faixa de velocidade aleatória. Definimos e inicializamos então o velocity.

extends KinematicBody

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

Da mesma forma que o jogador, movemos o inimigo a cada quadro chamando o método `` move_and_slide()``do KinematicBody método. Desta vez, não atualizamos o velocity cada frame: queremos que o monstro se mova a uma velocidade constante e saia da tela, mesmo que fosse para acertar um obstáculo.

Você talvez veja um aviso no GDScript de que o valor de retorno de move_and_slide() não é utilizado. Isto é esperado. Você pode simplesmente ignorar o aviso ou, se quiser ocultá-lo completamente, adicionar o comentário # warning-ignore:return_value_discarded logo acima da linha move_and_slide(velocity). Para ler mais sobre o sistema de aviso GDScript, veja Sistema de alertas do GDScript.

É preciso definir outra função para calcular a velocidade de partida. Esta função vai virar o monstro em direção ao jogador e randomizar tanto seu ângulo de movimento quanto sua velocidade.

A função terá como argumentos start_position, a posição de surgimento do inimigo, e o player_position como seus argumentos.

Posicionamos o inimigo em start_position e o giramos em direção ao jogador utilizando o método look_at_from_position(), e randomizamos o ângulo girando uma quantidade aleatória ao redor do eixo Y. Abaixo, rand_range() produz um valor aleatório entre -PI / 4 radians e PI / 4 radians.

# We will call this function from the Main scene.
func initialize(start_position, player_position):
    # We position the mob and turn it so that it looks at the player.
    look_at_from_position(start_position, player_position, Vector3.UP)
    # And rotate it randomly so it doesn't move exactly toward the player.
    rotate_y(rand_range(-PI / 4, PI / 4))

Calculamos então uma velocidade aleatória utilizando rand_range() mais uma vez e a utilizamos para calcular a velocidade.

Começamos criando um vetor 3D apontando para frente, multiplicando-o por nosso random_speed, e finalmente o giramos utilizando o método rotated() da classe Vector3.

func initialize(start_position, player_position):
    # ...

    # We calculate a random speed.
    var random_speed = rand_range(min_speed, max_speed)
    # We calculate a forward velocity that represents the speed.
    velocity = Vector3.FORWARD * random_speed
    # We then rotate the vector based on the mob's Y rotation to move in the direction it's looking.
    velocity = velocity.rotated(Vector3.UP, rotation.y)

Saindo da tela

Ainda temos que destruir os inimigos quando eles deixam a tela. Para isso, conectaremos o sinal screen_exited do nosso nó VisibilityNotifier ao Inimigo.

Volte para o viewport 3D clicando na etiqueta 3D na parte superior do editor. Você também pode pressionar Ctrl + F2 (Alt + 2 em macOS).

image8

Selecione o nó VisibilityNotifier e, no lado direito da interface, navegue até painel . Clique duas vezes no sinal screen_exited().

image9

Conecte o sinal para o Mob.

image10

Isto o levará de volta ao editor de scripts e adicionará uma nova função para você, _on_VisibilityNotifier_screen_exited(). A partir dela, chame o método queue_free(). Isto destruirá a instância do inimigo quando a caixa do VisibilityNotifier deixar a tela.

func _on_VisibilityNotifier_screen_exited():
    queue_free()

Nosso monstro está pronto para entrar no jogo! Na próxima parte, você fará os monstros surgirem na fase do jogo.

Aqui está o script Mob.gd completo para referência.

extends KinematicBody

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


func _on_VisibilityNotifier_screen_exited():
    queue_free()