Bevy Engine em Rust: ECS, 2D/3D e Portfólio | Rust Brasil

Guia prático de Bevy em Rust: Entity Component System, sistemas, assets, input, física leve, WebAssembly, testes e como montar um jogo de portfólio em 2026.

Para criar jogos e protótipos interativos em Rust em 2026, comece pelo Bevy quando você quer uma engine data-driven com Entity Component System, renderização moderna via wgpu e um código-base 100% Rust. Modele o jogo como entidades + componentes + sistemas, separe estado de lógica, carregue assets de forma explícita e só então acrescente física, áudio, UI e export WASM. Não escolha Bevy por moda: escolha quando ECS, iteração em código e controle do loop de frame resolvem o produto melhor do que um editor tradicional.

Bevy já aparece em conversas de carreira em gamedev com Rust, no panorama de Rust para jogos e em comparativos de GUI e ferramentas. Este artigo é o guia operacional: como estruturar um projeto Bevy de verdade, o que colocar no primeiro milestone e como transformar isso em sinal de portfólio e vagas Rust.

Resposta rápida: Bevy, ggez, macroquad ou engine AAA?

NecessidadeEscolha inicialMotivo
Jogo 2D/3D data-driven com ECS e ecossistema de pluginsBevyengine completa, schedules, assets e wgpu
Protótipo 2D mínimo em poucas horasmacroquad / ggezAPI pequena, feedback imediato
Editor maduro, marketplace e time grandeUnity / Unrealpipeline de conteúdo e hiring mais amplos
Ferramenta interna, simulação ou visualizadorBevy ou eguiBevy para cena/tempo real; egui para painéis
Demo jogável no browserBevy + WASM ou macroquadWASM viável; cuide do tamanho e dos assets
Servidor multiplayer / authoritative simRust + Tokio (+ Bevy só no cliente se fizer sentido)separe simulação de rede da renderização

Regra prática: se o valor está no modelo de dados e nos sistemas, Bevy encaixa. Se o valor está em arrastar assets num editor com designers não-programadores no dia um, avalie stacks com editor maduro.

O que Bevy resolve — e o que não resolve

Bevy organiza o jogo em três ideias:

  1. Entity — um ID opaco que agrupa componentes;
  2. Component — dados puros (Transform, Velocity, Health, Player);
  3. System — função que consulta o World e atualiza componentes a cada schedule.

Isso escala melhor do que uma hierarquia rígida de herança de classes de jogo. Novos comportamentos entram como novos componentes e sistemas, não como subclasses intermináveis.

Bevy resolve bem:

  • loops de frame com schedules (PreUpdate, Update, FixedUpdate, PostUpdate);
  • composição de comportamento sem herança profunda;
  • renderização moderna via wgpu;
  • hot-reload de assets em desenvolvimento;
  • plugins reutilizáveis e arquitetura modular;
  • demos técnicas, tools e jogos indie em Rust puro.

Bevy não resolve sozinho:

  • pipeline de arte AAA com editores visuais maduros;
  • hiring em massa de designers acostumados só a Unity/Unreal;
  • física AAA completa sem crates/plugins adicionais;
  • “jogo pronto” sem disciplina de estados, assets e performance;
  • multiplayer autoritativo — rede continua sendo problema de protocolo e simulação.

Se você ainda está consolidando Rust básico, volte ao guia como aprender Rust e aos tutoriais antes de brigar com lifetimes dentro de systems complexos.

Conceitos que você precisa internalizar

World, Resource e Query

  • World guarda entidades, componentes e resources.
  • Resource é estado global (Time, AssetServer, configuração, score).
  • Query seleciona entidades que possuem certo conjunto de componentes.

Pense em SQL mental: sistemas são consultas + updates sobre tabelas de componentes. Essa metáfora ajuda em entrevistas e no desenho de features.

Schedules e ordenação

Nem tudo roda “em qualquer ordem”. Input costuma vir antes da lógica; física em FixedUpdate; câmera e UI depois. Quando dois sistemas precisam de ordem, use sets explícitos ou .before / .after. Ordem implícita vira bug intermitente.

States

Menu, playing, paused e game over não devem virar if espalhado. Use estados do Bevy (ou uma máquina explícita) para spawnar/despawnar entidades e ativar sistemas só no momento certo.

Projeto inicial: arena 2D com player e inimigos

Um primeiro milestone crível:

bevy-arena/
  Cargo.toml
  assets/
    sprites/
      player.png
      enemy.png
    audio/
      hit.ogg
  src/
    main.rs
    components.rs
    systems/
      mod.rs
      movement.rs
      combat.rs
      spawn.rs
    states.rs

Dependências típicas (ajuste a versão à release estável escolhida):

[package]
name = "bevy-arena"
version = "0.1.0"
edition = "2021"

[dependencies]
bevy = { version = "0.15", features = ["default"] }

[profile.dev]
opt-level = 1

[profile.dev.package."*"]
opt-level = 3

Os opt-level no profile dev são um detalhe prático: Bevy em debug puro fica pesado; otimizar dependências sem abrir mão de rebuilds rápidos do seu crate melhora o feedback loop.

Componentes como dados

use bevy::prelude::*;

#[derive(Component)]
struct Player;

#[derive(Component)]
struct Enemy;

#[derive(Component, Deref, DerefMut)]
struct Velocity(Vec2);

#[derive(Component)]
struct Health {
    atual: i32,
    maximo: i32,
}

#[derive(Component)]
struct Collider {
    raio: f32,
}

Evite colocar lógica pesada dentro dos componentes. Dados ficam nos componentes; regras ficam nos systems. Essa disciplina facilita testes e refatoração.

App, plugins e estado

use bevy::prelude::*;

mod components;
mod states;
mod systems;

use states::GameState;
use systems::{combat, movement, spawn};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                title: "Bevy Arena — Rust Brasil".into(),
                resolution: (960., 540.).into(),
                ..default()
            }),
            ..default()
        }))
        .init_state::<GameState>()
        .add_systems(Startup, spawn::setup_camera)
        .add_systems(OnEnter(GameState::Playing), spawn::spawn_arena)
        .add_systems(
            Update,
            (
                movement::player_input,
                movement::aplicar_velocidade,
                combat::detectar_colisao,
                combat::aplicar_dano,
            )
                .run_if(in_state(GameState::Playing)),
        )
        .run();
}

O ponto importante não é o sprite: é a fronteira clara entre setup, enter-state, update e condições de execução.

Movimento com input

use bevy::prelude::*;
use crate::components::{Player, Velocity};

pub fn player_input(
    teclado: Res<ButtonInput<KeyCode>>,
    mut query: Query<&mut Velocity, With<Player>>,
) {
    const SPEED: f32 = 220.0;
    for mut velocity in &mut query {
        let mut dir = Vec2::ZERO;
        if teclado.pressed(KeyCode::KeyW) || teclado.pressed(KeyCode::ArrowUp) {
            dir.y += 1.0;
        }
        if teclado.pressed(KeyCode::KeyS) || teclado.pressed(KeyCode::ArrowDown) {
            dir.y -= 1.0;
        }
        if teclado.pressed(KeyCode::KeyA) || teclado.pressed(KeyCode::ArrowLeft) {
            dir.x -= 1.0;
        }
        if teclado.pressed(KeyCode::KeyD) || teclado.pressed(KeyCode::ArrowRight) {
            dir.x += 1.0;
        }
        velocity.0 = if dir.length_squared() > 0.0 {
            dir.normalize() * SPEED
        } else {
            Vec2::ZERO
        };
    }
}

pub fn aplicar_velocidade(
    tempo: Res<Time>,
    mut query: Query<(&Velocity, &mut Transform)>,
) {
    for (velocity, mut transform) in &mut query {
        transform.translation.x += velocity.x * tempo.delta_secs();
        transform.translation.y += velocity.y * tempo.delta_secs();
    }
}

O padrão a gravar é: ler input → escrever Velocity → outro system integra no Transform. Separar “intenção” de “integração” facilita dash, knockback e status effects depois.

Nota: APIs como delta_secs() e nomes de KeyCode variam entre releases do Bevy. Trate os snippets como arquitetura; confirme a assinatura na documentação da versão fixada no Cargo.toml.

Arquitetura que escala além do tutorial

1. Sistemas pequenos e compostos

Prefira vários systems com uma responsabilidade a um update_tudo. Isso melhora paralelismo potencial do ECS, clareza de bugs e testes.

2. Events em vez de acoplamento direto

Dano, coleta de item, death e mudança de fase funcionam melhor como events (EnemyDied, PlayerHit). Systems publicam; outros reagem. Evita queries gigantes e ordem frágil.

3. Resources para config e score

#[derive(Resource)]
struct ArenaConfig {
    largura: f32,
    altura: f32,
    inimigos_iniciais: u32,
}

#[derive(Resource, Default)]
struct Score(u32);

Configurações hardcodadas em systems viram dívida. Resources e assets de config deixam balanceamento iterável.

4. FixedUpdate para lógica sensível

Movimento casual pode viver em Update. Combate, física e simulações que não podem depender do frame time do monitor tendem a FixedUpdate. Misturar os dois sem critério produz replays e colisões inconsistentes.

5. Despawn e ciclos de vida

Estados devem limpar entidades. Vazamento de entities entre Menu e Playing é um dos bugs mais comuns em projetos Bevy iniciantes. Ao sair de um estado, despawn explícito ou marcadores de “cleanup” evitam fantasmas invisíveis consumindo CPU.

Assets, áudio e UI sem drama

  • Coloque sprites/áudios em assets/ e carregue via AssetServer.
  • Trate handles como dados: spawnar antes do load terminar exige lidar com estados de carregamento.
  • UI: comece com a UI do próprio Bevy para HUD (vida, score, pause). Para editores e ferramentas auxiliares, egui costuma ser mais rápido.
  • Áudio: um hit e um tema já bastam no portfólio; volume e mute mostram cuidado de produto.

Se a meta também inclui desktop tool + jogo, diferencie os papéis: Bevy para a cena; toolkit GUI para painéis densos.

Bevy na web (WASM)

Compilar para browser é um diferencial de demo, não o primeiro passo. Ordem sensata:

  1. jogo desktop estável;
  2. assets leves e orçamento de tamanho;
  3. target WASM e ajustes de input/áudio;
  4. hospedagem estática da demo.

Para o contexto mais amplo de WebAssembly em Rust, veja Rust e WebAssembly e o fluxo de build com Trunk. Em Bevy, WASM é excelência de apresentação; não use isso para esconder arquitetura frágil.

Testes e qualidade em um projeto de jogo

Jogos parecem “impossíveis de testar”, mas a parte valiosa do Bevy é testável:

  • funções puras de dano, score e spawn tables;
  • systems rodando em App de teste com world controlado;
  • asserts sobre componentes após um update;
  • golden tests de balanceamento (quantos frames até o player morrer em cenário X).

Exemplo de direção:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn dano_nao_passa_de_zero() {
        let mut h = Health { atual: 3, maximo: 3 };
        h.atual = (h.atual - 10).max(0);
        assert_eq!(h.atual, 0);
    }
}

Suba depois para testes de system com entidades spawnadas. O objetivo não é 100% de cobertura gráfica; é proteger regras de jogo e regressões de estado.

Combine com o hábito geral de testes em Rust e, se o projeto virar crate público, com tooling de Cargo.

Performance: o que medir cedo

  • Quantidade de entities e queries amplas demais;
  • sistemas que rodam sempre sem run_if;
  • alocações por frame em hot paths;
  • sprites/texture atlases sem cuidado;
  • logs excessivos em Update.

Antes de micro-otimizar, meça com instruments do próprio Bevy/tracing e com um frame budget. Para serviços backend a conversa de profiling é outra — veja profiling em produção — mas a mentalidade é a mesma: evidência antes de heroísmo.

Bevy vs outras opções do ecossistema Rust

CritérioBevymacroquad/ggezGodot (GDExtension)Unity/Unreal
Modelo mentalECS data-drivenAPI imediataárvore de nós + scriptseditor-first
Linguagem principalRustRustGDScript/C#/Rust bindingsC#/C++
Curva inicialmédiabaixabaixa/médiabaixa para designers
Controle do framealtoaltomédiomédio
Editor visuallimitado/ecossistemamínimofortemuito forte
Portfólio Rust “puro”excelentebomhíbridofraco como sinal Rust

Para quem mira empresas que usam Rust em ferramentas, simulação, infra de jogos ou produtos interativos, Bevy comunica domínio de ECS, schedules e engenharia de runtime. Para quem mira estúdio clássico de games, ainda pesa demonstrar familiaridade com pipelines Unity/Unreal — Bevy sozinho não é passe livre.

Como montar um portfólio Bevy que ajuda em vagas

Um repositório forte para carreira Rust e entrevistas:

  1. GDD de uma página: objetivo, controles, win/lose, escopo cortado.
  2. Arquitetura: pasta systems/, components claros, states.
  3. Milestone jogável: 60–90 segundos de loop divertido > mapa enorme vazio.
  4. README: como rodar, versão do Bevy, screenshots/GIF, decisões e trade-offs.
  5. Testes das regras + checklist manual de build.
  6. Roadmap honesto: o que ficou de fora (multiplayer, save, editor).

Sinais que recrutadores entendem rápido:

  • input e câmera estáveis;
  • spawn/despawn sem vazamento;
  • UI de pause/game over;
  • configuração separada da lógica;
  • commit history limpa.

Combine com o nicho em gamedev, o panorama de Rust para jogos e, se o ângulo for servidor de jogo, com mensageria ou redes (Quinn/QUIC) no backend — sem forçar Bevy no servidor se não houver ganho.

Erros comuns (e como evitar)

  1. God system que move, colide, anima e toca áudio.
  2. Estado global em static mut em vez de Resource.
  3. Ignorar FixedUpdate em combate/física.
  4. Não fixar versão do Bevy e copiar snippets de releases diferentes.
  5. Começar pelo WASM antes do loop divertido no desktop.
  6. Scope creep: inventário + crafting + árvore de skill no primeiro mês.
  7. Zero medição: otimizar systems sem saber se CPU ou GPU é o limite.

Roteiro de 7 dias para o primeiro jogo

DiaEntrega
1Janela, câmera, sprite do player, movimento
2Estados Menu/Playing/GameOver
3Inimigos + spawn timer
4Colisão e vida
5Score, HUD, som de hit
6Polimento: reset, dificuldade curta, README
7Grava GIF, publica repo, escreve post-mortem de 20 linhas

Se travar em ownership/borrowing no meio do caminho, pause o jogo e refine a base com traits e os tutoriais de concorrência. Bevy recompensa quem entende dados e lifetimes; pune quem tenta “só mais um clone” em todo lugar.

Perguntas frequentes

O que é Bevy em Rust?

É uma engine de jogos open source em Rust centrada em ECS, com plugins, assets e renderização via wgpu. Serve a jogos, demos e ferramentas interativas.

Bevy substitui Unity ou Unreal?

Não de forma geral. Substitui bem em times Rust, protótipos data-driven e produtos onde código e ECS importam mais que o editor. Em pipelines AAA com muitos designers, Unity/Unreal continuam padrão.

Preciso saber ECS antes de começar?

Não. Aprenda entidades, componentes e sistemas na prática, mas invista cedo em queries, schedules e states para não pintar um beco sem saída.

Bevy ajuda em portfólio e vagas?

Ajuda quando há jogo jogável, arquitetura legível e README com trade-offs. É um bom sinal para gamedev, tools, simulação e sistemas interativos.

Bevy roda no navegador?

Sim, via WebAssembly, com cuidados de assets, áudio e tamanho. Faça desktop primeiro.

Qual versão usar?

A estável mais recente no início do projeto, fixada explicitamente, com notes de migração à mão. Trate upgrades como trabalho de engenharia, não como cargo update cego.

Conclusão

Bevy é a porta de entrada mais convincente para jogos e protótipos sérios em Rust quando você topa pensar em ECS, schedules e dados. O caminho maduro é curto e disciplinado: fixe a versão, entregue um loop jogável, separe components/systems/states, teste as regras e documente decisões. Só então busque física avançada, WASM e multiplayer.

Para continuar, releia o panorama de Rust para jogos, o plano de carreira em gamedev, o comparativo de GUI em Rust e explore projetos práticos e vagas no ecossistema brasileiro. Bevy não é atalho mágico — é uma engine que recompensa engenharia clara.