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 | editar código]- 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
[editar | editar código]Siga 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
.zipque foi baixado
- Abra o executável
Interface da Godot
[editar | editar código]Estes 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
[editar | editar código]O 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 | editar código]- 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 | editar código]- Uma cena consiste em uma coleção de nós organizados em uma hierarquia.
- Pode ser salva em arquivos
.tscnou.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
[editar | editar código]Nesta 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 | editar código]- Crie uma cena nova chamada
Mundocom rootNode2D.
- Salve a cena com nome
mundo.tscn.
- Adicione um nó
Spritecomo filho deMundo.
- Arraste o
icon.pngpara a aba deTexturedoSprite.
- Execute o programa.
Movimentando a coisa na tela de forma não interativa
[editar | editar código]- Renomeie o nó
Sprite2DparaJogador
- Adicione um script no nó
Jogadorchamadojogador.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 | editar código]- Navegue para
Project > Project Settings > Input Map
- Adicione ações
jogador_esquerdajogador_direitajogador_cimajogador_baixo
- Adicione teclas para essas ações
- Atualize o script
jogador.gdpara 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 | editar código]- 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.gdpara 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 | editar código]- Vamos criar uma nova cena chamada
Jogadorbaseado em umCharacterBody2D
- Adicione um
Spritecomo filho deJogadore coloque a imagemicon.pngno campoTexture
- Adicione um
CollisionShape2Dcomo filho deJogador
- Adicione uma
RectangleShape2Dao campoShapedeCollisionShape2De redimensione ela para ter o mesmo tamanho daSprite
- Arraste o script
jogador.gddo 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ó
Jogadorda cenaWorld
- Adicione 3
StaticBody2Dà cenaWorlde adicioneCollisionShape2Ds a cada um dosStaticBody2D
- Arraste o arquivo
jogador.tscndo explorador de arquivos para a árvore de nós da cenaWorldpara 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
[editar | editar código]A 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 | editar código]- 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 | editar código]- Precisamos atualizar também a cena
Worldpara representar melhor um jogo de plataforma- Modifique os
StaticBody2Ds da cena para melhor refletir um jogo de plataforma
- Modifique os
Adicionando gravidade
[editar | editar código]- 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 | editar código]- Navegue para
Project > Project Settings > Input Mape 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 | editar código]- Adicione um nó
Câmera2Dà cenaJogadorcomo filho do nóJogador
- Marque a opção
CurrentdaCâmera2D
- Na aba
Smoothingdas propriedades daCâmera2D, marque a opçãoEnabled
- Marque as opções
Drag Margin H EnabledeDrag 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ó
Jogadorna cenaWorldno editor da Godot
- Você pode testar esses valores arrastando o nó
- Coloque o valor de -200 na propriedade
Adicionando tiros
[editar | editar código]- Crie uma cena chamada
Balaque herde deArea2D
- Adicione um
Spritecomo filho deBalae arraste o arquivoicon.pngpara o campoTexture
- Adicione uma
CollisionShape2Dcomo filho deBalae adicione umaRectangleShape2Dno campoShape- Redimensione a colisão para ficar do mesmo tamanho do
Sprite - Adicione um script
bala.gdno 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 Mape 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 | editar código]- Crie uma nova cena chamada
Inimigocom base em umaArea2D
- Adicione um
Spritecomo filho do nóInimigoe adicione a imagemicon.pngao campoTexture- No campo
Visibilitymodifique a cor da opçãoModulatepara uma cor vermelha
- No campo
- Adicione uma
CollisionShape2Dcomo filha do nóInimigoe adicione umaRectangleShape2Dno 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.gdao nóInimigo
- Selecione o nó
Inimigona árvore de nós, depois, na abaNodedo inspetor, conecte o sinalarea_enterednoInimigo
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 | editar código]- 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!