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.
Checking the stable version of the documentation...
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.

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.

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.

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.

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

No Inspetor, atribua um CylinderMesh à propriedade Malha.

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.

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

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.

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

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.

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

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.

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

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.
Defina a cor para uma que contraste com o fundo, como uma laranja brilhante.

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.

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.

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.

Seu caminho deve se parecer com isso.

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.

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
using Godot;
public partial class Main : Node
{
// Don't forget to rebuild the project so the editor knows about the new export variable.
[Export]
public PackedScene MobScene { get; set; }
}
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).

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

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

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.

Conecte-o ao nó Principal.

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:
Instanciar a cena da mob.
Gera uma posição aleatória no caminho de surgimento.
Obtenha a posição do jogador.
Chame o método
initialize()do mob, passando a ele a posição aleatória e a posição do jogador.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)
// We also specified this function name in PascalCase in the editor's connection window.
private void OnMobTimerTimeout()
{
// Create a new instance of the Mob scene.
Mob mob = MobScene.Instantiate<Mob>();
// Choose a random location on the SpawnPath.
// We store the reference to the SpawnLocation node.
var mobSpawnLocation = GetNode<PathFollow3D>("SpawnPath/SpawnLocation");
// And give it a random offset.
mobSpawnLocation.ProgressRatio = GD.Randf();
Vector3 playerPosition = GetNode<Player>("Player").Position;
mob.Initialize(mobSpawnLocation.Position, playerPosition);
// Spawn the mob by adding it to the Main scene.
AddChild(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
Vector3 playerPosition = GetNode<Player>("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)
using Godot;
public partial class Main : Node
{
[Export]
public PackedScene MobScene { get; set; }
private void OnMobTimerTimeout()
{
// Create a new instance of the Mob scene.
Mob mob = MobScene.Instantiate<Mob>();
// Choose a random location on the SpawnPath.
// We store the reference to the SpawnLocation node.
var mobSpawnLocation = GetNode<PathFollow3D>("SpawnPath/SpawnLocation");
// And give it a random offset.
mobSpawnLocation.ProgressRatio = GD.Randf();
Vector3 playerPosition = GetNode<Player>("Player").Position;
mob.Initialize(mobSpawnLocation.Position, playerPosition);
// Spawn the mob by adding it to the Main scene.
AddChild(mob);
}
}
Você pode testar a cena pressionando F6 (Cmd + R no macOS). Você deverá ver os monstros surgirem e se moverem em linha reta.

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