Criação do jogo "Like Coins" no Godot Engine. Parte 2

Espero que você esteja cansado de esperar pela segunda parte do artigo, que aborda o desenvolvimento de jogos usando o Godot Engine, usando o exemplo do jogo Like Coins? Muito de tudo "saboroso" e "saudável" foi preparado na agenda. Farei uma reserva imediata de que neste artigo concluiremos o jogo iniciado anteriormente, cujo início você pode ler aqui - Criando o jogo "Like Coins" no Godot Engine. Parte 1 , mas a série de artigos continuará, porque havia tanto material que me fez anular parte dele, mas definitivamente voltaremos a ele mais tarde. Deixe o "gamedev" começar!


Cena "Principal"


Na parte anterior do artigo, paramos no palco principal ( Main ) e, a partir dele, talvez continuemos. Excluímos tudo o que foi adicionado anteriormente (se você adicionou algo para verificar como tudo funciona), se nada foi carregado na cena, devemos adicionar o Node , que será o pai dos nós listados abaixo, que por sua vez também deve ser adicionado à cena:


ColorRect ("Background") - preencha a cor de fundo;
Player - o objeto "Player" (espero que você não esteja confuso porque chamo a cena de Player objeto?);
Node ("Container") - "container" para armazenamento temporário de moedas;
Position2D ("PlayerStart") - no início do jogo define a posição inicial do objeto "Player";
Timer ("GameTimer") - contador de limite de tempo;


Selecione ColorRect e, na barra de ferramentas, selecione: Layout -> Full Rect para esticá-lo por toda a área da tela (no futuro, muitas vezes recorreremos a essa função, por isso aconselho você a estudar outras operações especificadas na lista Layout ), para este nó, na propriedade "Cor" especifique a cor de preenchimento desejada. Você pode fazer o mesmo com o TextureRect . Somente em vez de preencher, você precisará carregar a imagem através da propriedade "Texture". Para Position2D , na propriedade "position", especifique os valores de "x" e "y" - isso servirá como a posição inicial para o Player . Obviamente, com a ajuda do script, você pode definir os valores de posicionamento diretamente no próprio Player , mas não apenas aprendemos a desenvolver jogos, mas também estudamos "Godot", portanto, a consideração de diferentes opções para resolver um problema não será supérflua.


Script para "Principal"


Adicione um script para Node e imprima o seguinte:


 extends Node #PackedScene        export (PackedScene) var Coin export (int) var playtime var level #  var score # var left_time #     var window_size #   var playing = false #    

As propriedades "Moeda" e "tempo de reprodução" serão exibidas no Inspector . Arraste a cena "Coin.tscn" para a propriedade "Coin" e defina o valor "playtime" para "40" (a duração do jogo em segundos).


Quando o jogo começa, cada vez que deve haver inicialização - preparação para o trabalho, determinação dos parâmetros necessários para uma operação de alta qualidade e sem erros do aplicativo. Esta é uma etapa obrigatória, portanto, você deve cuidar disso primeiro.


 func _ready(): randomize() #        window_size = get_viewport().get_visible_rect().size #    $Player.window_size = window_size #    "" $Player.hide() #   

Observe que, ao especificar o nome do objeto Player , o símbolo "$" é usado - este é o "açúcar sintático" que permite acessar diretamente o nó na cena atual, uma boa alternativa ao get_node("Node1") (embora o uso deste último não seja proibido). Se "Nó1" tiver um descendente de "Nó2", você também poderá usar este método - $Node1/Node2 . Observe que o preenchimento automático funciona muito bem em Godot, portanto, não o negligencie. Usar um espaço nos nomes dos nós é indesejável, mas ainda válido, nesse caso, use aspas - $"My best Node1" .


Novo jogo


Para iniciar um novo jogo, determinaremos a função correspondente a isso, que podemos chamar, por exemplo, pressionando um botão.


 func new_game(): playing = true #   level = 1 score = 0 time_left = playtime $Player.start($PlayerStart.position) $Player.show() $GameTimer.start() #    spawn_coins() #  

A função "start ()", cujo argumento é $PlayerStart.position , moverá o jogador para o local inicial, e a função "spawn_coins ()" é responsável, como você pode imaginar, pelas moedas de spawn no campo de jogo.


 func spawn_coins(): for i in range(4 + level): var c = Coin.instance() $CoinContainer.add_child(c) c.window_size = window_size c.position = Vector2(rand_range(0, window_size.x), rand_range(0, window_size.y)) 

A função range(4 + level) retornará um array com um determinado range cujo valor é igual à soma do número de moedas e o valor do nível atual. Um intervalo pode conter um argumento, como no nosso caso, dois ou três argumentos (o terceiro argumento será uma etapa da matriz). Nesta função, criamos várias instâncias do objeto "Coin" e adicionamos CoinContainer como elementos filho (espero que você não tenha esquecido que já temos acesso ao objeto, graças ao PackedScene ). Lembre-se de que toda vez que você cria uma instância de um novo nó (o método instance() ), ele deve ser adicionado à árvore usando add_child() . Em seguida, definimos a área para uma possível geração de moedas para que elas não apareçam acidentalmente atrás da tela e depois atribuímos uma posição aleatoriamente. A última linha não parece um pouco esteticamente agradável, então sugiro simplificá-la recorrendo ao Singleton.


Singleton


O nome do meio do Singleton é "Inicialização". Já é sugestivo, certo? Digo a você, um singleton funciona da seguinte maneira: um script no qual podemos escrever o que quisermos (começando de declarar variáveis ​​e terminando com "comutadores" de cenas, incluindo carregar e descarregar) é carregado primeiro, com o aplicativo em execução e todo o seu conteúdo acessível a partir de qualquer pontos do projeto. De certa forma, esse é um tipo de repositório global personalizado de “qualquer coisa” disponível a qualquer momento.


Observe que o projeto possui seu próprio repositório global, cujo conteúdo também podemos usar, e você pode acessá-lo usando ProjectSettings.get_setting(name) , em que name é o nome do parâmetro necessário.

Agora, para usar algo do repositório "_G", basta chamá-lo pelo nome e especificar o método a ser chamado, ou o que tivermos lá. Portanto, crie um script vazio e escreva nele a função indicada abaixo:


 extends Node func rand(): var rrand = Vector2(rand_range(40, 760), rand_range(40, 540)) return rrand #   


Em seguida, salve-o e vá para as configurações do projeto: Project -> Project Settings -> AutoLoad . Selecionamos nosso script recém-criado, definimos um nome para ele, por exemplo, "_G" e retornamos à função "spawn_coins ()" para ajustar levemente o prazo, substituindo-o pelo seguinte código:


  ... c.position = _G.rand() 

Agora vale a pena verificar o que aconteceu colocando "spawn_coins ()" no bloco "_ready ()" e executando o aplicativo na F5. E não se esqueça de selecionar Main.tscn como cenário principal; se, por algum motivo, você cometeu um erro na escolha, você pode alterar o cenário principal manualmente; para isso, acesse as configurações do projeto: General -> Run -> MainScene . Isso funciona? Então siga em frente.


Quantas moedas restam?


Vamos continuar. Em seguida, você precisa verificar quantas moedas restam para transferir o jogador para o próximo nível, dar a ele um pequeno “bônus” na forma de um aumento no tempo em 5 segundos e depois gerar novamente as moedas.


 func _process(delta): #   ?     ? if playing and $CoinContainer.get_child_count() == 0: #    level += 1 # ""   time_left += 5 #  spawn_coins() 

Interface do usuário


Toda a nossa interface será composta pelos seguintes elementos: indicador de pontuação, nível atual, hora, nome do jogo e um botão que acionará o lançamento do jogo. Crie uma cena ( HUD.tscn ) com um pai do CanvasLayer (permite desenhar uma interface do usuário no topo do campo de jogo). Olhando para o futuro, direi que não é muito conveniente gerenciar elementos da interface do usuário, pelo menos para mim, mas uma lista bastante ampla de elementos e desenvolvimento ativo inspiram um clima positivo no futuro brilhante para o desenvolvimento desse aspecto do mecanismo.



No Godot, existem os chamados "nós de controle" que permitem formatar automaticamente os elementos filhos em relação aos parâmetros especificados do pai. Cada tipo de "nós de controle" possui propriedades especiais que controlam como eles controlam a localização de seus descendentes. Um representante vívido desse tipo é o MarginContainer , que deve ser adicionado à cena. Usando Layout -> Top Wide estique-o na parte superior da janela e, nas propriedades deste objeto, na seção Margin , especifique os recuos das bordas: esquerda, superior e direita. MarginContainer deve ter três Label filhos com os seguintes nomes: ScoreLabel , LevelLabel e TimeLabel . Adicione-os à cena. Usando a propriedade Align , faça-os alinharem à esquerda, centro e direita. Resta adicionar outro Label ( Messagelabel ), colocando-o no centro, todos também com a ajuda de Layout , e um pouco mais abaixo, coloque o botão ( StartButton ).



Agora vamos tornar a interface responsiva, precisamos atualizar o tempo, o número de moedas coletadas e destacar o nível atual. Adicione um script para o nó HUD .


 extends CanvasLayer signal start_game func update_score(value): $MarginContainer/ScoreLabel.text = str(value) func update_level(value): if len(str(value)) == 1: $MarginContainer/TimeLabel.text = "0: 0" + str(value) else: $MarginContainer/TimeLabel.text = "0: " + str(value) func update_timer(value): $MarginContainer/TimeLabel.txt = str(value) 

Para MessageLabel , precisamos de um temporizador para alterar o texto da mensagem por um curto período. Adicione um nó Timer e substitua seu nome por MessageTimer . No inspetor, defina o tempo de espera para 2 segundos e marque a caixa de seleção no campo One Shot . Isso garante que o timer seja executado apenas uma vez na inicialização.


 func show_message(text): $MessageLabel.text = text $MessageLabel.show() $MessageTimer.start() 

Conecte o sinal timeout() ao "MessageTimer" e adicione o seguinte:


 func _on_MessageTimer_timeout(): $MessageLabel.hide() 

Na guia "Nó" do StartButton , conecte o sinal pressed() . Quando você clica no botão StartButton ele deve desaparecer junto com o MessageLabel e enviar um sinal para a cena principal, onde subsequentemente o interceptaremos com sucesso ao mesmo tempo, deslizando a função para executar - "new_game ()". Implementamos isso usando o código abaixo. Não se esqueça do botão na propriedade Text para definir qualquer chamada de texto para iniciar o jogo.


 func _on_StartButton_pressed(): $StartButton.hide() $MessageLabel.hide() emit_signal("start_game") 

Para finalmente terminar com a interface, escreveremos a última função final - a função de exibir uma mensagem sobre o final do jogo. Nesta função, precisamos que a inscrição "Game Over" seja exibida por não mais que dois segundos e depois desapareça, o que é possível graças à função "show_message ()". No entanto, você deve mostrar novamente o botão Iniciar de um novo jogo, assim que uma mensagem informando que o jogo acabou. yield() pausará a execução da função até que um sinal do MessageTimer seja recebido e, recebendo um sinal do MessageTimer sobre sua execução, a função continuará sendo executada, retornando ao seu estado original para que possamos iniciar um novo jogo novamente.


 func show_game_over(): show_message("Game Over") yield($MessageTimer, "timeout") $StartButton.show() $MessageLabel.text = "LIKE COINS!" $MessageLabel.show() 

Terminando?


Vamos configurar o feedback entre o HUD e o Main . Adicione a cena HUD à cena principal e conecte o sinal GameTimer através de timeout() na cena principal, adicionando o seguinte:


 func _on_GameTimer_timeout(): time_left -= 1 #  $HUD.update_timer(time_left) #   if time_left <= 0: game_over() #     

Em seguida, conecte os sinais de pickup() e die() do player.


 func _on_Player_pickup(): score += 1 $HUD.update_score(score) func _on_Player_die(): game_over() 

No final do jogo, várias outras coisas devem acontecer que não devem ser esquecidas. Escreva o código a seguir e eu explicarei.


 func game_over(): playing = false $GameTimer.stop() for coin in $CoinContainer.get_children(): coin.queue_free() $HUD.show_game_over() $Player.die() 

Esta função irá parar o jogo e, em seguida, as moedas restantes serão show_game_over() e as moedas restantes serão show_game_over() , e show_game_over() será chamado para o HUD . O próximo passo é iniciar a animação e interromper o processo de execução do nó Player .


Por fim, você deve ativar o StartButton , que deve estar conectado à função new_game() . Clique no nó HUD e, na caixa de diálogo de conexão, clique em Make Function to Off (isso impedirá a criação de uma nova função) e, no campo Method In Node , especifique o nome da função a ser conectada - new_game . Isso conectará o sinal a uma função existente, em vez de criar uma nova.


O toque final é remover new_game() da função _ready() e adicionar as duas linhas a seguir à função new_game() :


 ... $HUD.update_score(score) $HUD.update_timer(time_left) 

Agora podemos dizer com confiança que o jogo está pronto, agora é bastante "jogável", mas sem efeitos. Vamos considerar o último no próximo artigo, dedicando enorme atenção a várias "decorações" para diversificar a jogabilidade e explorar ainda mais as possibilidades de "Godot". Portanto, não esqueça de seguir o lançamento dos artigos. Boa sorte

Source: https://habr.com/ru/post/pt430800/


All Articles