Voltar para o Blog
Quest Log

Loops no GDScript: for e while para Jogos

Tela de codigo Godot mostrando um loop que spawna varios inimigos numa cena 2D

Aprenda loops no GDScript com exemplos tipados de Godot 4: for e while, break e continue, e como evitar o loop infinito que trava o jogo.

Ferramenta:Godot

Loops no GDScript: for e while para Jogos

Muita coisa em jogo acontece em quantidade. Voce raramente spawna um inimigo, spawna dez. Voce nao olha um item do inventario, percorre a lista inteira. Nao verifica uma vida, conta todas. Repetir uma tarefa varias vezes sem copiar e colar a mesma linha e trabalho dos loops no GDScript, e os dois que voce vai usar o tempo todo sao o for e o while. Neste texto eu explico os dois com exemplos tipados de Godot 4, sempre dentro de uma cena de jogo, mais o break, o continue e o erro classico que trava a tela: o loop infinito.

Se voce ainda esta firmando os fundamentos antes disso, vale passar pela logica de programacao para jogos primeiro, porque loop e uma das quatro pecas basicas e faz mais sentido depois que variaveis e condicionais estao no lugar. Aqui a gente foca so na repeticao.

O for: quando voce sabe quantas vezes

O for repete um bloco um numero conhecido de vezes ou percorre uma colecao item a item. Ele e a escolha padrao quando voce consegue responder "quantas vezes?" antes de comecar. A forma mais comum de contar e com range(), que gera uma sequencia de numeros.

func spawnar_inimigos(quantidade: int) -> void:
    for i in range(quantidade):
        var inimigo: Node2D = cena_inimigo.instantiate()
        inimigo.position = Vector2(100.0 * i, 0.0)
        add_child(inimigo)

Aqui range(quantidade) produz os numeros de 0 ate quantidade - 1, e o for roda o bloco uma vez para cada um. A cada volta a variavel i assume o proximo valor (0, 1, 2...), e usamos esse i para espalhar os inimigos na horizontal: o primeiro em x 0, o segundo em x 100, e assim por diante. Chamar spawnar_inimigos(10) cria dez inimigos enfileirados sem voce escrever dez linhas iguais.

Repare no detalhe de contagem: range(10) vai de 0 a 9, nunca chega no 10. Isso confunde no comeco, mas e util, porque 0 ate 9 sao exatamente dez numeros, e o indice 0 casa com o primeiro item de qualquer lista. O range() tambem aceita inicio e passo: range(1, 6) da 1, 2, 3, 4, 5, e range(0, 10, 2) da 0, 2, 4, 6, 8. Serve para numerar ondas, distribuir waypoints ou montar uma grade.

Percorrer um array

Quando voce ja tem uma colecao pronta, nem precisa do range(). O for percorre o array direto, entregando cada elemento na variavel do loop. E o jeito natural de varrer um inventario:

var inventario: Array[String] = ["espada", "pocao", "chave"]

func listar_itens() -> void:
    for item in inventario:
        print("Item: ", item)

A cada volta, item vira "espada", depois "pocao", depois "chave". Note o tipo Array[String]: uma lista que so aceita texto, entao um numero solto nao entra ali por engano. Se voce precisar tambem da posicao de cada item, ai sim volta o range() combinado com o tamanho da lista:

func listar_com_indice() -> void:
    for i in range(inventario.size()):
        print(i, ": ", inventario[i])

Esse padrao aparece muito em menus e HUDs, quando voce quer mostrar "1: espada, 2: pocao" numerado na tela. O mesmo for in funciona para qualquer colecao: um array de inimigos vivos, os filhos de um node, as teclas de um dicionario.

func contar_vidas(vidas: Array[Node2D]) -> int:
    var total: int = 0
    for vida in vidas:
        if vida.visible:
            total += 1
    return total

Aqui percorremos os icones de vida na HUD e contamos quantos ainda estao visiveis. Um loop que acumula um resultado numa variavel (aqui total) e um dos usos mais comuns do for: somar dano de uma lista, contar moedas coletadas, medir quantos inimigos restam na tela.

O while: quando voce nao sabe quantas vezes

O while e o outro lado da moeda. Ele repete enquanto uma condicao for verdadeira, sem saber de antemao quantas voltas vai dar. Serve para "continue fazendo isso ate algo mudar". Pense em gastar municao ate acabar:

func gastar_municao(tiros: int) -> void:
    var restante: int = tiros
    while restante > 0:
        print("Bang! Municao: ", restante)
        restante -= 1

Enquanto restante for maior que zero, o bloco roda; a cada tiro restante cai um, e quando chega a zero o loop para sozinho. Poderiamos ter usado um for aqui, ja que sabemos o total de tiros, e nesse caso especifico o for seria ate mais limpo. O while brilha mesmo quando o numero de voltas depende de algo que muda em tempo de execucao e voce nao consegue prever.

Um exemplo mais honesto de while e consumir uma fila ate esvaziar, sem saber quantos itens vao entrar nela:

var fila_de_spawn: Array[String] = []

func processar_fila() -> void:
    while not fila_de_spawn.is_empty():
        var tipo: String = fila_de_spawn.pop_front()
        criar_por_tipo(tipo)

Cada pop_front() tira um item da frente da fila e o cria. O loop segue ate a fila ficar vazia. Como outra parte do jogo pode empilhar mais inimigos na fila a qualquer momento, voce nao tem um numero fixo para colocar num range(). Esse "processe ate acabar" e o territorio natural do while.

break e continue: controlando o loop por dentro

Nem sempre voce quer rodar o loop inteiro. Duas palavras mudam o fluxo por dentro. O break sai do loop na hora, ignorando o que faltava. O continue pula so a volta atual e segue para a proxima.

O break e otimo para uma busca: assim que achou o que queria, nao ha motivo para continuar varrendo.

func achar_primeiro_inimigo(inimigos: Array[Node2D]) -> Node2D:
    for inimigo in inimigos:
        if inimigo.tem_metodo_vivo() and inimigo.esta_vivo():
            return inimigo
    return null

Nesse caso o proprio return ja sai do loop e da funcao, que e a forma mais limpa. Mas quando voce nao vai retornar e so precisa parar, o break faz o servico. Imagine procurar um espaco livre numa grade de inventario e guardar o indice:

func primeiro_slot_livre(slots: Array[bool]) -> int:
    var achado: int = -1
    for i in range(slots.size()):
        if slots[i] == false:
            achado = i
            break
    return achado

Assim que encontra o primeiro slot livre, o break interrompe a varredura. Sem ele, o loop continuaria checando slots a toa. O continue, por outro lado, serve para ignorar certos itens sem parar o loop. Pense em aplicar cura so nos aliados que ainda estao vivos:

func curar_aliados(aliados: Array[Node2D], cura: int) -> void:
    for aliado in aliados:
        if not aliado.esta_vivo():
            continue
        aliado.vida += cura

Quando o aliado esta morto, o continue pula direto para o proximo sem executar a linha de cura. E mais legivel que aninhar tudo dentro de um if, porque separa o caso a ignorar do trabalho de verdade.

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

O loop infinito: como o jogo trava

Aqui esta o erro que todo iniciante comete pelo menos uma vez, e vale entender bem porque ele parece um crash misterioso. Um loop infinito e um while cuja condicao nunca fica falsa. Como todo o codigo de um quadro roda de uma vez so, antes da Godot desenhar a tela de novo, um loop que nunca termina congela o jogo inteiro. A janela para de responder e voce precisa fecha-la na forca.

# NAO faca isso: restante nunca muda, o loop roda para sempre
func bug_de_travamento(tiros: int) -> void:
    var restante: int = tiros
    while restante > 0:
        print("Bang!")
        # esqueci o restante -= 1

O problema e sutil: falta a linha que faz restante caminhar para zero. A condicao restante > 0 continua verdadeira para sempre, o print dispara milhoes de vezes por segundo e a Godot nunca chega a desenhar o proximo frame. A regra de ouro do while e simples: antes de escrever o corpo, garanta que exista dentro dele alguma coisa que va tornar a condicao falsa. No exemplo bom mais acima era o restante -= 1; no da fila era o pop_front() esvaziando a lista.

Uma protecao extra, util enquanto voce esta aprendendo ou quando a logica e complicada, e um contador de seguranca que corta o loop se algo der errado:

func processar_com_trava() -> void:
    var voltas: int = 0
    while not fila_de_spawn.is_empty():
        fila_de_spawn.pop_front()
        voltas += 1
        if voltas > 10000:
            push_warning("Loop passou do limite, algo esta errado")
            break

Se por qualquer motivo a condicao nao fechar, o break no limite salva o jogo de travar e ainda te avisa no console. Nao e para deixar isso em todo loop, mas e um bom paraquedas em codigo que ainda esta ganhando confianca.

for ou while: como escolher

A decisao e quase sempre direta. Faca a si mesmo uma pergunta: eu sei quantas vezes isso precisa rodar? Se a resposta e sim, seja um numero fixo ou o tamanho de uma colecao, use for. Spawnar dez inimigos, percorrer o inventario, somar o dano de uma lista de ataques, contar vidas visiveis: tudo isso tem um total conhecido, tudo isso pede for.

Se a resposta e nao, se o numero de voltas depende de uma condicao que muda enquanto o jogo roda, use while. Esvaziar uma fila que outros sistemas alimentam, repetir uma tentativa ate dar certo, continuar um efeito ate um timer zerar: nesses casos voce nao tem o total antecipado, entao o while e o certo. Na duvida, a maioria esmagadora do codigo de jogo do dia a dia usa for, e o while fica reservado para os poucos casos de "ate acabar".

Loops combinam muito bem com dois outros temas. Quando o corpo do loop fica grande, extraia parte dele para uma funcao com nome, o que deixa a intencao clara; veja como em funcoes no GDScript. E quando voce quer variar o que cada volta faz, por exemplo spawnar inimigos em posicoes ou com atributos sorteados, o loop se junta a numeros aleatorios no Godot para gerar hordas que nunca saem iguais.

func spawnar_horda(quantidade: int) -> void:
    for i in range(quantidade):
        var inimigo: Node2D = cena_inimigo.instantiate()
        var x: float = randf_range(0.0, 640.0)
        var y: float = randf_range(0.0, 360.0)
        inimigo.position = Vector2(x, y)
        add_child(inimigo)

O for garante a quantidade exata; o sorteio dentro dele garante que cada inimigo apareca num ponto diferente da tela. Um loop, muita variedade.

Por onde seguir

Loop e a peca que transforma codigo que faz uma coisa em codigo que cuida de muitas. O resumo cabe em poucas linhas: use for quando sabe o total (contando com range() ou percorrendo uma colecao), use while quando depende de uma condicao mudar, use break para sair mais cedo e continue para pular uma volta, e sempre garanta que o while tenha como terminar para nao travar o jogo.

O jeito de fixar e o de sempre: abra a Godot e escreva. Faca um loop que imprime de 1 a 10, depois um que spawna cinco quadrados em fila, depois um que percorre uma lista de nomes e mostra cada um. Erre de proposito um while sem fim so para ver a janela congelar e entender na pratica o que estava acontecendo. Quando essas repeticoes viram reflexo, boa parte da logica de um jogo passa a parecer obvia, porque quase todo sistema, do inventario a horda, e no fundo um loop bem escolhido.

Perguntas frequentes

Qual a diferenca entre for e while no GDScript?

O for repete um numero conhecido de vezes ou percorre uma colecao (um range, um array). O while repete enquanto uma condicao for verdadeira, sem saber de antemao quantas voltas vai dar. Use for quando sabe o total, while quando depende de uma condicao mudar.

Por que meu jogo travou com um loop?

Quase sempre e um loop infinito: um while cuja condicao nunca fica falsa. Como todo o quadro roda de uma vez, o jogo congela na tela ate voce fechar a janela. Garanta que algo dentro do while caminhe para o fim, ou use break para sair.

Posso mudar um array enquanto percorro ele com for?

Evite remover itens do mesmo array que voce esta percorrendo, porque os indices se deslocam e voce pula elementos. O padrao seguro e montar uma lista dos que vao sair e remover depois do loop, ou percorrer de tras para frente.