🎯 Exemples recommandés
Balanced sample collections from various categories for you to explore
Exemples Bevy Game Engine
Exemples du moteur de jeu Bevy Rust avec ECS, graphiques 2D/3D, audio et mécaniques de jeu
💻 Bevy Hello World rust
🟢 simple
⭐⭐
Configuration d'application Bevy de base avec création de fenêtre et introduction à l'architecture ECS
⏱️ 15 min
🏷️ bevy, rust, gamedev, ecs
Prerequisites:
Basic Rust knowledge, ECS (Entity Component System) concepts
// Bevy Hello World - Basic Game Engine Setup
// Cargo.toml dependencies:
// bevy = "0.13"
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, hello_world_system)
.run();
}
#[derive(Component)]
struct Player;
#[derive(Component)]
struct Name(String);
#[derive(Resource)]
struct GameState {
score: u32,
level: u32,
}
fn setup(mut commands: Commands) {
// Spawn 2D camera
commands.spawn(Camera2dBundle::default());
// Initialize game state resource
commands.insert_resource(GameState {
score: 0,
level: 1,
});
// Create player entity
commands.spawn((
SpriteBundle {
sprite: Sprite {
color: Color::rgb(0.25, 0.25, 0.75),
custom_size: Some(Vec2::new(50.0, 50.0)),
..default()
},
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..default()
},
Player,
Name("Player".to_string()),
));
// Create some sprites
for i in 0..5 {
commands.spawn((
SpriteBundle {
sprite: Sprite {
color: Color::hsl(i as f32 * 72.0, 0.7, 0.5),
custom_size: Some(Vec2::new(30.0, 30.0)),
..default()
},
transform: Transform::from_xyz(
(i as f32 - 2.0) * 100.0,
150.0,
0.0
),
..default()
},
Name(format!("Sprite {}", i + 1)),
));
}
println!("Game initialized! Bevy ECS systems are running.");
}
fn hello_world_system(
time: Res<Time>,
mut game_state: ResMut<GameState>,
query: Query<&Name>,
) {
// Update game state
game_state.score += 1;
// Print hello world with game info
if time.elapsed_seconds() as u32 % 2 == 0 {
println!("Hello from Bevy Game Engine!");
println!("Score: {}, Level: {}", game_state.score, game_state.level);
println!("Active entities: {}", query.iter().count());
}
}
💻 Jeu de Plateformes 2D rust
🟡 intermediate
⭐⭐⭐
Jeu de plateformes 2D complet avec mouvement du joueur, sauts, détection de collisions et conception de niveaux
⏱️ 30 min
🏷️ bevy, rust, gamedev, platformer, 2d
Prerequisites:
Rust basics, Bevy ECS fundamentals, Basic game physics
// Bevy 2D Platformer Game
// Features: Player movement, jumping, physics, collision detection
use bevy::prelude::*;
const GRAVITY: f32 = -980.0;
const PLAYER_SPEED: f32 = 200.0;
const JUMP_FORCE: f32 = 400.0;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(ClearColor(Color::rgb(0.53, 0.81, 0.98)))
.add_event::<JumpEvent>()
.add_systems(Startup, setup)
.add_systems(
Update,
(
player_movement,
player_jump,
apply_gravity,
check_ground_collision,
camera_follow_player,
).chain(),
)
.run();
}
#[derive(Component)]
struct Player {
velocity: Vec2,
is_grounded: bool,
}
#[derive(Component)]
struct Ground;
#[derive(Component)]
struct Platform;
#[derive(Event)]
struct JumpEvent;
#[derive(Bundle)]
struct PlayerBundle {
player: Player,
sprite_bundle: SpriteBundle,
}
impl PlayerBundle {
fn new(x: f32, y: f32) -> Self {
Self {
player: Player {
velocity: Vec2::ZERO,
is_grounded: false,
},
sprite_bundle: SpriteBundle {
sprite: Sprite {
color: Color::rgb(0.3, 0.3, 0.8),
custom_size: Some(Vec2::new(40.0, 60.0)),
..default()
},
transform: Transform::from_xyz(x, y, 1.0),
..default()
},
}
}
}
fn setup(mut commands: Commands) {
// Spawn camera
commands.spawn(Camera2dBundle::default());
// Spawn player
commands.spawn(PlayerBundle::new(0.0, 100.0));
// Create ground
commands.spawn((
SpriteBundle {
sprite: Sprite {
color: Color::rgb(0.2, 0.7, 0.2),
custom_size: Some(Vec2::new(800.0, 50.0)),
..default()
},
transform: Transform::from_xyz(0.0, -250.0, 0.0),
..default()
},
Ground,
));
// Create platforms
let platforms = [
(-200.0, 0.0, 120.0, 20.0),
(200.0, 50.0, 120.0, 20.0),
(0.0, 150.0, 100.0, 20.0),
(-300.0, 200.0, 80.0, 20.0),
(300.0, 250.0, 100.0, 20.0),
];
for (x, y, width, height) in platforms {
commands.spawn((
SpriteBundle {
sprite: Sprite {
color: Color::rgb(0.6, 0.4, 0.2),
custom_size: Some(Vec2::new(width, height)),
..default()
},
transform: Transform::from_xyz(x, y, 0.0),
..default()
},
Platform,
));
}
}
fn player_movement(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut player_query: Query<&mut Player, With<Player>>,
time: Res<Time>,
) {
for mut player in player_query.iter_mut() {
let mut direction = 0.0;
if keyboard_input.pressed(KeyCode::ArrowLeft) {
direction -= 1.0;
}
if keyboard_input.pressed(KeyCode::ArrowRight) {
direction += 1.0;
}
player.velocity.x = direction * PLAYER_SPEED;
}
}
fn player_jump(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut jump_events: EventWriter<JumpEvent>,
player_query: Query<&Player, With<Player>>,
) {
if let Ok(player) = player_query.get_single() {
if keyboard_input.just_pressed(KeyCode::Space) && player.is_grounded {
jump_events.send(JumpEvent);
}
}
}
fn apply_gravity(
mut player_query: Query<(&mut Player, &mut Transform)>,
time: Res<Time>,
) {
for (mut player, mut transform) in player_query.iter_mut() {
player.velocity.y += GRAVITY * time.delta_seconds();
transform.translation.y += player.velocity.y * time.delta_seconds();
transform.translation.x += player.velocity.x * time.delta_seconds();
}
}
fn check_ground_collision(
mut jump_events: EventReader<JumpEvent>,
mut player_query: Query<(&mut Player, &Transform), Without<Ground>>,
ground_query: Query<(Entity, &Transform), With<Ground>>,
platform_query: Query<(Entity, &Transform), With<Platform>>,
) {
for (mut player, player_transform) in player_query.iter_mut() {
let player_bottom = player_transform.translation.y - 30.0;
// Check ground collision
for (_, ground_transform) in ground_query.iter() {
let ground_top = ground_transform.translation.y + 25.0;
if player_bottom <= ground_top
&& player_bottom >= ground_top - 10.0
&& player.velocity.y <= 0.0 {
player.velocity.y = 0.0;
player.is_grounded = true;
return;
}
}
// Check platform collision
for (_, platform_transform) in platform_query.iter() {
let platform_top = platform_transform.translation.y + 10.0;
let platform_left = platform_transform.translation.x - platform_transform.scale.x / 2.0;
let platform_right = platform_transform.translation.x + platform_transform.scale.x / 2.0;
let player_x = player_transform.translation.x;
if player_bottom <= platform_top
&& player_bottom >= platform_top - 10.0
&& player_x >= platform_left
&& player_x <= platform_right
&& player.velocity.y <= 0.0 {
player.velocity.y = 0.0;
player.is_grounded = true;
return;
}
}
player.is_grounded = false;
}
// Handle jump events
for _event in jump_events.read() {
for (mut player, _) in player_query.iter_mut() {
if player.is_grounded {
player.velocity.y = JUMP_FORCE;
player.is_grounded = false;
}
}
}
}
fn camera_follow_player(
player_query: Query<&Transform, (With<Player>, Without<Camera>)>,
mut camera_query: Query<&mut Transform, (With<Camera>, Without<Player>)>,
) {
if let (Ok(player_transform), Ok(mut camera_transform)) =
(player_query.get_single(), camera_query.get_single_mut()) {
let target_x = player_transform.translation.x;
let current_x = camera_transform.translation.x;
// Smooth camera follow
let new_x = current_x + (target_x - current_x) * 0.1;
camera_transform.translation.x = new_x;
}
}
💻 Scène 3D avec Éclairage rust
🟡 intermediate
⭐⭐⭐⭐
Création de scènes 3D avec modèles, éclairage, matériaux et contrôles de caméra
⏱️ 25 min
🏷️ bevy, rust, gamedev, 3d, graphics
Prerequisites:
3D graphics basics, Bevy 3D concepts, Linear algebra
// Bevy 3D Scene with Lighting and Camera Controls
// Features: 3D primitives, lighting, materials, camera orbit
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(ClearColor(Color::rgb(0.1, 0.1, 0.15)))
.add_systems(Startup, setup)
.add_systems(Update, (camera_orbit, animate_objects))
.run();
}
#[derive(Component)]
struct RotatingObject {
speed: f32,
}
#[derive(Component)]
struct MainCamera {
focus: Vec3,
distance: f32,
angle: f32,
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// Spawn camera
commands.spawn((
Camera3dBundle {
transform: Transform::from_xyz(0.0, 5.0, 8.0)
.looking_at(Vec3::ZERO, Vec3::Y),
..default()
},
MainCamera {
focus: Vec3::ZERO,
distance: 8.0,
angle: 0.0,
},
));
// Add directional light (sun)
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: 10000.0,
shadows_enabled: true,
..default()
},
transform: Transform::from_rotation(Quat::from_euler(
EulerRot::XYZ,
-0.5,
0.5,
0.0,
)),
..default()
});
// Add ambient light
commands.insert_resource(AmbientLight {
color: Color::rgb(0.2, 0.2, 0.3),
brightness: 0.3,
});
// Create ground plane
commands.spawn(PbrBundle {
mesh: meshes.add(Circle::new(7.0)),
material: materials.add(StandardMaterial {
base_color: Color::rgb(0.1, 0.3, 0.1),
perceptual_roughness: 0.8,
metallic: 0.0,
..default()
}),
transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
..default()
});
// Create central cube
commands.spawn((
PbrBundle {
mesh: meshes.add(Cuboid::new(2.0, 2.0, 2.0)),
material: materials.add(StandardMaterial {
base_color: Color::rgb(0.8, 0.7, 0.6),
metallic: 0.1,
perceptual_roughness: 0.4,
..default()
}),
transform: Transform::from_xyz(0.0, 1.0, 0.0),
..default()
},
RotatingObject { speed: 0.5 },
));
// Create spheres in circle
for i in 0..8 {
let angle = i as f32 * std::f32::consts::TAU / 8.0;
let x = angle.cos() * 4.0;
let z = angle.sin() * 4.0;
commands.spawn((
PbrBundle {
mesh: meshes.add(Sphere::new(0.5)),
material: materials.add(StandardMaterial {
base_color: Color::hsl(i as f32 * 45.0, 0.7, 0.5),
metallic: 0.2,
perceptual_roughness: 0.3,
..default()
}),
transform: Transform::from_xyz(x, 0.5, z),
..default()
},
RotatingObject { speed: 0.3 + (i as f32 * 0.1) },
));
}
// Create torus
commands.spawn((
PbrBundle {
mesh: meshes.add(Torus {
major_radius: 1.5,
minor_radius: 0.3,
}),
material: materials.add(StandardMaterial {
base_color: Color::rgb(0.9, 0.4, 0.4),
metallic: 0.6,
perceptual_roughness: 0.2,
..default()
}),
transform: Transform::from_xyz(-3.0, 2.0, 0.0),
..default()
},
RotatingObject { speed: 0.8 },
));
// Create cylinder
commands.spawn((
PbrBundle {
mesh: meshes.add(Cylinder::new(0.5, 3.0)),
material: materials.add(StandardMaterial {
base_color: Color::rgb(0.4, 0.4, 0.9),
metallic: 0.3,
perceptual_roughness: 0.5,
..default()
}),
transform: Transform::from_xyz(3.0, 1.5, 0.0),
..default()
},
RotatingObject { speed: 0.4 },
));
// Create icosphere
commands.spawn((
PbrBundle {
mesh: meshes.add(Icosphere::new(0.8, 5)),
material: materials.add(StandardMaterial {
base_color: Color::rgb(0.9, 0.8, 0.3),
metallic: 0.7,
perceptual_roughness: 0.1,
..default()
}),
transform: Transform::from_xyz(0.0, 3.0, -2.0),
..default()
),
RotatingObject { speed: 0.6 },
));
}
fn camera_orbit(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut camera_query: Query<&mut Transform, With<MainCamera>>,
mut camera: ResMut<MainCamera>,
time: Res<Time>,
) {
if let Ok(mut transform) = camera_query.get_single_mut() {
// Rotate camera around focus point
if keyboard_input.pressed(KeyCode::ArrowLeft) {
camera.angle -= 1.5 * time.delta_seconds();
}
if keyboard_input.pressed(KeyCode::ArrowRight) {
camera.angle += 1.5 * time.delta_seconds();
}
// Zoom in/out
if keyboard_input.pressed(KeyCode::ArrowUp) {
camera.distance = (camera.distance - 2.0 * time.delta_seconds()).max(3.0);
}
if keyboard_input.pressed(KeyCode::ArrowDown) {
camera.distance = (camera.distance + 2.0 * time.delta_seconds()).min(20.0);
}
// Move focus point
let mut focus_change = Vec3::ZERO;
if keyboard_input.pressed(KeyCode::KeyW) {
focus_change.z -= 1.0;
}
if keyboard_input.pressed(KeyCode::KeyS) {
focus_change.z += 1.0;
}
if keyboard_input.pressed(KeyCode::KeyA) {
focus_change.x -= 1.0;
}
if keyboard_input.pressed(KeyCode::KeyD) {
focus_change.x += 1.0;
}
if keyboard_input.pressed(KeyCode::KeyQ) {
focus_change.y -= 1.0;
}
if keyboard_input.pressed(KeyCode::KeyE) {
focus_change.y += 1.0;
}
camera.focus += focus_change * 2.0 * time.delta_seconds();
// Update camera position
let x = camera.angle.cos() * camera.distance;
let z = camera.angle.sin() * camera.distance;
transform.translation = camera.focus + Vec3::new(x, 5.0, z);
transform.look_at(camera.focus, Vec3::Y);
}
}
fn animate_objects(
mut objects_query: Query<(&mut Transform, &RotatingObject)>,
time: Res<Time>,
) {
for (mut transform, rotating) in objects_query.iter_mut() {
transform.rotate_y(rotating.speed * time.delta_seconds());
transform.rotate_x(rotating.speed * 0.7 * time.delta_seconds());
// Add floating motion
let float_offset = time.elapsed_seconds() * rotating.speed;
transform.translation.y += (float_offset).sin() * 0.01;
}
}
💻 Système Audio rust
🟡 intermediate
⭐⭐⭐
Système complet de gestion audio avec effets sonores, musique de fond et audio spatial
⏱️ 20 min
🏷️ bevy, rust, gamedev, audio, sound
Prerequisites:
Bevy basics, Audio fundamentals, Event systems
// Bevy Audio System with Sound Effects and Music
// Features: Sound manager, spatial audio, audio mixing
use bevy::prelude::*;
// Asset paths for audio files
const JUMP_SOUND: &str = "sounds/jump.wav";
const COIN_SOUND: &str = "sounds/coin.wav";
const BACKGROUND_MUSIC: &str = "music/background.ogg";
const EXPLOSION_SOUND: &str = "sounds/explosion.wav";
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Bevy Audio System".into(),
resolution: (800.0, 600.0).into(),
..default()
}),
..default()
}))
.add_event::<PlaySoundEvent>()
.add_event::<ChangeMusicEvent>()
.insert_resource(AudioState {
music_volume: 0.7,
sfx_volume: 0.8,
is_muted: false,
current_music: None,
})
.add_systems(Startup, setup)
.add_systems(Update, (
handle_sound_events,
handle_music_events,
handle_keyboard_input,
update_audio_state,
spawn_sound_effects,
))
.run();
}
#[derive(Resource)]
struct AudioState {
music_volume: f32,
sfx_volume: f32,
is_muted: bool,
current_music: Option<Handle<AudioSink>>,
}
#[derive(Event)]
struct PlaySoundEvent {
sound: SoundType,
position: Option<Vec3>,
}
#[derive(Event)]
struct ChangeMusicEvent {
music: MusicType,
fade_duration: Option<f32>,
}
#[derive(Component)]
struct AudioSource {
sound_type: SoundType,
cooldown: Timer,
}
#[derive(Component)]
struct MusicController;
#[derive(Clone, Copy, PartialEq)]
enum SoundType {
Jump,
Coin,
Explosion,
Footstep,
}
#[derive(Clone, Copy, PartialEq)]
enum MusicType {
MainMenu,
Gameplay,
Victory,
BossBattle,
}
#[derive(Component)]
struct SpatialSound {
max_distance: f32,
reference_distance: f32,
}
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
// Spawn 2D camera
commands.spawn(Camera2dBundle::default());
// Create UI for audio controls
commands
.spawn(NodeBundle {
style: Style {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
padding: UiRect::all(Val::Px(20.0)),
..default()
},
..default()
})
.with_children(|parent| {
// Title
parent.spawn(TextBundle::from_section(
"Audio System Demo",
TextStyle {
font_size: 40.0,
color: Color::WHITE,
..default()
},
));
// Instructions
parent.spawn(TextBundle::from_section(
"Controls:
Space - Play Jump Sound
C - Play Coin Sound
E - Play Explosion Sound
M - Toggle Music
Arrow Up/Down - Music Volume
Arrow Left/Right - SFX Volume
P - Mute/Unmute",
TextStyle {
font_size: 20.0,
color: Color::rgb(0.8, 0.8, 0.8),
..default()
},
));
// Status display will be added here
});
// Load audio assets
asset_server.load(JUMP_SOUND);
asset_server.load(COIN_SOUND);
asset_server.load(EXPLOSION_SOUND);
asset_server.load(BACKGROUND_MUSIC);
// Spawn some sound source objects
spawn_sound_objects(&mut commands);
}
fn spawn_sound_objects(mut commands: Commands) {
// Create objects that can emit sounds
let objects = [
(Vec3::new(-200.0, 0.0, 0.0), Color::RED),
(Vec3::new(0.0, 0.0, 0.0), Color::GREEN),
(Vec3::new(200.0, 0.0, 0.0), Color::BLUE),
];
for (pos, color) in objects {
commands.spawn((
SpriteBundle {
sprite: Sprite {
color,
custom_size: Some(Vec2::new(50.0, 50.0)),
..default()
},
transform: Transform::from_xyz(pos.x, pos.y, pos.z),
..default()
},
AudioSource {
sound_type: SoundType::Coin,
cooldown: Timer::from_seconds(1.0, TimerMode::Once),
},
SpatialSound {
max_distance: 300.0,
reference_distance: 50.0,
},
));
}
}
fn handle_sound_events(
mut commands: Commands,
mut sound_events: EventReader<PlaySoundEvent>,
asset_server: Res<AssetServer>,
audio_state: Res<AudioState>,
) {
for event in sound_events.read() {
if audio_state.is_muted {
continue;
}
let sound_path = match event.sound {
SoundType::Jump => JUMP_SOUND,
SoundType::Coin => COIN_SOUND,
SoundType::Explosion => EXPLOSION_SOUND,
SoundType::Footstep => "sounds/footstep.wav",
};
let sound_handle: Handle<AudioSource> = asset_server.load(sound_path);
match event.position {
Some(pos) => {
// Spatial audio
commands.spawn(SpatialAudioBundle {
source: sound_handle,
settings: PlaybackSettings::DESPAWN,
spatial: SpatialSettings {
gain: 1.0,
..default()
},
transform: Transform::from_translation(pos),
});
}
None => {
// 2D audio (UI sounds)
commands.spawn(AudioBundle {
source: sound_handle,
settings: PlaybackSettings {
volume: audio_state.sfx_volume,
..default()
},
});
}
}
}
}
fn handle_music_events(
mut commands: Commands,
mut music_events: EventReader<ChangeMusicEvent>,
asset_server: Res<AssetServer>,
) {
for event in music_events.read() {
let music_path = match event.music {
MusicType::MainMenu => "music/menu.ogg",
MusicType::Gameplay => BACKGROUND_MUSIC,
MusicType::Victory => "music/victory.ogg",
MusicType::BossBattle => "music/boss.ogg",
};
commands.spawn((
AudioBundle {
source: asset_server.load(music_path),
settings: PlaybackSettings::LOOP,
},
MusicController,
));
}
}
fn handle_keyboard_input(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut sound_events: EventWriter<PlaySoundEvent>,
mut music_events: EventWriter<ChangeMusicEvent>,
mut audio_state: ResMut<AudioState>,
) {
// Sound effects
if keyboard_input.just_pressed(KeyCode::Space) {
sound_events.send(PlaySoundEvent {
sound: SoundType::Jump,
position: None,
});
}
if keyboard_input.just_pressed(KeyCode::KeyC) {
sound_events.send(PlaySoundEvent {
sound: SoundType::Coin,
position: None,
});
}
if keyboard_input.just_pressed(KeyCode::KeyE) {
sound_events.send(PlaySoundEvent {
sound: SoundType::Explosion,
position: None,
});
}
// Music control
if keyboard_input.just_pressed(KeyCode::KeyM) {
music_events.send(ChangeMusicEvent {
music: MusicType::Gameplay,
fade_duration: Some(2.0),
});
}
// Volume control
if keyboard_input.pressed(KeyCode::ArrowUp) {
audio_state.music_volume = (audio_state.music_volume + 0.02).min(1.0);
}
if keyboard_input.pressed(KeyCode::ArrowDown) {
audio_state.music_volume = (audio_state.music_volume - 0.02).max(0.0);
}
if keyboard_input.pressed(KeyCode::ArrowRight) {
audio_state.sfx_volume = (audio_state.sfx_volume + 0.02).min(1.0);
}
if keyboard_input.pressed(KeyCode::ArrowLeft) {
audio_state.sfx_volume = (audio_state.sfx_volume - 0.02).max(0.0);
}
// Mute toggle
if keyboard_input.just_pressed(KeyCode::KeyP) {
audio_state.is_muted = !audio_state.is_muted;
}
}
fn update_audio_state(
audio_state: Res<AudioState>,
mut music_controllers: Query<&mut AudioSink, With<MusicController>>,
audio_sinks: Res<Assets<AudioSink>>,
) {
// Update music volume
for mut sink in music_controllers.iter_mut() {
if let Some(sink) = audio_sinks.get(&sink) {
sink.set_volume(audio_state.music_volume);
}
}
}
fn spawn_sound_effects(
mut commands: Commands,
audio_sources: Query<(&Transform, &mut AudioSource, &SpatialSound)>,
time: Res<Time>,
mut sound_events: EventWriter<PlaySoundEvent>,
) {
for (transform, mut audio_source, spatial) in audio_sources.iter_mut() {
audio_source.cooldown.tick(time.delta());
// Randomly emit sounds from objects
if audio_source.cooldown.finished() {
if fastrand::f32() < 0.01 { // 1% chance per frame
sound_events.send(PlaySoundEvent {
sound: audio_source.sound_type,
position: Some(transform.translation),
});
audio_source.cooldown.reset();
}
}
}
}