UC02841

Desenvolvimento de videojogos

Unity / Godot — primeiros jogos 2D/3D

Curso profissional · 50h · Game dev

Plano

  1. Game dev — landscape
  2. Setup Unity / Godot
  3. Cena, GameObjects, componentes
  4. Scripting (C# em Unity, GDScript em Godot)
  5. Física e colisões
  6. Input do jogador
  7. UI e sistemas de jogo
  8. Audio
  9. Build e distribuição

Bloco 1 · Game dev hoje

Motor de jogo escolhido

  • Unity — C#, indústria standard, todos os dispositivos.
  • Godot — GDScript (Python-like), open source, leve.
  • Unreal — C++, AAA, blueprints visuais.
  • GameMaker — GML, 2D, indie.

Esta UC: Godot (gratuito, fácil) ou Unity (industry standard). Vamos focar Godot.

Por que Godot

  • Open source (MIT) — sem royalties.
  • Editor leve (50MB vs 1GB+ Unity).
  • GDScript simples (Python-like).
  • 2D first-class (não 3D adaptado a 2D).
  • Cross-platform (Win/Mac/Linux/Android/iOS/Web).
  • Cresceu muito desde Unity drama de 2023.

Setup

Download em godotengine.org.

  • Godot 4.x (versão moderna).
  • Standard ou .NET (se queres C#).

Executável único — abre e usa.

IDE integrada com editor de script e debugger.

Bloco 2 · Conceitos fundamentais

Cena e Nodes

  • Scene: organização de uma "cena" (menu, nível, HUD).
  • Node: bloco fundamental — tudo é um Node.
  • Tree: hierarquia parent-child de nodes.
Main (Node2D)
├── Player (CharacterBody2D)
│   ├── Sprite2D
│   └── CollisionShape2D
├── Coins (Node2D)
│   ├── Coin1 (Area2D)
│   └── Coin2 (Area2D)
└── UI (CanvasLayer)
    └── Score (Label)

Node types principais (2D)

  • Node2D: base 2D (posição, rotação, escala).
  • Sprite2D: imagem.
  • AnimatedSprite2D: spritesheet animado.
  • CharacterBody2D: corpo com física controlado.
  • RigidBody2D: corpo físico simulado.
  • StaticBody2D: corpo fixo (chão, paredes).
  • Area2D: detecta entrada/saída (sem colisão sólida).
  • CollisionShape2D: forma da colisão.
  • Label, Button: UI.

Bloco 3 · GDScript

Hello GDScript

extends Node2D   # tipo de node que estende

func _ready():
    print("Cena pronta!")

func _process(delta):
    # chamado todos os frames
    pass

Atalhos:

  • _ready(): chamado quando node entra na cena.
  • _process(delta): cada frame (delta = segundos desde último).
  • _physics_process(delta): cada physics tick (60Hz fixo).
  • _input(event): input.

Variáveis e tipos

var nome = "Player"           # auto-tipo
var vida: int = 100           # explicit
var velocidade: float = 200.0
var ativo: bool = true
var posicao: Vector2 = Vector2(100, 200)

const MAX_VIDA = 100          # constante

@export var velocidade_max: float = 300.0   # editável no editor

@export torna variável visível e editável no inspector do Godot.

Controlo

# If
if vida <= 0:
    morrer()
elif vida < 30:
    avisar()
else:
    continuar()

# Match (switch)
match estado:
    "idle": idle()
    "moving": mover()
    _: pass    # default

# Loops
for i in range(10):
    print(i)

for inimigo in inimigos:
    inimigo.atacar()

while vida > 0:
    sofrer_dano(1)

Bloco 4 · Player movement

CharacterBody2D + movimento

extends CharacterBody2D

const SPEED = 300.0
const JUMP_VELOCITY = -400.0

func _physics_process(delta):
    # Gravidade
    if not is_on_floor():
        velocity.y += 980 * delta
    
    # Jump
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY
    
    # Movimento horizontal
    var direction = Input.get_axis("ui_left", "ui_right")
    if direction:
        velocity.x = direction * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
    
    move_and_slide()

Input actions

No Project Settings → Input Map:

  • ui_left ← seta esquerda + A
  • ui_right ← seta direita + D
  • ui_accept ← espaço + Enter

Vantagem: dissocia teclas do código (gamepad, rebinding).

Input.is_action_pressed("ui_left")        # enquanto premido
Input.is_action_just_pressed("ui_accept") # apenas no frame
Input.is_action_just_released("attack")   # apenas no frame

Bloco 5 · Colisões e sinais

Area2D detecta entrada

extends Area2D
# Coin que desaparece quando player entra

func _ready():
    body_entered.connect(_on_body_entered)

func _on_body_entered(body):
    if body.name == "Player":
        Game.score += 10
        queue_free()   # apaga o coin

Sinais = sistema de eventos do Godot (como callbacks).

Sinais customizados

extends Node

signal vida_mudou(nova_vida)
signal morreu

var vida = 100

func sofrer_dano(d):
    vida -= d
    vida_mudou.emit(vida)
    if vida <= 0:
        morreu.emit()

# Em outro node:
player.vida_mudou.connect(_atualizar_hud)
player.morreu.connect(_game_over)

Bloco 6 · Audio

Reproduzir som

# AudioStreamPlayer node na cena
@onready var som_pulo = $SomPulo

func pular():
    velocity.y = JUMP_VELOCITY
    som_pulo.play()

Tipos:

  • AudioStreamPlayer: sem posicionamento (UI, música).
  • AudioStreamPlayer2D: com posição (sons 2D).

Formatos: WAV (sem compressão), OGG (compressão).

Bloco 7 · UI

Canvas Layer + Labels

UI (CanvasLayer)
├── ScoreLabel (Label)
├── LivesLabel (Label)
└── GameOverPanel (Panel)
    ├── Title (Label)
    └── RestartButton (Button)
extends CanvasLayer

@onready var score_label = $ScoreLabel

func _ready():
    Game.score_mudou.connect(_atualizar_score)

func _atualizar_score(novo):
    score_label.text = "Score: " + str(novo)

Singletons (Autoload)

Para estado global (score, settings, save game):

game.gd:

extends Node

signal score_mudou(novo)

var score = 0:
    set(value):
        score = value
        score_mudou.emit(value)

var vida = 3

Project Settings → Autoload → adicionar game.gd como Game.

Agora Game.score acessível de qualquer lado.

Bloco 8 · Animação

AnimatedSprite2D + spritesheet

  1. Carregar imagem com várias frames.
  2. SpriteFrames resource — definir animações ("idle", "run", "jump").
  3. Em código:
@onready var sprite = $AnimatedSprite2D

func _physics_process(delta):
    if velocity.x != 0:
        sprite.play("run")
        sprite.flip_h = velocity.x < 0
    else:
        sprite.play("idle")

AnimationPlayer (Tweens)

# Animação programática
var tween = create_tween()
tween.tween_property(self, "modulate:a", 0.0, 1.0)   # fade out 1s
tween.tween_callback(queue_free)                      # apaga depois

Útil para UI animations, transições.

Bloco 9 · Build

Export

Project → Export → adicionar preset:

  • Windows Desktop (.exe)
  • Linux/X11 (.x86_64)
  • macOS (.app)
  • HTML5 (browser)
  • Android (.apk)
# Ou CLI
godot --export-release "Windows Desktop" build/jogo.exe

itch.io = melhor plataforma para distribuir jogos indie gratuitos / pay-what-you-want.

Otimização básica

  • Texturas apropriadas (não maiores que precisas).
  • Audio compressed (.ogg) para música.
  • Reusar resources (não criar 100 nodes iguais).
  • Pool de objects para tiros/inimigos.
  • queue_free() ao apagar, não free().

UC02841 · resumo

  • Godot = motor open source, leve, perfeito para começar.
  • Node tree = arquitectura fundamental.
  • GDScript = Python-like, fácil de aprender.
  • Signals = eventos para comunicação entre nodes.
  • CharacterBody2D + move_and_slide() para player.
  • Area2D + body_entered para colectáveis.
  • Autoloads para estado global.
  • AnimatedSprite2D para animações.
  • Export para múltiplas plataformas com 1 clique.
  • itch.io para distribuir o primeiro jogo!