Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Gerando monstros

Nesta parte, vamos fazer os monstros surgirem aleatoriamente ao longo de um caminho. No final, você terá monstros vagando pelo tabuleiro de jogo.

imagem0

Clique duas vezes em main.tscn na aba FileSystem (Sistema de Arquivos) para abrir a cena Main.

Antes de desenhar o caminho, vamos alterar a resolução do jogo. Nosso jogo possui um tamanho de janela padrão de 1152x648. Vamos defini-lo como 720x540, uma resolução compacta e agradável.

Vá para Project -> Project Settings.

imagem1

Se você ainda estiver com a aba Input Map aberta, mude para a aba Geral.

No menu esquerdo, navegue para baixo até Display -> Window (Tela -> Janela). À direita, defina a Viewport Width (Largura da Janela de Exibição) como 720 e a Viewport Height (Altura da Janela de Exibição) como 540.

imagem2

Criando o caminho de surgimento

Assim como você fez no tutorial do jogo 2D, você vai desenhar um caminho e usar um nó PathFollow3D para obter locais aleatórios nele.

No entanto, em 3D, é um pouco mais complicado desenhar o caminho. Queremos que ele esteja em torno da vista do jogo para que os monstros apareçam fora da tela. Mas se desenharmos um caminho, não o veremos pela pré-visualização da câmera.

Para encontrar os limites da visualização, podemos usar algumas malhas de marcação (placeholder meshes). Sua janela de exibição ainda deve estar dividida em duas partes, com a pré-visualização da câmera na parte inferior. Se não for o caso, pressione Ctrl + 2 (Cmd + 2 no macOS) para dividir a visualização em duas. Selecione o nó Camera3D e marque a caixa de seleção Preview (Pré-visualização) na janela de exibição inferior.

imagem3

Adicionando cilindros de substituto

Vamos adicionar as malhas substitutas. Adicione um novo Node3D como filho do nó Principal e o nomeie Cilindros. Vamos usá-lo para agrupar os cilindros. Selecione Cilindros e adicione um nó como filho MeshInstance3D

imagem4

No Inspetor, atribua um CylinderMesh à propriedade Malha.

imagem5

Defina a janela de exibição superior com a visualização ortogonal superior usando o menu no canto superior esquerdo da janela de exibição. Como alternativa, você pode pressionar a tecla 7 do teclado.

image6

A grade pode ser uma distração. Você pode ocultá-la acessando o menu View na barra de ferramentas e clicando em View Grid.

image7

Agora você quer mover o cilindro ao longo do plano do solo, observando a visualização da câmera no inferior da janela de visualização. Eu recomendo usar o encaixe de grade para fazer isso. Você pode alterná-lo clicando no ícone do ímã na barra de ferramentas ou pressionando Y.

image8

Mova o cilindro para uma posição logo fora da área visível da câmera, no canto superior esquerdo.

image9

Vamos criar cópias da malha e posicioná-las ao redor da área do jogo. Pressione Ctrl + D (Cmd + D no macOS) para duplicar o nó. Você também pode clicar com o botão direito no nó, na aba Cena, e selecionar Duplicar. Mova a cópia para baixo, ao longo do eixo Z azul, até que ela fique logo fora da área de visualização da câmera.

Selecione ambos os cilindros pressionando a tecla Shift e clique no não selecionado e duplique-os.

image10

Mova-os para a direita arrastando o eixo X vermelho.

imagem11

Eles estão um pouco difíceis de ver em branco, não estão? Vamos fazer com que eles se destaquem dando a eles um novo material.

Em 3D, os materiais definem as propriedades visuais de uma superfície, como sua cor, como ela reflete a luz e muito mais. Podemos usá-los para mudar a cor de uma malha.

Podemos atualizar todos os quatro cilindros de uma vez. Selecione todas as instâncias de malha no painel Cena. Para fazer isso, você pode clicar no primeiro e com o Shift apertado clicar no último.

imagem12

No Inspector (Inspetor), expanda a seção Material e atribua um StandardMaterial3D ao slot 0.

imagem13

Clique no ícone da esfera para abrir o recurso material. Você obtém uma visualização do material e uma longa lista de seções preenchidas com propriedades. Você pode usá-los para criar todos os tipos de superfícies, de metal a rocha ou água.

Expanda a seção Albedo.

../../_images/albedo_section.webp

Defina a cor para uma que contraste com o fundo, como uma laranja brilhante.

imagem14

We can now use the cylinders as guides. Fold them in the Scene dock by clicking the gray arrow next to them. Moving forward, you can also toggle their visibility by clicking the eye icon next to Cylinders.

image15

Adicione um nó filho Path3D ao nó Main. Na barra de ferramentas, quatro ícones aparecem. Clique na ferramenta Add Point (Adicionar Ponto), o ícone com o sinal "+" verde.

image16

Nota

Você pode passar o mouse sobre qualquer ícone para ver uma dica de ferramenta descrevendo a ferramenta.

Clique no centro de cada cilindro para criar um ponto. Em seguida, clique no ícone Fechar curva na barra de ferramentas para fechar o caminho. Se algum ponto estiver um pouco fora, você pode clicar e arrastar sobre ele para reposicioná-lo.

image17

Seu caminho deve se parecer com isso.

image18

Para obter posições aleatórias nele, precisamos de um nó PathFollow3D. Adicione um PathFollow3D como filho do Path3D. Renomeie os dois nós para SpawnLocation e SpawnPath, respectivamente. É mais descritivo para o que vamos usá-los.

image19

Com isso, estamos prontos para programar o mecanismo de geração.

Gerando monstros aleatóriamente

Clique com o botão direito no nó Main e anexe um novo script a ele.

Primeiro, exportamos uma variável para o Inspector para que possamos atribuir mob.tscn ou qualquer outro monstro a ela.

extends Node

@export var mob_scene: PackedScene

Queremos gerar inimigos em intervalos de tempo regulares. Para fazer isso, precisamos voltar ao local e adicionar um temporizador. Antes disso, porém, precisamos atribuir o arquivo mob.tscn para a propriedade mob_scene acima (caso contrário, é nulo!)

Volte para a tela 3D e selecione o nó Main. Arraste mob.tscn da aba FileSystem (Sistema de Arquivos) para o slot Mob Scene no Inspector (Inspetor).

image20

Adicione um novo nó Timer como filho de Main. Nomeie-o como MobTimer.

image21

No Inspetor, defina o Wait Time como 0,5 segundos e ative o Autostart para que ele inicie automaticamente quando executarmos o jogo.

image22

Os temporizadores emitem um sinal timeout cada vez que chegam ao final de seu Tempo de espera. Por padrão, eles reiniciam automaticamente, emitindo o sinal em um ciclo. Podemos nos conectar a este sinal a partir do nó Principal para gerar os monstros a cada 0.5 segundos.

Com o MobTimer ainda selecionado, vá até o painel Signals à direita e dê um duplo clique no sinal timeout.

image23

Conecte-o ao nó Principal.

image24

Isso o levará de volta ao script, com uma nova função vazia _on_mob_timer_timeout().

Vamos programar lógica de surgimento do mob. Nós vamos:

  1. Instanciar a cena da mob.

  2. Gera uma posição aleatória no caminho de surgimento.

  3. Obtenha a posição do jogador.

  4. Chame o método initialize() do mob, passando a ele a posição aleatória e a posição do jogador.

  5. Adicione o mob como um filho do nó Principal.

func _on_mob_timer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instantiate()

    # Choose a random location on the SpawnPath.
    # We store the reference to the SpawnLocation node.
    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    # And give it a random offset.
    mob_spawn_location.progress_ratio = randf()

    var player_position = $Player.position
    mob.initialize(mob_spawn_location.position, player_position)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

Acima, randf() produz um valor aleatório entre 0 e 1`, que é o que o progress_ratio do nó PathFollow espera: 0 é o início do caminho, 1 é o fim do caminho. O caminho que temos definido é em torno do viewport da câmera, então qualquer valor aleatório entre 0 e 1 é uma posição aleatória ao lado das bordas do viewport!

Observe que, se você remover o Jogador da cena principal, a seguinte linha

var player_position = $Player.position

gera um erro porque não existe nenhum $Player!

Aqui está o script main.gd completo até agora, para referência.

extends Node

@export var mob_scene: PackedScene


func _on_mob_timer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instantiate()

    # Choose a random location on the SpawnPath.
    # We store the reference to the SpawnLocation node.
    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    # And give it a random offset.
    mob_spawn_location.progress_ratio = randf()

    var player_position = $Player.position
    mob.initialize(mob_spawn_location.position, player_position)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

Você pode testar a cena pressionando F6 (Cmd + R no macOS). Você deverá ver os monstros surgirem e se moverem em linha reta.

image25

Por enquanto, eles se chocam e deslizam um contra o outro quando seus caminhos se cruzam. Trataremos disso na próxima parte.