diff --git a/examples/games/contributors.rs b/examples/games/contributors.rs index 545f8babb7..2d653d496a 100644 --- a/examples/games/contributors.rs +++ b/examples/games/contributors.rs @@ -1,7 +1,8 @@ //! This example displays each contributor to the bevy source code as a bouncing bevy-ball. use bevy::{math::bounding::Aabb2d, prelude::*, utils::HashMap}; -use rand::{prelude::SliceRandom, Rng}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; use std::{ env::VarError, hash::{DefaultHasher, Hash, Hasher}, @@ -13,13 +14,14 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .init_resource::() + .init_resource::() .add_systems(Startup, (setup_contributor_selection, setup)) - .add_systems(Update, (gravity, movement, collisions, selection)) + // Systems are chained for determinism only + .add_systems(Update, (gravity, movement, collisions, selection).chain()) .run(); } -// Store contributors with their commit count in a collection that preserves the uniqueness -type Contributors = HashMap; +type Contributors = Vec<(String, usize)>; #[derive(Resource)] struct ContributorSelection { @@ -55,26 +57,34 @@ struct Velocity { rotation: f32, } +// We're using a shared seeded RNG here to make this example deterministic for testing purposes. +// This isn't strictly required in practical use unless you need your app to be deterministic. +#[derive(Resource, Deref, DerefMut)] +struct SharedRng(ChaCha8Rng); +impl Default for SharedRng { + fn default() -> Self { + Self(ChaCha8Rng::seed_from_u64(10223163112)) + } +} + const GRAVITY: f32 = 9.821 * 100.0; const SPRITE_SIZE: f32 = 75.0; const SELECTED: Hsla = Hsla::hsl(0.0, 0.9, 0.7); const DESELECTED: Hsla = Hsla::new(0.0, 0.3, 0.2, 0.92); +const SELECTED_Z_OFFSET: f32 = 100.0; + const SHOWCASE_TIMER_SECS: f32 = 3.0; const CONTRIBUTORS_LIST: &[&str] = &["Carter Anderson", "And Many More"]; -fn setup_contributor_selection(mut commands: Commands, asset_server: Res) { - // Load contributors from the git history log or use default values from - // the constant array. Contributors are stored in a HashMap with their - // commit count. - let contribs = contributors().unwrap_or_else(|_| { - CONTRIBUTORS_LIST - .iter() - .map(|name| (name.to_string(), 1)) - .collect() - }); +fn setup_contributor_selection( + mut commands: Commands, + asset_server: Res, + mut rng: ResMut, +) { + let contribs = contributors_or_fallback(); let texture_handle = asset_server.load("branding/icon.png"); @@ -83,11 +93,12 @@ fn setup_contributor_selection(mut commands: Commands, asset_server: Res, mut velocity_query: Query<&mut Velocity>) { fn collisions( window: Single<&Window>, mut query: Query<(&mut Velocity, &mut Transform), With>, + mut rng: ResMut, ) { let window_size = window.size(); @@ -253,8 +263,6 @@ fn collisions( let max_bounce_height = (window_size.y - SPRITE_SIZE * 2.0).max(0.0); let min_bounce_height = max_bounce_height * 0.4; - let mut rng = rand::thread_rng(); - for (mut velocity, mut transform) in &mut query { // Clamp the translation to not go out of the bounds if transform.translation.y < collision_area.min.y { @@ -333,7 +341,26 @@ fn contributors() -> Result { }, ); - Ok(contributors) + Ok(contributors.into_iter().collect()) +} + +/// Get the contributors list, or fall back to a default value if +/// it's unavailable or we're in CI +fn contributors_or_fallback() -> Contributors { + let get_default = || { + CONTRIBUTORS_LIST + .iter() + .cycle() + .take(1000) + .map(|name| (name.to_string(), 1)) + .collect() + }; + + if cfg!(feature = "bevy_ci_testing") { + return get_default(); + } + + contributors().unwrap_or_else(|_| get_default()) } /// Give each unique contributor name a particular hue that is stable between runs.