---
title: "Rust para Jogos: Bevy, ECS e Game Engines | Rust Brasil"
url: "https://rustlang.com.br/artigos/rust-para-jogos/"
markdown_url: "https://rustlang.com.br/artigos/rust-para-jogos.MD"
description: "Guia completo de Rust para jogos em 2026: Bevy engine, ggez, macroquad, wgpu e arquitetura ECS com exemplos práticos de game loop."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Rust para Jogos: Bevy, ECS e Game Engines | Rust Brasil

Guia completo de Rust para jogos em 2026: Bevy engine, ggez, macroquad, wgpu e arquitetura ECS com exemplos práticos de game loop.


## Introdução

O desenvolvimento de jogos é uma das fronteiras mais emocionantes para Rust. A linguagem oferece exatamente o que game engines precisam: **performance previsível sem garbage collector** (eliminando stutters causados por pausas de GC), **abstrações zero-cost** (generics e traits não adicionam overhead em runtime), **segurança de memória** (reduzindo crashes em produção) e **excelente suporte a concorrência** (fundamental para jogos modernos que aproveitam múltiplos cores).

O **Bevy** se estabeleceu como a engine de jogos mais popular do ecossistema Rust, implementando uma arquitetura **Entity Component System (ECS)** que é simultaneamente ergonômica e de alta performance. Além do Bevy, frameworks como **ggez**, **macroquad** e **wgpu** cobrem desde protótipos 2D até renderização 3D avançada. Neste artigo, vamos explorar o ecossistema, construir um jogo funcional e entender como empresas estão adotando Rust para gamedev.

## Ecossistema de Desenvolvimento de Jogos

### Engines e Frameworks

| Engine/Framework | Tipo | Destaques |
|---|---|---|
| **Bevy** | Engine completa | ECS, hot-reloading, 2D/3D, multiplataforma |
| **ggez** | Framework 2D | Simples, inspirado no LOVE2D, ideal para iniciantes |
| **macroquad** | Framework 2D | Minimalista, compila para Wasm facilmente |
| **Fyrox** | Engine 3D | Editor visual, cenas, física, UI |
| **wgpu** | API gráfica | Abstração sobre Vulkan/Metal/DX12/WebGPU |
| **winit** | Janelamento | Criação de janelas cross-platform |
| **gilrs** | Input | Suporte a gamepads e joysticks |
| **kira** | Áudio | Engine de áudio com mixagem e efeitos |

### Cargo.toml para um Projeto Bevy

```toml
[package]
name = "meu-jogo"
version = "0.1.0"
edition = "2021"

[dependencies]
bevy = "0.15"
rand = "0.8"

# Otimização: compilar dependências com otimizações,
# mas manter debug info no nosso código
[profile.dev.package."*"]
opt-level = 3

[profile.dev]
opt-level = 1
```

## Arquitetura ECS (Entity Component System)

Antes de mergulhar no código, é importante entender a arquitetura ECS, que é o coração do Bevy:

- **Entity**: Um identificador único (apenas um número). Não contém dados nem lógica
- **Component**: Dados puros associados a uma entidade (posição, velocidade, sprite, vida)
- **System**: Funções que operam sobre entidades que possuem determinados componentes

Esta arquitetura é ideal para jogos porque:
1. **Cache-friendly**: Componentes do mesmo tipo ficam contíguos em memória
2. **Paralelizável**: Systems independentes rodam em paralelo automaticamente
3. **Composicional**: Comportamentos emergem da combinação de componentes simples

## Exemplo Prático: Jogo de Esquiva Espacial com Bevy

Vamos construir um jogo completo onde o jogador controla uma nave que precisa esquivar de asteroides.

```rust
use bevy::prelude::*;
use rand::Rng;

// === Componentes ===

#[derive(Component)]
struct Jogador {
    velocidade: f32,
}

#[derive(Component)]
struct Asteroide {
    velocidade: f32,
}

#[derive(Component)]
struct Pontuacao(u32);

#[derive(Resource)]
struct PontuacaoGlobal {
    valor: u32,
    timer_spawn: Timer,
}

// === Setup ===

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    // Câmera 2D
    commands.spawn(Camera2d);

    // Jogador (nave)
    commands.spawn((
        Sprite {
            color: Color::srgb(0.2, 0.7, 1.0),
            custom_size: Some(Vec2::new(40.0, 50.0)),
            ..default()
        },
        Transform::from_xyz(0.0, -250.0, 0.0),
        Jogador { velocidade: 400.0 },
    ));

    // Recurso de pontuação
    commands.insert_resource(PontuacaoGlobal {
        valor: 0,
        timer_spawn: Timer::from_seconds(0.8, TimerMode::Repeating),
    });

    // Texto de pontuação
    commands.spawn((
        Text::new("Pontos: 0"),
        TextFont {
            font_size: 30.0,
            ..default()
        },
        TextColor(Color::WHITE),
        Node {
            position_type: PositionType::Absolute,
            top: Val::Px(10.0),
            left: Val::Px(10.0),
            ..default()
        },
        Pontuacao(0),
    ));
}

// === Systems ===

/// Movimenta o jogador com as setas do teclado.
fn mover_jogador(
    keyboard: Res<ButtonInput<KeyCode>>,
    time: Res<Time>,
    mut query: Query<(&mut Transform, &Jogador)>,
) {
    let (mut transform, jogador) = query.single_mut();
    let mut direcao = Vec3::ZERO;

    if keyboard.pressed(KeyCode::ArrowLeft) || keyboard.pressed(KeyCode::KeyA) {
        direcao.x -= 1.0;
    }
    if keyboard.pressed(KeyCode::ArrowRight) || keyboard.pressed(KeyCode::KeyD) {
        direcao.x += 1.0;
    }
    if keyboard.pressed(KeyCode::ArrowUp) || keyboard.pressed(KeyCode::KeyW) {
        direcao.y += 1.0;
    }
    if keyboard.pressed(KeyCode::ArrowDown) || keyboard.pressed(KeyCode::KeyS) {
        direcao.y -= 1.0;
    }

    if direcao.length() > 0.0 {
        direcao = direcao.normalize();
    }

    transform.translation += direcao * jogador.velocidade * time.delta_secs();

    // Limitar aos bounds da tela
    transform.translation.x = transform.translation.x.clamp(-600.0, 600.0);
    transform.translation.y = transform.translation.y.clamp(-350.0, 350.0);
}

/// Gera novos asteroides periodicamente.
fn gerar_asteroides(
    mut commands: Commands,
    time: Res<Time>,
    mut pontuacao: ResMut<PontuacaoGlobal>,
) {
    pontuacao.timer_spawn.tick(time.delta());

    if pontuacao.timer_spawn.just_finished() {
        let mut rng = rand::thread_rng();
        let x = rng.gen_range(-500.0..500.0);
        let tamanho = rng.gen_range(20.0..60.0);
        let velocidade = rng.gen_range(100.0..350.0);

        commands.spawn((
            Sprite {
                color: Color::srgb(0.9, 0.3, 0.2),
                custom_size: Some(Vec2::splat(tamanho)),
                ..default()
            },
            Transform::from_xyz(x, 400.0, 0.0),
            Asteroide { velocidade },
        ));
    }
}

/// Movimenta asteroides para baixo e remove os que saíram da tela.
fn mover_asteroides(
    mut commands: Commands,
    time: Res<Time>,
    mut query: Query<(Entity, &mut Transform, &Asteroide)>,
    mut pontuacao: ResMut<PontuacaoGlobal>,
) {
    for (entity, mut transform, asteroide) in &mut query {
        transform.translation.y -= asteroide.velocidade * time.delta_secs();

        // Rotação visual
        transform.rotate_z(2.0 * time.delta_secs());

        // Remover asteroides que saíram da tela (e contar pontos)
        if transform.translation.y < -450.0 {
            commands.entity(entity).despawn();
            pontuacao.valor += 10;
        }
    }
}

/// Detecta colisão entre jogador e asteroides.
fn detectar_colisao(
    mut commands: Commands,
    jogador_query: Query<&Transform, With<Jogador>>,
    asteroide_query: Query<(Entity, &Transform, &Sprite), With<Asteroide>>,
    mut pontuacao: ResMut<PontuacaoGlobal>,
) {
    let jogador_pos = jogador_query.single().translation;

    for (entity, asteroide_transform, sprite) in &asteroide_query {
        let tamanho = sprite.custom_size.unwrap_or(Vec2::splat(30.0)).x;
        let distancia = jogador_pos.distance(asteroide_transform.translation);

        if distancia < (tamanho / 2.0 + 20.0) {
            // Colisão detectada — remover asteroide e penalizar
            commands.entity(entity).despawn();
            pontuacao.valor = pontuacao.valor.saturating_sub(50);
        }
    }
}

/// Atualiza o texto de pontuação na tela.
fn atualizar_pontuacao(
    pontuacao: Res<PontuacaoGlobal>,
    mut query: Query<(&mut Text, &Pontuacao)>,
) {
    if pontuacao.is_changed() {
        for (mut texto, _) in &mut query {
            **texto = format!("Pontos: {}", pontuacao.valor);
        }
    }
}

// === Main ===

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                title: "Esquiva Espacial — Rust com Bevy".to_string(),
                resolution: (1280.0, 720.0).into(),
                ..default()
            }),
            ..default()
        }))
        .add_systems(Startup, setup)
        .add_systems(Update, (
            mover_jogador,
            gerar_asteroides,
            mover_asteroides,
            detectar_colisao,
            atualizar_pontuacao,
        ))
        .run();
}
```

## Jogo 2D Simples com macroquad

Para protótipos rápidos, macroquad oferece uma API minimalista:

```toml
[dependencies]
macroquad = "0.4"
```

```rust
use macroquad::prelude::*;

struct Bola {
    x: f32,
    y: f32,
    vx: f32,
    vy: f32,
    raio: f32,
}

#[macroquad::main("Pong — Rust")]
async fn main() {
    let mut jogador_y: f32 = screen_height() / 2.0;
    let largura_raquete = 15.0;
    let altura_raquete = 100.0;

    let mut bola = Bola {
        x: screen_width() / 2.0,
        y: screen_height() / 2.0,
        vx: 300.0,
        vy: 200.0,
        raio: 10.0,
    };

    let mut pontos: u32 = 0;

    loop {
        clear_background(Color::from_rgba(20, 20, 40, 255));

        let dt = get_frame_time();

        // Input do jogador
        if is_key_down(KeyCode::Up) {
            jogador_y -= 400.0 * dt;
        }
        if is_key_down(KeyCode::Down) {
            jogador_y += 400.0 * dt;
        }
        jogador_y = jogador_y.clamp(0.0, screen_height() - altura_raquete);

        // Mover bola
        bola.x += bola.vx * dt;
        bola.y += bola.vy * dt;

        // Colisão com paredes
        if bola.y - bola.raio < 0.0 || bola.y + bola.raio > screen_height() {
            bola.vy *= -1.0;
        }

        // Colisão com raquete
        if bola.x - bola.raio < 30.0 + largura_raquete
            && bola.y > jogador_y
            && bola.y < jogador_y + altura_raquete
        {
            bola.vx = bola.vx.abs();
            bola.vx *= 1.05; // Acelerar a cada rebatida
            pontos += 1;
        }

        // Bola saiu pela direita — rebater de volta
        if bola.x + bola.raio > screen_width() {
            bola.vx *= -1.0;
        }

        // Bola saiu pela esquerda — reiniciar
        if bola.x < 0.0 {
            bola.x = screen_width() / 2.0;
            bola.y = screen_height() / 2.0;
            bola.vx = 300.0;
            bola.vy = 200.0;
            pontos = 0;
        }

        // Desenhar
        draw_rectangle(30.0, jogador_y, largura_raquete, altura_raquete, BLUE);
        draw_circle(bola.x, bola.y, bola.raio, WHITE);

        // Linha central
        for i in (0..screen_height() as i32).step_by(20) {
            draw_line(
                screen_width() / 2.0, i as f32,
                screen_width() / 2.0, (i + 10) as f32,
                1.0, GRAY,
            );
        }

        // Pontuação
        draw_text(
            &format!("Pontos: {}", pontos),
            screen_width() / 2.0 - 60.0,
            40.0,
            30.0,
            WHITE,
        );

        next_frame().await;
    }
}
```

## wgpu: Renderização de Baixo Nível

Para quem precisa de controle total sobre a GPU, wgpu fornece uma API segura sobre Vulkan, Metal e DirectX 12:

```toml
[dependencies]
wgpu = "24"
winit = "0.30"
pollster = "0.4"
```

wgpu é usado internamente pelo Bevy e é a base para renderização cross-platform em Rust, incluindo suporte a WebGPU no navegador.

## Empresas e Projetos Usando Rust em Gamedev

- **Embark Studios**: Estúdio de jogos AAA (fundado por ex-DICE/EA) que usa Rust como linguagem principal
- **Ready at Dawn**: Componentes de engine em Rust
- **Veloren**: MMORPG voxel open-source escrito inteiramente em Rust
- **Fish Fight**: Jogo multiplayer 2D construído com macroquad
- **A/B Street**: Simulador de tráfego urbano open-source em Rust
- **Tiny Glade**: Jogo relaxante de construção que usa Bevy para ferramentas internas
- **Ambient**: Runtime de jogos multiplayer em Rust + Wasm

## O Game Loop em Rust

Entender o game loop é fundamental. Em Bevy, ele é gerenciado automaticamente, mas o conceito é:

```rust
// Conceitual — o Bevy implementa isso internamente
loop {
    // 1. Processar input (teclado, mouse, gamepad)
    processar_input();

    // 2. Atualizar lógica do jogo (física, IA, gameplay)
    // Usa delta_time para independência de framerate
    atualizar_logica(delta_time);

    // 3. Renderizar frame
    renderizar();

    // 4. Apresentar na tela
    apresentar_frame();
}
```

Bevy divide isso em **schedules**: `PreUpdate`, `Update`, `PostUpdate`, `FixedUpdate` (para física), e cada system é automaticamente paralelizado quando possível.

## Como Começar

1. **Aprenda Rust primeiro**: Domine ownership e traits — [tutorial de primeiros passos](/tutoriais/primeiros-passos/) e [traits e generics](/tutoriais/traits-generics/)
2. **Comece com macroquad**: API simples, resultado visual rápido, compila para Wasm
3. **Migre para Bevy**: Quando precisar de mais estrutura, ECS e ecossistema de plugins
4. **Meça performance**: Use a [receita de medir tempo de execução](/receitas/medir-tempo-execucao/) para profiling
5. **Explore Wasm**: Publique seus jogos no navegador com [Rust para WebAssembly](/artigos/rust-para-webassembly/)
6. **Estude ECS**: Entenda Entity Component System — é o padrão da indústria moderna

## Conclusão

Rust está se tornando uma opção cada vez mais viável para desenvolvimento de jogos. Bevy oferece uma experiência de desenvolvimento moderna com ECS, hot-reloading e uma comunidade ativa. Para jogos 2D, protótipos e game jams, frameworks como macroquad e ggez permitem começar em minutos. E para quem precisa de controle total, wgpu fornece acesso direto à GPU com segurança de memória. O futuro do gamedev em Rust é promissor.

---

## Veja Também

- [Receita: Medir Tempo de Execução](/receitas/medir-tempo-execucao/) — Profiling para otimizar seu game loop
- [Tutorial: Structs, Enums e Pattern Matching](/tutoriais/structs-enums-pattern-matching/) — Fundamento para ECS
- [Tutorial: Traits e Generics](/tutoriais/traits-generics/) — Essencial para entender Bevy
- [Rust para WebAssembly](/artigos/rust-para-webassembly/) — Publique jogos no navegador
- [Rust para Data Science](/artigos/rust-para-data-science/) — ndarray para cálculos matemáticos em jogos
- [Tutorial: Concorrência](/tutoriais/concorrencia/) — Processamento paralelo para engines

---

Se você se interessa por desenvolvimento de jogos em outras linguagens, confira também:

- <a href="https://python.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Pygame: desenvolvimento de jogos 2D com Python</a>
- <a href="https://ziglang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'ziglang.com.br' })">Zig para Game Dev: controle de baixo nível e performance para jogos</a>
