The main game scene

Now it's time to bring everything we did together into a playable game scene.

Crie uma nova cena e adicione um :ref: Node <class_Node> chamado Main. (O motivo para usarmos um Node ao invés deum Node2D é que esse será um recipiente para lidar com a lógica do jogo, por si ele não necessita de funcionalidade 2D.)

Click the Instance button (represented by a chain link icon) and select your saved Player.tscn.

../../_images/instance_scene.png

Agora adicione os seguintes nós como filhos de Principal, e os nomeie como mostrado (os valores estão em segundos):

  • Timer (nomeado MobTimer) - para controlar a frequência com que a turba é gerada

  • Timer (nomeado ScoreTimer) - para incrementar a pontuação a cada segundo

  • Timer (nomeado StartTimer) - para dar um atraso antes de começar

  • Position2D (nomeado StartPosition) - para indicar a posição inicial do jogador

Defina a propriedade Wait Time (tempo de espera) para cada um dos nós Timer da seguinte forma:

  • MobTimer: 0.5

  • ScoreTimer: 1

  • StartTimer: 2

Além disso, configure a propriedade One Shot ("uma só vez") de StartTimer para "Ativo" e Position do nó StartPosition para (240, 450).

Gerando monstros

O nó Principal ficará gerando novos inimigos, e nós queremos que eles apareçam em lugares aleatórios nos cantos da tela. Adicione um nó Path2D chamado CaminhoTurba como filho de Principal. Quando você selecionar Path2D, aparecerão alguns botões novos na parte superior do editor:

../../_images/path2d_buttons.png

Selecione o do meio ("Adicionar Ponto") e desenhe o caminho clicando para adicionar os pontos nos cantos mostrados. Para que os pontos fiquem alinhados à grade, verifique se a opção <b>Encaixar na Grade</b> está marcada. Essa opção pode ser encontrada à esquerda do botão "Travar", parecido como um ímã e ao lado de uma série de 3 pontos verticais.

../../_images/grid_snap_button.png

Importante

Desenhe o caminho em sentido horário, ou sua turba vai surgir apontando para fora em vez de para dentro!

../../_images/draw_path2d.gif

Depois de colocar o ponto 4 na imagem, clique no botão "Fechar Curva", e sua curva estará completa.

Agora que o caminho está definido, adicione um nó PathFollow2D como filho de CaminhoTurba e dê o nome de LocalGeraçãoTurba. Esse nó vai rotacionar automaticamente e seguir o caminho conforme ele se move, para que possamos usá-lo para selecionar uma posição e uma direção aleatória ao longo do caminho.

Sua cena deve se parecer com isso:

../../_images/main_scene_nodes.png

Script principal

Adicione um roteiro a Principal. No começo do script, nós usamos export (PackedScene) para permitir-nos escolher a cena Inimigo que queremos instanciar.

extends Node

export(PackedScene) var mob_scene
var score

We also add a call to randomize() here so that the random number generator generates different random numbers each time the game is run:

func _ready():
    randomize()

Click the Main node and you will see the Mob Scene property in the Inspector under "Script Variables".

Você pode atribuir o valor dessa propriedade de duas formas:

  • Drag Mob.tscn from the "FileSystem" dock and drop it in the Mob Scene property.

  • Clique na seta para baixo ao lado de "[vazio]" e escolha "Carregar". Selecione Mob.tscn.

Em seguida, selecione o nó do Player no painel Cena, e acesse o nó na barra lateral. Certifique-se de selecionar a aba Sinais no Painel de nós.

Você deve ver uma lista de sinais para o nó Player. Em seguida, encontre e dê dois cliques no sinal hit da lista (ou clique com o botão direito e selecione "Conectar..."). Isso abrirá a caixa de diálogo de conexão de sinal. Queremos criar uma função chamada game_over, que lidará com tudo o que precisa acontecer quando um jogo acaba. Digite "game_over" na caixa "Método no Nó" na parte inferior da janela de conexão de sinal e clique em "Conectar". Adicione o código a seguir à nova função, assim como uma função new_game (novo jogo) para definir tudo para um novo jogo:

func game_over():
    $ScoreTimer.stop()
    $MobTimer.stop()

func new_game():
    score = 0
    $Player.start($StartPosition.position)
    $StartTimer.start()

Agora, conecte o sinal timeout() de cada um dos nós de Timer (StartTimer, ScoreTimer , e``MobTimer``) para o script principal. StartTimer irá iniciar os outros dois temporizadores. ScoreTimer irá incrementar a pontuação em 1.

func _on_ScoreTimer_timeout():
    score += 1

func _on_StartTimer_timeout():
    $MobTimer.start()
    $ScoreTimer.start()

In _on_MobTimer_timeout(), we will create a mob instance, pick a random starting location along the Path2D, and set the mob in motion. The PathFollow2D node will automatically rotate as it follows the path, so we will use that to select the mob's direction as well as its position. When we spawn a mob, we'll pick a random value between 150.0 and 250.0 for how fast each mob will move (it would be boring if they were all moving at the same speed).

Note que uma nova instância deve ser adicionada à cena usando add_child() (adicionar filho).

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

    # Choose a random location on Path2D.
    var mob_spawn_location = get_node("MobPath/MobSpawnLocation")
    mob_spawn_location.offset = randi()

    # Set the mob's direction perpendicular to the path direction.
    var direction = mob_spawn_location.rotation + PI / 2

    # Set the mob's position to a random location.
    mob.position = mob_spawn_location.position

    # Add some randomness to the direction.
    direction += rand_range(-PI / 4, PI / 4)
    mob.rotation = direction

    # Choose the velocity for the mob.
    var velocity = Vector2(rand_range(150.0, 250.0), 0.0)
    mob.linear_velocity = velocity.rotated(direction)

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

Importante

Why PI? In functions requiring angles, Godot uses radians, not degrees. Pi represents a half turn in radians, about 3.1415 (there is also TAU which is equal to 2 * PI). If you're more comfortable working with degrees, you'll need to use the deg2rad() and rad2deg() functions to convert between the two.

Testando a cena

Let's test the scene to make sure everything is working. Add this new_game call to _ready():

func _ready():
    randomize()
    new_game()

Vamos também atribuir Main como nossa "Cena Principal" - aquela que é executada automaticamente quando o jogo é iniciado. Pressione o botão "Reproduzir" e selecione `` Main.tscn`` quando solicitado.

Dica

If you had already set another scene as the "Main Scene", you can right click Main.tscn in the FileSystem dock and select "Set As Main Scene".

Você deve ser capaz de mover o jogador, ver os inimigos nascendo, e ver o jogador desaparecer quando atingido por um inimigo.

Quando tiver certeza que tudo esta funcionando, remova a chamada de new_game() em _ready().

What's our game lacking? Some user interface. In the next lesson, we'll add a title screen and display the player's score.