Voltar para o Blog
Quest Log

Funcoes no GDScript: Parametros e Retorno Tipado

Tela do editor da Godot mostrando um script GDScript com varias funcoes tipadas de um jogo

Aprenda funcoes no GDScript do zero: como declarar com func, tipar parametros e retorno, usar valores padrao e chamar funcoes com exemplos de jogo.

Ferramenta:Godot

Funcoes no GDScript: Parametros e Retorno Tipado

Cedo ou tarde todo iniciante repara que esta copiando e colando as mesmas linhas de codigo pelo projeto: a conta de dano num lugar, quase igual em outro, a formula de pontuacao espalhada em tres cenas. E ai que as funcoes no GDScript entram e mudam o jogo. Uma funcao empacota uma tarefa dentro de um nome, e voce passa a chamar esse nome em vez de reescrever a logica. Neste texto eu mostro como declarar funcoes com func, tipar os parametros e o retorno, usar valores padrao e evitar os tropecos classicos de quem esta comecando, sempre com exemplos de jogo em Godot 4.

Se voce ainda esta assentando o basico de variaveis, condicionais e loops, vale primeiro passar pela logica de programacao para jogos, porque funcao e a peca que organiza tudo aquilo. Aqui a gente foca em transformar codigo repetido e desorganizado em blocos limpos e reaproveitaveis.

O que sao funcoes no GDScript

Uma funcao e um bloco de codigo com nome. Voce escreve a logica uma vez, da um nome pra ela, e depois chama esse nome toda vez que precisar rodar aquela logica. Em GDScript a palavra que abre uma funcao e func. A forma tipada, que e a que a gente usa aqui, declara o tipo de cada parametro e o tipo do que a funcao devolve.

func soma(a: int, b: int) -> int:
    return a + b

Vamos ler essa linha com calma, porque ela tem tudo que importa. func avisa que comeca uma funcao. soma e o nome. Dentro dos parenteses vao os parametros: a e b, os dois inteiros (int). A seta -> aponta para o tipo de retorno, aqui tambem int. O return entrega o resultado para quem chamou a funcao. Traduzindo pra fala: "a funcao soma recebe dois inteiros e devolve um inteiro".

Chamar essa funcao e simples. Voce escreve o nome dela e passa os valores entre parenteses:

var pontos: int = soma(10, 5)
print(pontos)   # 15

Os valores 10 e 5 que voce passa na chamada sao os argumentos. Dentro da funcao, eles viram os parametros a e b. Essa e a ponte: o mundo de fora manda dados para dentro da funcao pelos parametros, e a funcao manda o resultado de volta pelo return.

Retorno tipado: void quando nao devolve nada

Nem toda funcao precisa devolver um valor. Muitas so executam uma acao: tocar um som, mover um sprite, aplicar dano. Quando a funcao faz algo mas nao entrega nenhum resultado, o tipo de retorno e void.

var vida: int = 100

func curar(pontos: int) -> void:
    vida += pontos
    print("Vida agora: ", vida)

O curar recebe quantos pontos curar, soma na vida e mostra o resultado, mas nao devolve nada para quem chamou. Por isso o -> void. Compare com uma funcao que precisa entregar uma resposta, tipica de perguntas do jogo:

func esta_vivo() -> bool:
    return vida > 0

O esta_vivo nao recebe parametro nenhum (parenteses vazios) e devolve um bool, verdadeiro ou falso. Funcoes que devolvem bool deixam os if limpos de ler, porque o nome vira quase uma frase:

func _process(delta: float) -> void:
    if not esta_vivo():
        game_over()

Declarar o tipo de retorno nao e frescura. A Godot passa a te avisar quando voce tenta usar o resultado errado, por exemplo somar texto com o numero que a funcao devolveu, e o codigo roda um pouco mais rapido. Para quem esta aprendendo, a seta e o tipo funcionam como um bilhete que diz de antemao o que sai dali.

Parametros: os dados que entram

Parametros sao as entradas da funcao, os dados que voce passa para ela trabalhar. Uma funcao pode ter zero, um ou varios parametros, cada um com seu tipo. Pense em aplicar dano num inimigo: a funcao precisa saber quanto de dano, entao esse valor entra como parametro.

var vida: int = 100

func aplicar_dano(dano: int) -> void:
    vida -= dano
    if vida <= 0:
        vida = 0
        morrer()

func morrer() -> void:
    print("O player morreu")

Cada vez que voce chama aplicar_dano(25), o 25 entra como o parametro dano e a funcao faz a conta com ele. No dia seguinte um inimigo mais forte chama aplicar_dano(60), e a mesma funcao serve, sem reescrever nada. Esse e o poder do parametro: uma logica, muitos valores diferentes.

Quando a tarefa depende de mais de uma informacao, voce lista varios parametros separados por virgula. Uma pontuacao que soma o valor base multiplicado por um combo, por exemplo:

func calcular_pontuacao(inimigos: int, valor_base: int, combo: float) -> int:
    var total: float = inimigos * valor_base * combo
    return int(total)

Aqui entram tres parametros de tipos diferentes e sai um inteiro. Repare que a ordem importa: quando voce chama calcular_pontuacao(3, 100, 1.5), o 3 vira inimigos, o 100 vira valor_base e o 1.5 vira combo. Trocar a ordem dos argumentos troca o significado da conta.

Próximo nível
Quer aprender isso na prática?

No CursoGame.Dev você sai dos tutoriais soltos e constrói jogos publicáveis, com trilha progressiva, quests práticas e feedback real.

Conhecer a plataforma
+500 alunos4.9/5Garantia 7 dias

Valores padrao: parametros opcionais

As vezes um parametro tem um valor que serve na maioria das vezes, e voce so quer mudar de vez em quando. Para isso existe o valor padrao: voce atribui um valor ao parametro na propria declaracao, e ele passa a ser opcional na hora de chamar.

func mover_jogador(direcao: Vector2, velocidade: float = 250.0) -> void:
    position += direcao * velocidade

Como velocidade tem o padrao 250.0, voce pode chamar mover_jogador(Vector2.RIGHT) e a funcao usa 250 sozinha. Mas se um power-up deixa o player mais rapido, voce chama mover_jogador(Vector2.RIGHT, 400.0) e o valor que voce passou substitui o padrao. Um so metodo cobre o caso comum e o caso especial.

Uma regra importante: parametros com valor padrao precisam vir depois dos parametros sem padrao. A Godot reclama se voce inverter. Pense sempre nos obrigatorios primeiro, nos opcionais por ultimo.

func criar_inimigo(tipo: String, vida: int = 50, agressivo: bool = false) -> void:
    print("Spawnou ", tipo, " com ", vida, " de vida")

Essa funcao aceita tres jeitos de chamar: so o tipo, tipo com vida, ou os tres de uma vez. Valores padrao evitam que voce crie tres funcoes quase iguais so pra dar conta de variacoes pequenas.

Por que quebrar o codigo em funcoes

Talvez voce esteja pensando: pra que tanto trabalho, se da pra jogar tudo direto no _process? A resposta tem dois nomes que valem ouro no longo prazo.

O primeiro e DRY, de "don't repeat yourself", nao se repita. Toda vez que voce copia e cola um trecho, cria uma armadilha: no dia que a regra mudar, voce precisa lembrar de corrigir em todos os lugares que copiou, e sempre esquece um. Com a logica dentro de uma funcao, voce conserta num lugar so e o projeto inteiro herda a correcao. A conta de dano vive em aplicar_dano, a pontuacao em calcular_pontuacao, e ponto.

O segundo e legibilidade. Codigo bem fatiado em funcoes com bons nomes se le quase como texto. Compare um bloco gigante e anonimo com esta sequencia:

func _process(delta: float) -> void:
    processar_input(delta)
    atualizar_inimigos(delta)
    checar_colisoes()
    atualizar_hud()

Voce entende o que o frame faz sem ler uma linha do que esta dentro de cada funcao. Os nomes contam a historia. Quando um bug aparece na HUD, voce ja sabe onde abrir. Isso e mais valioso que qualquer comentario, porque o nome nao mente: ele e o codigo.

_ready e _process: as funcoes que a Godot chama por voce

Ate agora voce escreveu funcoes e chamou elas mesmo. Mas existe um grupo especial que a propria Godot chama automaticamente, nos momentos certos do ciclo de vida do node. As duas mais comuns sao _ready e _process.

O _ready roda uma vez, assim que o node entra na cena e esta pronto pra uso. E o lugar de preparar o estado inicial: zerar a vida, pegar referencias, configurar o comeco.

extends CharacterBody2D

var vida: int
var velocidade: float

func _ready() -> void:
    vida = 100
    velocidade = 250.0
    print("Player pronto")

Ja o _process(delta) roda todo frame, dezenas de vezes por segundo, enquanto o node existe. E ali que vai a logica continua: movimento, checagens, animacao. O parametro delta e o tempo em segundos desde o frame anterior, e multiplicar valores por ele mantem o movimento igual em qualquer maquina.

func _process(delta: float) -> void:
    var direcao: Vector2 = Input.get_vector("esquerda", "direita", "cima", "baixo")
    position += direcao * velocidade * delta

Repare que _ready e _process sao funcoes comuns: tem func, nome, parametro e retorno tipado. A unica diferenca e que voce nao as chama, a Godot chama. O nome com underline na frente e a convencao pra esses metodos do ciclo de vida. Voce so preenche o corpo com o que quer que aconteca naquele momento.

Erros comuns de quem esta comecando

Alguns tropecos aparecem toda semana com quem esta aprendendo funcoes. Conhecer eles antes economiza horas de cabeca quente.

Esquecer o return numa funcao que promete devolver algo. Se voce declara -> int mas nao escreve return, a funcao nao entrega o que prometeu e a Godot avisa. Toda funcao com tipo de retorno diferente de void precisa de um return que devolva aquele tipo.

Confundir o nome do parametro com uma variavel de fora. O parametro so existe dentro da funcao. Mudar dano la dentro nao altera nenhuma variavel dano que exista fora dela. Pense no parametro como uma copia local pra funcao trabalhar.

Passar argumentos na ordem errada. Como a posicao define quem e quem, chamar calcular_pontuacao(1.5, 100, 3) quando a assinatura espera (inimigos, valor_base, combo) bagunca a conta silenciosamente. Confira sempre a ordem da assinatura.

Chamar a funcao sem os parenteses. esta_vivo sozinho e uma referencia ao metodo, nao a execucao dele. Pra rodar, precisa ser esta_vivo(). Parenteses vazios ainda sao parenteses.

Juntando tudo num player de verdade

Veja como as pecas convivem num unico script. Um player que se move, leva dano, cura e reporta seu estado, tudo fatiado em funcoes com nomes claros:

extends CharacterBody2D

var vida: int
const VIDA_MAXIMA: int = 100

func _ready() -> void:
    vida = VIDA_MAXIMA

func _physics_process(delta: float) -> void:
    var direcao: Vector2 = Input.get_vector("esquerda", "direita", "cima", "baixo")
    mover(direcao)
    move_and_slide()

func mover(direcao: Vector2, velocidade: float = 250.0) -> void:
    velocity = direcao * velocidade

func aplicar_dano(dano: int) -> void:
    vida -= dano
    if not esta_vivo():
        vida = 0
        morrer()

func curar(pontos: int) -> void:
    vida = min(vida + pontos, VIDA_MAXIMA)

func esta_vivo() -> bool:
    return vida > 0

func morrer() -> void:
    print("Game over")

Cada funcao faz uma coisa e diz no nome o que faz. _physics_process orquestra o frame chamando mover. aplicar_dano decide se e hora de morrer perguntando a esta_vivo. O valor padrao em mover deixa espaco pra um futuro power-up de velocidade sem mexer no resto. Esse e o tipo de codigo que continua legivel daqui a seis meses.

Por onde seguir

Funcoes sao o esqueleto de qualquer projeto de jogo: elas organizam a logica em blocos que voce nomeia, reaproveita e conserta num lugar so. O segredo nao e decorar a sintaxe do func, e sim ganhar o reflexo de perguntar "isso aqui merece virar uma funcao?" toda vez que uma tarefa se repete ou fica grande demais pra ler de uma vez.

Com funcoes firmes, os proximos fundamentos rendem mais. Vale continuar por loops no GDScript, porque e comum uma funcao processar varios elementos de uma vez, e por arrays e listas no GDScript, que sao os dados que essas funcoes costumam receber e devolver. E se em algum momento bater a duvida entre linguagens, a comparacao entre C# e GDScript no Godot mostra que o conceito de funcao tipada e o mesmo dos dois lados, muda so a roupa.

O melhor jeito de fixar e abrir a Godot e refatorar: pegue um script seu cheio de codigo repetido e va extraindo cada pedaco pra uma funcao com nome. Em poucos projetos, quebrar o codigo em funcoes deixa de ser tarefa e vira o jeito natural de pensar.

Perguntas frequentes

O que sao funcoes no GDScript?

Funcao e um bloco de codigo com nome que agrupa uma tarefa, como aplicar dano ou mover o jogador. Voce declara com func, define parametros de entrada e o tipo do retorno, e chama esse bloco toda vez que precisar em vez de repetir as mesmas linhas.

Preciso sempre declarar o tipo de retorno da funcao?

Nao e obrigatorio, mas e o padrao recomendado. Escrever -> int, -> bool ou -> void faz a Godot avisar erros antes de rodar, deixa o codigo mais rapido e documenta o que cada funcao devolve. Se a funcao nao retorna nada, use -> void.

Qual a diferenca entre parametro e valor de retorno?

Parametro e o que entra na funcao, o dado que voce passa entre parenteses ao chamar. Retorno e o que sai, o resultado que a funcao devolve com return. Uma funcao pode ter varios parametros e no maximo um valor de retorno.