Introdução à Godot Engine
A Godot Engine é uma poderosa e flexível ferramenta de desenvolvimento de jogos de código aberto. Criada inicialmente por Juan Linietsky e Ariel Manzur, ela foi lançada em 2014 e rapidamente ganhou popularidade devido à sua acessibilidade e recursos robustos. Projetada para ser uma alternativa viável a outras engines de jogos proprietárias, a Godot se destaca por ser gratuita e por oferecer total liberdade aos desenvolvedores para criar, modificar e distribuir seus jogos sem restrições de licenciamento.
Características principais
editar- Multiplataforma: A Godot permite o desenvolvimento de jogos para diversas plataformas, incluindo Windows, macOS, Linux, Android, iOS, sendo até mesmo viável exportar jogos para a Web. Isso facilita a publicação dos jogos em múltiplos dispositivos com pouco esforço adicional.
- Suporte a 2D e 3D: A engine possui ferramentas integradas para o desenvolvimento tanto de jogos 2D quanto 3D.
- Sistema de Scripts: A Godot utiliza sua própria linguagem de script, chamada GDScript, relativamente semelhante à Python. Além disso, também suporta outras linguagens como C#, VisualScript e C++ para quem prefere uma abordagem diferente ou mais performance.
- Cenas e Nós: A arquitetura da Godot é baseada em cenas e nós, em que cada cena é composta por uma hierarquia de nós. Esta estrutura modular permite uma grande flexibilidade e reutilização de componentes, facilitando a organização e o desenvolvimento do jogo. Esse aspecto será aprofundado mais à frente no tutorial.
- Desempenho: A Godot é leve e eficiente, projetada para rodar bem sem sacrificar a qualidade gráfica ou a complexidade do jogo.
Instalação da Godot
editarSiga esse passo-a-passo para instalar a Godot na sua máquina:
- Acesse a página de downloads da Godot 4
- Faça o download clicando no botão azul Godot Engine
- Salve em algum lugar que você vai se lembrar
- Extraia os arquivos do
.zip
que foi baixado
- Abra o executável
Interface da Godot
editarEstes são os pontos principais sobre a interface da Godot:
- Barra de Menus: Localizada no topo, contém menus principais como "Scene", "Project", "Debug", "Help", etc., oferecendo acesso rápido a diversas funcionalidades e configurações do projeto.
- Viewport: A área central onde os desenvolvedores podem visualizar e editar suas cenas em 2D ou 3D. Inclui ferramentas para navegação, zoom, e seleção de objetos.
- Scene Dock: À esquerda, exibe a hierarquia de nós da cena atual, permitindo adicionar, remover e organizar nós facilmente.
- FileSystem Dock: À esquerda, abaixo do Scene Dock, mostra a estrutura de arquivos do projeto, facilitando o acesso a recursos como scripts, imagens, e cenas.
- Inspector: À direita, exibe as propriedades do nó ou recurso atualmente selecionado, permitindo ajustes detalhados e específicos.
- Node Dock: À direita, abaixo do Inspector, exibe os nós filhos e as conexões de sinais do nó selecionado, ajudando na organização e interação entre os nós.
- Script Editor: Localizado na parte inferior ou como uma aba separada, é um editor de código integrado com destaque de sintaxe, auto-complete, e ferramentas de depuração para escrita de scripts em GDScript, C#, ou outras linguagens suportadas.
- Animation Player: Um editor de animações integrado que permite criar e editar animações para objetos, ajustando propriedades ao longo do tempo.
- Output Panel: Localizado na parte inferior, exibe mensagens de log, erros, e outras informações úteis durante o desenvolvimento e depuração.
- Toolbar: Contém botões para operações comuns como salvar, rodar o projeto, mudar entre modos 2D e 3D e ajustar o layout da interface.
- Asset Library: Provém acesso direto a uma biblioteca de recursos externos que podem ser integrados ao projeto, incluindo plugins, assets gráficos e scripts.
- Editor Settings: Permite personalizar a interface do editor, incluindo temas, atalhos de teclado, e outras preferências do usuário.
Esquema de nós
editarO sistema de nós (nodes) da Godot consiste no núcleo da sua arquitetura de desenvolvimento, responsável por oferecer uma maneira flexível e modular de criar jogos e aplicações. Neste esquema, tudo no jogo ou aplicação é um nó, sendo esses nós organizados em uma árvore de cena (scene tree). Cada nó possui uma funcionalidade específica e pode ser combinado com outros para criar comportamentos complexos. Abaixo está uma descrição detalhada desse sistema.
Nó Básico (Node)
editar- O nó básico é a unidade fundamental de construção no Godot.
- Serve como a classe base para todos os outros tipos de nós.
- Pode ser adicionado à árvore de cena e possui métodos para gerenciamento de hierarquia, sinais e grupos.
Cena (Scene)
editar- Uma cena consiste em uma coleção de nós organizados em uma hierarquia.
- Pode ser salva em arquivos
.tscn
ou.scn
, que podem ser instanciados em outras cenas, permitindo a reutilização e modularidade. - Uma cena típica pode representar um personagem, um nível de jogo, uma interface de usuário, etc.
Mini-projeto
editarNesta seção, será descrito o processo de criação de um projeto básico em Godot, com o objetivo de familiarizar o leitor com a engine e suas funcionalidades principais.
Mostrando alguma coisa na tela
editar- Crie uma cena nova chamada
Mundo
com rootNode2D
.
- Salve a cena com nome
mundo.tscn
.
- Adicione um nó
Sprite
como filho deMundo
.
- Arraste o
icon.png
para a aba deTexture
doSprite
.
- Execute o programa.
Movimentando a coisa na tela de forma não interativa
editar- Renomeie o nó
Sprite2D
paraJogador
- Adicione um script no nó
Jogador
chamadojogador.gd
extends Sprite2D func _ready() -> void: pass func _process(delta: float) -> void: self.position.x += 10.0;
- Rode o jogo e veja ele se movendo!
- Devemos usar o valor de `delta` da função para que o movimento passe a ser independente do fps do jogo
extends Sprite2D func _ready() -> void: pass func _process(delta: float) -> void: self.position.x += 200.0 * delta;
- Se você rodar o jogo agora, provavelmente não vai notar muita diferença, mas agora o movimento está acontecendo de forma independente do fps
- Podemos extrair também o valor 200 em uma variável exportada no editor, para facilidade de modificação
extends Sprite2D @export var velocidade := 200.0; func _ready() -> void: pass func _process(delta: float) -> void: self.position.x += velocidade * delta;
Movimentando a coisa na tela de forma interativa
editar- Navegue para
Project > Project Settings > Input Map
- Adicione ações
jogador_esquerda
jogador_direita
jogador_cima
jogador_baixo
- Adicione teclas para essas ações
- Atualize o script
jogador.gd
para utilizar os novos inputs
extends Sprite2D @export var velocidade := 200.0; func _physics_process(delta: float) -> void: var direcao := Vector2.ZERO; if Input.is_action_pressed("jogador_direita"): direcao.x += 1; if Input.is_action_pressed("jogador_esquerda"): direcao.x += -1; if Input.is_action_pressed("jogador_baixo"): direcao.y += 1 if Input.is_action_pressed("jogador_cima"): direcao.y += -1; self.position += direcao.normalized() * velocidade * delta;
- Podemos extrair o código de pegar a direção do movimento para sua própria função
extends Sprite2D @export var velocidade := 200.0; var direcao := Vector2.ZERO; func pega_input() -> void: direcao = Vector2.ZERO; if Input.is_action_pressed("jogador_direita"): direcao.x += 1; if Input.is_action_pressed("jogador_esquerda"): direcao.x += -1; if Input.is_action_pressed("jogador_baixo"): direcao.y += 1; if Input.is_action_pressed("jogador_cima"): direcao.y += -1; func _physics_process(delta: float) -> void: pega_input(); self.position += direcao.normalized() * velocidade * delta;
Pegando input de controle
editar- Adicione os analógicos do controle para as ações de input definidas no tópico anterior
- Atualize o deadzone das ações
- Atualize o código de input do
jogador.gd
para utilizar as funcionalidade do analógico
extends Sprite2D @export var velocidade := 200.0; var direcao := Vector2.ZERO; func pega_input() -> void: direcao = Vector2.ZERO; direcao.x = Input.get_action_strength("jogador_direita") - Input.get_action_strength("jogador_esquerda"); direcao.y = Input.get_action_strength("jogador_baixo") - Input.get_action_strength("jogador_cima"); func _physics_process(delta: float) -> void: pega_input(); if direcao.length_squared() > 1: # Isso é para fazer o movimento ter sensibilidade com o analógico direcao = direcao.normalized(); self.position += direcao * velocidade * delta;
Adicionando colisões
editar- Vamos criar uma nova cena chamada
Jogador
baseado em umCharacterBody2D
- Adicione um
Sprite
como filho deJogador
e coloque a imagemicon.png
no campoTexture
- Adicione um
CollisionShape2D
como filho deJogador
- Adicione uma
RectangleShape2D
ao campoShape
deCollisionShape2D
e redimensione ela para ter o mesmo tamanho daSprite
- Arraste o script
jogador.gd
do explorador de arquivos até o nóJogador
- Edite o script para funcionar com um
KinematicBody2D
extends Sprite2D @export var velocidade := 200.0; var direcao := Vector2.ZERO; func pega_input() -> void: direcao = Vector2.ZERO; direcao.x = Input.get_action_strength("jogador_direita") - Input.get_action_strength("jogador_esquerda"); direcao.y = Input.get_action_strength("jogador_baixo") - Input.get_action_strength("jogador_cima"); func _physics_process(delta: float) -> void: pega_input(); if direcao.length_squared() > 1: # Isso é para fazer o movimento ter sensibilidade com o analógico direcao = direcao.normalized(); self.position += direcao * velocidade * delta;
- Delete o nó
Jogador
da cenaWorld
- Adicione 3
StaticBody2D
à cenaWorld
e adicioneCollisionShape2D
s a cada um dosStaticBody2D
- Arraste o arquivo
jogador.tscn
do explorador de arquivos para a árvore de nós da cenaWorld
para instanciar um jogador
- Perceba que, se rodar o jogo agora, as colisões não vão ocorrer ainda. Para isso, precisamos fazer mais algumas alterações no código do jogador
extends CharacterBody2D @export var velocidade := 200.0; var direcao := Vector2.ZERO; func pega_input() -> void: direcao = Vector2.ZERO; direcao.x = Input.get_action_strength("jogador_direita") - Input.get_action_strength("jogador_esquerda"); direcao.y = Input.get_action_strength("jogador_baixo") - Input.get_action_strength("jogador_cima"); func _physics_process(_delta: float) -> void: pega_input(); if direcao.length_squared() > 1: # Isso é para fazer o movimento ter sensibilidade com o analógico direcao = direcao.normalized(); velocity = direcao * velocidade; move_and_slide();
Projeto final: Jogo de Plataforma
editarA partir do mini-projeto feito anteriormente, será desenvolvido um jogo de plataforma básico que irá adicionar conceitos mais complexos na parte de programação.
Modificando o movimento
editar- Na movimentação de plataforma, o jogador se movimenta com as setas apenas da esquerda para a direita
extends CharacterBody2D @export var velocidade := 200.0; var direcao := Vector2.ZERO; func pega_input() -> void: direcao = Vector2.ZERO; direcao.x = Input.get_action_strength("jogador_direita") - Input.get_action_strength("jogador_esquerda"); func _physics_process(_delta: float) -> void: pega_input(); if direcao.length_squared() > 1: direcao = direcao.normalized(); velocity = direcao * velocidade; move_and_slide();
Mudando o mundo
editar- Precisamos atualizar também a cena
World
para representar melhor um jogo de plataforma- Modifique os
StaticBody2D
s da cena para melhor refletir um jogo de plataforma
- Modifique os
Adicionando gravidade
editar- Para um jogo de plataforma, também é necessário que haja gravidade
- Isso requer algumas modificações no código do jogador
extends CharacterBody2D @export var gravidade := 20.0; @export var velocidade := 200.0; var direcao := Vector2.ZERO; func pega_input() -> void: direcao = Vector2.ZERO; direcao.x = Input.get_action_strength("jogador_direita") - Input.get_action_strength("jogador_esquerda"); func _physics_process(_delta: float) -> void: pega_input(); if direcao.length_squared() > 1: direcao = direcao.normalized(); velocity.x = direcao.x * velocidade; velocity.y += gravidade; self.move_and_slide();
Adicionando pulo
editar- Navegue para
Project > Project Settings > Input Map
e adicione uma nova açãojogador_pulo
- Mapeie alguma tecla/botão do controle para essa ação
- Modifique o código do jogador para utilizar o pulo
extends CharacterBody2D @export var gravidade := 20.0; @export var velocidade := 200.0; @export var velocidade_pulo := 700.0; var direcao := Vector2.ZERO; var acabou_de_pular: bool = false; func pega_input() -> void: direcao = Vector2.ZERO; direcao.x = Input.get_action_strength("jogador_direita") - Input.get_action_strength("jogador_esquerda"); acabou_de_pular = Input.is_action_just_pressed("jogador_pulo") and self.is_on_floor(); func _physics_process(_delta: float) -> void: pega_input(); if direcao.length_squared() > 1: direcao = direcao.normalized(); velocity.x = direcao.x * velocidade; velocity.y += gravidade; if acabou_de_pular: velocity.y = -velocidade_pulo; move_and_slide();
Adicionando câmera
editar- Adicione um nó
Câmera2D
à cenaJogador
como filho do nóJogador
- Marque a opção
Current
daCâmera2D
- Na aba
Smoothing
das propriedades daCâmera2D
, marque a opçãoEnabled
- Marque as opções
Drag Margin H Enabled
eDrag Margin V Enabled
- Na aba
Limit
:- Coloque o valor de -200 na propriedade
Left
- Coloque o valor de -100 na propriedade
Top
- Coloque o valor de 1224 na propriedade
Right
- Coloque o valor de 700 na propriedade
Bottom
- Marque a opção
Smoothed
- Note que esses valores devem ser modificados dependendo das condições do seu nível
- Você pode testar esses valores arrastando o nó
Jogador
na cenaWorld
no editor da Godot
- Você pode testar esses valores arrastando o nó
- Coloque o valor de -200 na propriedade
Adicionando tiros
editar- Crie uma cena chamada
Bala
que herde deArea2D
- Adicione um
Sprite
como filho deBala
e arraste o arquivoicon.png
para o campoTexture
- Adicione uma
CollisionShape2D
como filho deBala
e adicione umaRectangleShape2D
no campoShape
- Redimensione a colisão para ficar do mesmo tamanho do
Sprite
- Adicione um script
bala.gd
no nóBala
- Redimensione a colisão para ficar do mesmo tamanho do
class_name Bala extends Area2D @export var velocidade := 1000.0; var direcao := 1; func _physics_process(delta: float) -> void: self.position.x += direcao * velocidade * delta; if abs(self.position.x) > 1000: self.queue_free();
- Agora, navegue para
Project > Project Settings > Input Map
e adicione uma ação de input chamadajogador_tiro
- Adicione uma tecla/botão do controle para essa ação
- Modifique o código do jogador para utilizar a ação e atirar
extends CharacterBody2D @export var bala_cena: PackedScene; @export var gravidade := 20.0; @export var velocidade := 200.0; @export var velocidade_pulo := 700.0; var direcao := Vector2.ZERO; var olhando_para := 1; var acabou_de_pular: bool = false; var acabou_de_atirar: bool = false; func pega_input() -> void: direcao = Vector2.ZERO; direcao.x = Input.get_action_strength("jogador_direita") - Input.get_action_strength("jogador_esquerda"); acabou_de_pular = Input.is_action_just_pressed("jogador_pulo") and self.is_on_floor(); acabou_de_atirar = Input.is_action_just_pressed("jogador_tiro"); func _physics_process(_delta: float) -> void: pega_input(); if direcao.length_squared() > 1: direcao = direcao.normalized(); if abs(direcao.x) > 0: olhando_para = sign(direcao.x) as int; velocity.x = direcao.x * velocidade; velocity.y += gravidade; if acabou_de_pular: velocity.y = -velocidade_pulo; if acabou_de_atirar: var nova_bala := bala_cena.instantiate() as Bala; self.get_parent().add_child(nova_bala); nova_bala.direcao = olhando_para; nova_bala.global_position = self.global_position; move_and_slide();
Adicionando inimigo que toma dano
editar- Crie uma nova cena chamada
Inimigo
com base em umaArea2D
- Adicione um
Sprite
como filho do nóInimigo
e adicione a imagemicon.png
ao campoTexture
- No campo
Visibility
modifique a cor da opçãoModulate
para uma cor vermelha
- No campo
- Adicione uma
CollisionShape2D
como filha do nóInimigo
e adicione umaRectangleShape2D
no campoShape
- Redimensione o retângulo para ficar do mesmo tamanho do
Sprite
- Redimensione o retângulo para ficar do mesmo tamanho do
- Adicione alguns inimigos na cena
World
- Adicione um script
inimigo.gd
ao nóInimigo
- Selecione o nó
Inimigo
na árvore de nós, depois, na abaNode
do inspetor, conecte o sinalarea_entered
noInimigo
extends Area2D func _on_area_entered(area: Area2D) -> void: pass # Replace with function body.
- Agora, modifique o script `inimigo.gd` para adicionar lógica de vida e morte
extends Area2D var vida := 5; func _on_area_entered(area: Area2D) -> void: if area is Bala: vida -= 1; area.queue_free(); if vida == 0: morre(); func morre() -> void: print("inimigo morreu!!"); self.queue_free();
Conteúdo extra para estudo
editar- Jogo do GDQuest sobre Godot: Uma aula interativa feita pelo pessoal da GDQuest sobre os básicos de Godot e GDScript. Essencial para qualquer iniciante!