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 Principal.tscn
no painel Sistema de Arquivos para abrir a cena Principal .
Antes de traçar o caminho, vamos mudar a resolução do jogo. Nosso jogo tem um tamanho de janela padrão de 1024x600
. Vamos configurá-lo para 720x540
, uma boa telinha.
Vá para Project -> Project Settings.
No menu à esquerda, navegue até Exibir -> janela. À direita, defina a Largura como ''720'' e a Altura como ''540''.
Criando o caminho de surgimento¶
Como você fez no tutorial do jogo 2D, você vai desenhar um caminho e usar um nó PathFollow para gerar 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 vista, podemos usar algumas malhas de suporte. Sua janela de exibição ainda deve ser dividido em duas partes, com a visualização da câmera na parte inferior. Se este não for o caso, pressione Ctrl + 2 (Cmd + 2 em macOS) para dividir a sua visão em duas partes. Selecione o nó Câmera e clique na caixa de seleção Preview na parte inferior da janela de exibição.
Adicionando cilindros de substituto¶
Vamos adicionar as malhas substitutas. Adicione um novo nó Espacial como filho do nó Principal e o nomeie Cilindros. Vamos usá-lo para agrupar os cilindros. Como filho dele, adicione um nó MeshInstance.
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 me causa um pouco de distração. Você pode ligá-la ou desligá-la acessando o menu Exibir na barra de ferramentas e clicando em Exibir grade.
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.
Coloque o cilindro de modo que ele fique bem fora da visão da câmera no canto superior esquerdo.
Vamos criar cópias da malha e colocá-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 do mouse no nó no painel*Cena* e selecionar Duplicar. Mova a cópia para baixo ao longo do eixo Z azul até que esteja fora da 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 Inspetor, expanda a seção Material e atribua um SpatialMaterial no 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 e defina a cor para algo que contraste com o fundo, como um laranja brilhante.
Agora podemos usar os cilindros como guias. Dobre-os no painel Cena clicando na seta cinza ao lado deles. Continuando, você também pode alternar sua visibilidade clicando no ícone de olho ao lado de Cilindros.
Adicione um nó Path como filho de Principal. Na barra de ferramentas, quatro ícones são exibidos. Clique na ferramenta Adicionar ponto, no í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 gerar posições aleatórias nele, precisamos de um nó PathFollow. Adicione um PathFollow como filho de Path. Renomeie os dois nós para SpawnPath e SpawnLocation, respectivamente. Assim fica 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 do mouse no nó Principal e adicione um novo script a ele.
Primeiro exportamos uma variável para o Inspetor para que possamos atribuir Mob.tscn
ou qualquer outro monstro a ela.
Então, como vamos gerar os monstros processualmente, queremos randomizar os números toda vez que jogamos o jogo. Se não fizermos isso, os monstros sempre surgem seguindo a mesma sequência.
extends Node
export (PackedScene) var mob_scene
func _ready():
randomize()
public class Main : Node
{
// Don't forget to rebuild the project so the editor knows about the new export variable.
#pragma warning disable 649
// We assign this in the editor, so we don't need the warning about not being assigned.
[Export]
public PackedScene MobScene;
#pragma warning restore 649
public override void _Ready()
{
GD.Randomize();
}
}
Queremos desovar multidões em intervalos regulares de tempo. Para isso, precisamos voltar a cena e acrescentar um temporizador. Antes disso, porém, precisamos atribuir o arquivo Mob.tscn
à propriedade mob_scene
.
Volte para a tela 3D e selecione o nó principal. Arraste Mob.tscn
do painel Sistema de Arquivos para o slot Cena Inimigo no inspetor.
Adicione um novo nó Timer como filho de Principal. Nomeie-o InimigoTimer.
No Inspetor, defina seu Tempo de Espera para ''0,5'' segundos e ative o Autoiniciar para que ele seja iniciado 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, dirija-se ao painel Nó à direita e clique duas vezes no sinal timeout
.
Conecte-o ao nó Principal.
Isso o levará de volta ao script, com uma nova função _on_MobTimer_timeout()
vazia.
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_MobTimer_timeout():
# Create a new instance of the Mob scene.
var mob = mob_scene.instance()
# 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.unit_offset = randf()
var player_position = $Player.transform.origin
mob.initialize(mob_spawn_location.translation, player_position)
add_child(mob)
// We also specified this function name in PascalCase in the editor's connection window
public void OnMobTimerTimeout()
{
// Create a new instance of the Mob scene.
Mob mob = (Mob)MobScene.Instance();
// Choose a random location on the SpawnPath.
// We store the reference to the SpawnLocation node.
var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
// And give it a random offset.
mobSpawnLocation.UnitOffset = GD.Randf();
Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
mob.Initialize(mobSpawnLocation.Translation, playerPosition);
AddChild(mob);
}
Acima, ''randf()'' produz um valor aleatório entre ''0'' e ''1'', que é o que o nó PathFollow ''unit_offset'' espera.
Aqui está o script Main.gd
completo até agora, para referência.
extends Node
export (PackedScene) var mob_scene
func _ready():
randomize()
func _on_MobTimer_timeout():
var mob = mob_scene.instance()
var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
mob_spawn_location.unit_offset = randf()
var player_position = $Player.transform.origin
mob.initialize(mob_spawn_location.translation, player_position)
add_child(mob)
public class Main : Node
{
#pragma warning disable 649
[Export]
public PackedScene MobScene;
#pragma warning restore 649
public override void _Ready()
{
GD.Randomize();
}
public void OnMobTimerTimeout()
{
Mob mob = (Mob)MobScene.Instance();
var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
mobSpawnLocation.UnitOffset = GD.Randf();
Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
mob.Initialize(mobSpawnLocation.Translation, playerPosition);
AddChild(mob);
}
}
Você pode testar a cena pressionando F6. Você deve 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.