Noções Básicas de Desenvolvimento de Jogos
9 exemplos para você começar com o desenvolvimento de jogos em Rust - 7 básicos e 2 intermediários.
Pré-requisitos
cargo new my_game && cd my_game
cargo add bevyFerramentas: Os exemplos usam Bevy na Rust 1.97.0 (edição 2024). Fixe a versão do Bevy em
Cargo.toml- as APIs evoluem a cada lançamento.
Exemplos Básicos
1. App Bevy Mínimo
Janela + plugins padrão inicializam a renderização e a entrada.
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
}DefaultPluginsagrupa plugins de janela, renderização, entrada e assets.- Sistemas
Startuprodam uma vez;Updateroda a cada frame. Commandsenfileira spawns/despawns a serem aplicados posteriormente na agenda.
Relacionado: Bevy Engine
2. Spawnar uma entidade sprite
Entidades são IDs com pacotes de componentes.
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
Sprite::from_image(asset_server.load("player.png")),
Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
));
}- Componentes são dados puros; o comportamento reside em sistemas que os consultam.
AssetServercarrega assincronamente - trateLoadStatepara placeholders.- A posição
Transformusa coordenadas de mão direita; 2D frequentemente usa o plano XY.
Relacionado: Assets e Cenas
3. Mover com entrada do teclado
Leia o recurso de entrada em um sistema Update.
fn player_move(
keyboard: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut Transform, With<Player>>,
) {
let mut dir = Vec2::ZERO;
if keyboard.pressed(KeyCode::KeyW) { dir.y += 1.0; }
if keyboard.pressed(KeyCode::KeyS) { dir.y -= 1.0; }
if keyboard.pressed(KeyCode::KeyA) { dir.x -= 1.0; }
if keyboard.pressed(KeyCode::KeyD) { dir.x += 1.0; }
for mut transform in &mut query {
transform.translation += (dir.normalize_or_zero() * 200.0).extend(0.0) * 0.016;
}
}ButtonInputsubstitui a APIInputmais antiga em versões recentes do Bevy.- Multiplique o movimento pelo delta time para independência da taxa de quadros (o exemplo usa aproximadamente 0.016).
- Componentes de marcação (
Player) filtram consultas.
Relacionado: Entrada e Eventos
4. Padrão de componente marcador
Tipos de tamanho zero marcam entidades para consultas.
#[derive(Component)]
struct Player;- Marcadores não carregam dados, mas habilitam filtros
With<Player>. - Prefira muitos componentes pequenos em vez de structs "deus" para ECS amigável ao cache.
- Derive
Componentpara registro no registro de tipos do app.
5. Recurso de Tempo
Frame e timestep fixo de Time.
fn debug_fps(time: Res<Time>) {
if time.elapsed_secs().fract() < 0.016 {
println!("delta: {:.4}", time.delta_secs());
}
}delta_secsvaria com o FPS - use para interpolação de renderização.Time<Fixed>fornece dt fixo para sistemas de física no scheduleFixedUpdate.- Nunca assuma 60 FPS na lógica do jogo.
Relacionado: Loops de Jogo e Tempo
6. Eventos para lógica desacoplada
Dispare e ouça sem acoplamento direto de sistemas.
#[derive(Event)]
struct DamageEvent {
amount: f32,
}
fn apply_damage(mut reader: EventReader<DamageEvent>) {
for e in reader.read() {
println!("damage {}", e.amount);
}
}- Eventos consumidos uma vez por frame por leitores padrão.
- Bom para UI, gatilhos de áudio e hooks de conquistas.
- Não use em excesso para caminhos críticos com milhares por frame.
Relacionado: Entrada e Eventos
7. Critérios de execução e estados
Fluxo de jogo com sistemas com escopo de estado.
#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)]
enum GameState {
#[default]
Menu,
Playing,
}OnEnter(GameState::Playing)spawna entidades de nível.OnExitlimpa para evitar vazamento de entidades.- Estados serializam conjuntos de sistemas de menu vs. jogabilidade.
Exemplos Intermediários
8. Schedule de atualização fixa para física
Separe o tick da física da taxa de quadros de renderização.
// Registre o sistema em FixedUpdate com Time<Fixed> para integração estável
// Exemplo: a integração de velocidade usa fixed_delta_seconds()- Física a 60 Hz fixa enquanto a renderização roda em FPS variável.
- Interpole as transformações renderizadas entre o estado de física anterior e o atual para visuais suaves.
- Veja a página de tempo dedicada para detalhes do padrão acumulador.
Relacionado: Loops de Jogo e Tempo
9. Passagem de renderização wgpu personalizada (conceito)
Renderização de nível inferior quando os materiais Bevy não são suficientes.
// wgpu: device.create_shader_module, render pass set_pipeline, draw indexed- Bevy usa wgpu internamente - um escape hatch via materiais personalizados ou nós de render graph.
- Útil para shaders de computação e efeitos procedurais.
- Maior complexidade - profile antes de sair das abstrações Bevy.
Relacionado: Gráficos com wgpu
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4 e Polars 0.46+.