Voltar para o Blog
Quest Log

Integrar Steam no Godot com GodotSteam: Guia Passo a Passo

Tela de computador mostrando um jogo indie feito na Godot com um popup de conquista desbloqueada no canto da tela

Aprenda a integrar seu jogo Godot com a Steam usando o GodotSteam: conquistas, saves na nuvem e overlay, com codigo GDScript tipado e passo a passo.

Se você está perto de lançar, provavelmente já descobriu uma verdade incômoda: a Godot pura não conversa com a Steam. Não existe um botão mágico para ligar conquistas, saves na nuvem ou o overlay. Para integrar seu jogo Godot com a Steam você precisa de uma ponte, e essa ponte se chama GodotSteam. Neste guia prático você vai instalar o addon, inicializar a API, desbloquear uma conquista, gravar um save na nuvem e ligar o overlay, tudo com GDScript tipado e sem enrolação.

Vamos ser honestos desde o começo: isto é conteúdo para quem já tem (ou está prestes a ter) uma página na Steam. Se você ainda está decidindo se vale a pena, dê um passo atrás e leia primeiro o guia completo de como publicar um jogo na Steam. Se você já está com o pé na porta do lançamento, siga em frente.

Por que a Godot pura não fala com a Steam

A Godot é uma engine de código aberto que não embute nenhuma tecnologia proprietária de terceiros. Se você ainda está conhecendo a engine a fundo, a página da Godot aqui na CursoGame.Dev reúne o panorama dela e os tutoriais que ajudam a chegar até aqui. A Steamworks, por outro lado, é a SDK proprietária da Valve que controla conquistas, nuvem, overlay, multiplayer e microtransações. As duas simplesmente não se conhecem de fábrica.

O GodotSteam resolve isso. Ele é um addon real, gratuito e de código aberto que embrulha a Steamworks SDK e a expõe como uma API amigável para o GDScript. Em vez de escrever C++ e lidar com ponteiros da SDK, você chama funções direto no seu script. Para a Godot 4, o GodotSteam funciona via GDExtension (instalável pela AssetLib ou direto do GitHub), e existem também builds precompiladas do editor caso você prefira uma versão já pronta com tudo integrado.

O que o GodotSteam te dá

Com o addon instalado e a API inicializada, você ganha acesso a praticamente tudo que a Steamworks oferece. Para quem está lançando um jogo indie solo ou de time pequeno, os recursos que realmente importam no dia um são:

  • Conquistas (achievements): aqueles popups de "conquista desbloqueada" que aumentam retenção e dão aquele gostinho de progresso.
  • Saves na nuvem (Steam Cloud): o jogador troca de máquina e o progresso vai junto, sem esforço dele.
  • Overlay: o Shift+Tab que abre o navegador da Steam, convites, screenshots e a lista de amigos por cima do seu jogo.
  • Estatísticas, ranks (leaderboards), Rich Presence e multiplayer para quando você quiser ir além do básico.

Neste guia focamos nos três primeiros, que são o mínimo esperado de qualquer jogo sério na plataforma.

Pré-requisitos: App ID e o App ID de teste 480

Aqui está o pré-requisito que pega muita gente de surpresa. Para usar qualquer recurso da Steam, você precisa de um App ID. E ter um App ID próprio significa ter uma página de loja na Steam, o que exige pagar a taxa do Steam Direct por jogo. Não existe atalho: sem página, sem App ID de verdade.

A boa notícia é que você não precisa esperar pela sua página para começar a programar. Durante o desenvolvimento, use o App ID 480, que é o Spacewar, o jogo de exemplo público da Valve. Com ele você inicializa a API, testa conquistas de exemplo, grava arquivos na nuvem e valida o overlay, tudo sem gastar um centavo. Quando sua página estiver aprovada, você troca o 480 pelo seu App ID real e configura tudo no backend do Steamworks.

Você também vai precisar do cliente Steam instalado e logado na mesma máquina onde roda o jogo. Sem a Steam aberta, o addon não tem com quem conversar.

Instalar o GodotSteam

Existem dois caminhos, e vale entender a diferença:

  1. GDExtension (recomendado para a Godot 4): você mantém o editor padrão da Godot e adiciona o GodotSteam como um plugin. Instale pela AssetLib de dentro do editor procurando por "GodotSteam GDExtension" ou baixe o pacote do repositório oficial no GitHub e coloque na pasta addons/ do seu projeto.
  2. Editor precompilado: uma versão do editor da Godot que já vem com o GodotSteam embutido. É mais direto, mas te prende àquela build específica.

Seja qual for o caminho, coloque também os arquivos da biblioteca da Steam (a steam_api correspondente ao seu sistema) junto do executável, e crie um arquivo steam_appid.txt com o número do App ID (durante o desenvolvimento, 480) na raiz do projeto, ao lado do executável de teste. É esse arquivo que diz para a Steam qual jogo você está fingindo ser.

Como a API do GodotSteam evolui entre versões, confira sempre os nomes exatos dos métodos na documentação oficial do GodotSteam. Os exemplos abaixo são realistas e ilustrativos, mas o nome de uma função pode ter mudado na versão que você baixou.

Curso de Godot
Quer fazer seu primeiro jogo na Godot?

No curso de Godot da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Godot
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

Inicializar a API e rodar os callbacks

Todo o resto depende deste passo. Você inicializa a Steam uma vez, no começo do jogo, e a partir daí precisa rodar os callbacks a cada frame para que a Steam entregue as respostas assíncronas (conquistas confirmadas, dados da nuvem chegando, etc.).

Um bom lugar para isso é um autoload (singleton) que fica vivo o jogo inteiro. Repare no GDScript tipado, do jeito que deve ser:

extends Node

var steam_habilitado: bool = false
var steam_id: int = 0
var nome_jogador: String = ""

func _ready() -> void:
    var resposta: Dictionary = Steam.steamInitEx()
    if resposta["status"] == 0:
        steam_habilitado = true
        steam_id = Steam.getSteamID()
        nome_jogador = Steam.getPersonaName()
        print("Steam iniciada para: ", nome_jogador)
    else:
        steam_habilitado = false
        push_warning("Falha ao iniciar a Steam: " + str(resposta["verbal"]))

func _process(_delta: float) -> void:
    if steam_habilitado:
        Steam.run_callbacks()

Alguns pontos importantes:

  • Steam.steamInitEx() devolve um dicionário com o status da inicialização. Versões mais antigas usavam Steam.steamInit(), que retorna um booleano mais simples. Confirme qual existe na sua build.
  • Steam.run_callbacks() precisa rodar todo frame. Se você esquecer disso, nada assíncrono funciona: conquistas parecem travar, dados da nuvem não chegam. É o erro silencioso número um.
  • Guarde o steam_habilitado e cheque antes de qualquer chamada. Assim o jogo não quebra quando alguém roda a build fora da Steam.

Desbloquear uma conquista

Antes de qualquer linha de código, entenda a regra de ouro: a conquista precisa existir no backend do Steamworks antes de você conseguir desbloquear ela. Você entra no painel de parceiro, cria a conquista com um ID (por exemplo ACH_PRIMEIRO_CHEFAO), define ícone e nome, publica, e só então o código consegue mexer nela. O código não cria conquistas, ele apenas dispara as que já foram definidas.

Com a conquista já cadastrada, desbloquear é um passo duplo. Você marca a conquista e, logo em seguida, envia para os servidores:

func desbloquear_conquista(id_conquista: String) -> void:
    if not steam_habilitado:
        return

    var conquistada: bool = Steam.getAchievement(id_conquista)["achieved"]
    if conquistada:
        return

    Steam.setAchievement(id_conquista)
    Steam.storeStats()
    print("Conquista enviada: ", id_conquista)

O detalhe que derruba muita gente é o Steam.storeStats(). O setAchievement só marca a conquista localmente; é o storeStats que empurra tudo para a Valve e faz o popup aparecer. Sem ele, a conquista fica presa e nunca desbloqueia de verdade.

Aqui vale um plano maior: em vez de espalhar chamadas de setAchievement pelo projeto inteiro, centralize a lógica de progresso e desbloqueio num único gerenciador. Se você quer montar isso direito, com condições, contadores e persistência, vale estudar como construir um sistema de conquistas robusto que fale com a Steam por uma camada só.

Salvar na nuvem com Steam Cloud

O Steam Cloud sincroniza arquivos entre as máquinas do jogador automaticamente, desde que você configure a cota e os padrões de arquivo no backend do Steamworks. Do lado do código, a Steam expõe uma "gaveta" de arquivos onde você grava e lê bytes.

Gravar um save é escrever um buffer de bytes com um nome de arquivo:

func salvar_na_nuvem(nome_arquivo: String, dados: Dictionary) -> bool:
    if not steam_habilitado:
        return false

    var json_texto: String = JSON.stringify(dados)
    var buffer: PackedByteArray = json_texto.to_utf8_buffer()
    var sucesso: bool = Steam.fileWrite(nome_arquivo, buffer, buffer.size())
    return sucesso

E ler de volta na hora de carregar:

func carregar_da_nuvem(nome_arquivo: String) -> Dictionary:
    if not steam_habilitado:
        return {}

    if not Steam.fileExists(nome_arquivo):
        return {}

    var tamanho: int = Steam.getFileSize(nome_arquivo)
    var buffer: PackedByteArray = Steam.fileRead(nome_arquivo, tamanho)
    var json_texto: String = buffer.get_string_from_utf8()

    var json: JSON = JSON.new()
    var erro: int = json.parse(json_texto)
    if erro != OK:
        return {}
    return json.data

Repare que o save da nuvem é, na prática, o seu save local com um destino diferente. Se o seu jogo ainda não tem uma estrutura de save decente, resolva isso primeiro: monte um sistema de save e load em Godot bem organizado e depois só troque a camada de escrita para apontar para o Steam.fileWrite. Uma boa arquitetura de save separa "o que salvar" de "onde salvar", e é justamente essa separação que faz a nuvem ser plugável.

Uma dica prática: mantenha o save local funcionando mesmo com a nuvem ativa. Assim, se a Steam estiver offline ou o addon não inicializar, o jogador não perde progresso.

Checklist de erros comuns

A integração com a Steam falha quase sempre pelos mesmos motivos, e são todos silenciosos: nada quebra, só não funciona. Quando algo não acontece como esperado, percorra esta lista antes de culpar o código.

  • A API não inicializa. Confira se o cliente Steam está aberto e logado, se o steam_appid.txt está na raiz ao lado do executável com o número certo (480 durante o desenvolvimento) e se os arquivos da steam_api do seu sistema estão junto do binário. Sem qualquer um desses três, o steamInitEx() volta com status de erro.
  • A conquista não desbloqueia. Ou ela não existe no backend do Steamworks com o ID exato que você passou, ou você esqueceu o Steam.storeStats() depois do setAchievement. São os dois suspeitos de sempre.
  • Nada assíncrono responde. Conquistas que parecem travar, dados da nuvem que não chegam: quase sempre é o Steam.run_callbacks() que não está rodando todo frame. Confirme que ele está num _process de um autoload vivo o jogo inteiro.
  • O save da nuvem não sincroniza entre máquinas. A escrita local com fileWrite funciona, mas a cota e os padrões de arquivo do Steam Cloud precisam estar configurados no backend. Sem isso, o arquivo fica só na máquina.
  • Tudo funciona no editor e quebra no amigo que testou. Ele provavelmente rodou a build fora da Steam. É por isso que você checa steam_habilitado antes de cada chamada: o jogo tem que rodar normalmente mesmo sem a Steam atrás.

Essa disciplina de testar cada recurso isolado, com a Steam aberta, é o que evita a pior situação possível, que é descobrir um desses problemas com o jogo já publicado e o jogador reclamando que a conquista não pingou.

Overlay e próximos passos

O overlay é a parte mais fácil, porque na maior parte dos casos ele simplesmente funciona. Uma vez que a API foi inicializada com sucesso e o jogo roda pela Steam, o Shift+Tab já abre a interface por cima do seu jogo, com convites, screenshots e navegador. Você não precisa escrever quase nada.

O cuidado real está no ciclo de vida do jogo. Quando o overlay abre, muitos jogos pausam automaticamente. Se o seu tem gameplay em tempo real, escute o evento de ativação do overlay e pause o jogo enquanto ele estiver aberto, para o jogador não morrer olhando a lista de amigos:

func _ready() -> void:
    Steam.overlay_toggled.connect(_ao_alternar_overlay)

func _ao_alternar_overlay(aberto: bool, _user_initiated: bool, _app_id: int) -> void:
    get_tree().paused = aberto

Confirme o nome exato do sinal (overlay_toggled ou similar) na documentação da sua versão do GodotSteam, porque esse é o tipo de detalhe que muda entre releases.

Com conquistas, nuvem e overlay no lugar, você tem o pacote mínimo que a comunidade da Steam espera. Os próximos passos naturais, quando sobrar fôlego, são leaderboards para ranquear os jogadores, Rich Presence para mostrar "está no chefão final" na lista de amigos, e a Steam Input para dar suporte decente a controles.

O Rich Presence, por exemplo, é um dos mais baratos de adicionar e dá um acabamento imediato. Você define pares de chave e valor que aparecem na lista de amigos, atualizando conforme o estado do jogo:

func atualizar_presenca(situacao: String) -> void:
    if not steam_habilitado:
        return
    # "steam_display" aponta para um token definido no backend;
    # "situacao" é a chave que esse token exibe.
    Steam.setRichPresence("steam_display", "#Status_Jogando")
    Steam.setRichPresence("situacao", situacao)

Mas nada disso é obrigatório para lançar. Se o seu jogo for moddável, vale conhecer também como funciona o Steam Workshop, outro degrau de integração que estende a vida do jogo por anos.

O mais importante: teste com o App ID 480 durante todo o desenvolvimento, valide cada recurso com a Steam aberta e logada, e só troque pelo seu App ID real quando sua página estiver aprovada e as conquistas cadastradas no backend. Assim você chega no dia do lançamento com a integração já rodando redonda, em vez de descobrir problemas com o jogo já na vitrine.

Perguntas frequentes

Preciso pagar para testar a integração com a Steam?

Para publicar de verdade, sim: você precisa de um App ID proprio, o que exige uma pagina na Steam e o pagamento da taxa do Steam Direct. Mas para testar o codigo durante o desenvolvimento voce pode usar o App ID 480 (Spacewar), o jogo de exemplo publico da Valve, sem pagar nada.

O GodotSteam funciona com Godot exportado para o navegador?

Nao. A Steamworks SDK e desktop-only (Windows, macOS e Linux). Builds para navegador (HTML5/Web) ou celular nao conseguem falar com o cliente Steam, entao conquistas, nuvem e overlay so funcionam nas exportacoes para desktop.

Por que minha conquista nao desbloqueia?

Os dois motivos mais comuns: a conquista nao foi criada no backend do Steamworks (o codigo so pode desbloquear conquistas que ja existem la, com o mesmo ID exato) ou voce esqueceu de chamar Steam.storeStats() depois de Steam.setAchievement(). Sem o storeStats, o desbloqueio nao e enviado para os servidores da Valve.

O GodotSteam substitui a Steamworks SDK?

Nao, ele a embrulha. O GodotSteam e um addon que expoe as funcoes da Steamworks SDK para o GDScript. Voce ainda depende da Steam rodando na maquina e de um App ID valido; o addon so faz a ponte entre a Godot e a SDK.

Preciso da conta do jogador logada para testar?

Sim. A integracao depende do cliente Steam aberto e com uma conta logada na mesma maquina. Ao rodar pelo editor com o App ID 480, a Steam trata o jogo de teste como se fosse o Spacewar, entao voce consegue exercitar conquistas e nuvem antes mesmo de ter sua propria pagina.