mirror of
https://github.com/bevyengine/bevy
synced 2025-02-16 22:18:33 +00:00
Improve contributors example quality (#3258)
# Objective Fixes #3181 ## Solution Refactored `contributors.rs` example: - Renamed unclear variables - Split setup system into two separate systems Co-authored-by: CrazyRoka <rokarostuk@gmail.com>
This commit is contained in:
parent
38c7d5eb9e
commit
ca80fe65ed
1 changed files with 123 additions and 81 deletions
|
@ -1,14 +1,15 @@
|
||||||
use bevy::prelude::*;
|
use bevy::{prelude::*, utils::HashSet};
|
||||||
use rand::{prelude::SliceRandom, Rng};
|
use rand::{prelude::SliceRandom, Rng};
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeSet,
|
env::VarError,
|
||||||
io::{BufRead, BufReader},
|
io::{self, BufRead, BufReader},
|
||||||
process::Stdio,
|
process::Stdio,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_startup_system(setup_contributor_selection)
|
||||||
.add_startup_system(setup)
|
.add_startup_system(setup)
|
||||||
.add_system(velocity_system)
|
.add_system(velocity_system)
|
||||||
.add_system(move_system)
|
.add_system(move_system)
|
||||||
|
@ -17,7 +18,8 @@ fn main() {
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
type Contributors = BTreeSet<String>;
|
// Store contributors in a collection that preserves the uniqueness
|
||||||
|
type Contributors = HashSet<String>;
|
||||||
|
|
||||||
struct ContributorSelection {
|
struct ContributorSelection {
|
||||||
order: Vec<(String, Entity)>,
|
order: Vec<(String, Entity)>,
|
||||||
|
@ -52,19 +54,25 @@ const ALPHA: f32 = 0.92;
|
||||||
|
|
||||||
const SHOWCASE_TIMER_SECS: f32 = 3.0;
|
const SHOWCASE_TIMER_SECS: f32 = 3.0;
|
||||||
|
|
||||||
fn setup(
|
const CONTRIBUTORS_LIST: &[&str] = &["Carter Anderson", "And Many More"];
|
||||||
|
|
||||||
|
fn setup_contributor_selection(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
) {
|
) {
|
||||||
let contribs = contributors();
|
// Load contributors from the git history log or use default values from
|
||||||
|
// the constant array. Contributors must be unique, so they are stored in a HashSet
|
||||||
|
let contribs = contributors().unwrap_or_else(|_| {
|
||||||
|
CONTRIBUTORS_LIST
|
||||||
|
.iter()
|
||||||
|
.map(|name| name.to_string())
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
let texture_handle = asset_server.load("branding/icon.png");
|
let texture_handle = asset_server.load("branding/icon.png");
|
||||||
|
|
||||||
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
|
let mut contributor_selection = ContributorSelection {
|
||||||
commands.spawn_bundle(UiCameraBundle::default());
|
|
||||||
|
|
||||||
let mut sel = ContributorSelection {
|
|
||||||
order: vec![],
|
order: vec![],
|
||||||
idx: 0,
|
idx: 0,
|
||||||
};
|
};
|
||||||
|
@ -82,7 +90,7 @@ fn setup(
|
||||||
|
|
||||||
let transform = Transform::from_xyz(pos.0, pos.1, 0.0);
|
let transform = Transform::from_xyz(pos.0, pos.1, 0.0);
|
||||||
|
|
||||||
let e = commands
|
let entity = commands
|
||||||
.spawn()
|
.spawn()
|
||||||
.insert_bundle((
|
.insert_bundle((
|
||||||
Contributor { hue },
|
Contributor { hue },
|
||||||
|
@ -107,10 +115,17 @@ fn setup(
|
||||||
})
|
})
|
||||||
.id();
|
.id();
|
||||||
|
|
||||||
sel.order.push((name, e));
|
contributor_selection.order.push((name, entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
sel.order.shuffle(&mut rnd);
|
contributor_selection.order.shuffle(&mut rnd);
|
||||||
|
|
||||||
|
commands.insert_resource(contributor_selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
|
||||||
|
commands.spawn_bundle(UiCameraBundle::default());
|
||||||
|
|
||||||
commands.spawn_bundle((SelectTimer, Timer::from_seconds(SHOWCASE_TIMER_SECS, true)));
|
commands.spawn_bundle((SelectTimer, Timer::from_seconds(SHOWCASE_TIMER_SECS, true)));
|
||||||
|
|
||||||
|
@ -145,25 +160,23 @@ fn setup(
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
commands.insert_resource(sel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the next contributor to display and selects the entity
|
/// Finds the next contributor to display and selects the entity
|
||||||
fn select_system(
|
fn select_system(
|
||||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
mut sel: ResMut<ContributorSelection>,
|
mut contributor_selection: ResMut<ContributorSelection>,
|
||||||
mut dq: Query<&mut Text, With<ContributorDisplay>>,
|
mut text_query: Query<&mut Text, With<ContributorDisplay>>,
|
||||||
mut tq: Query<&mut Timer, With<SelectTimer>>,
|
mut timer_query: Query<&mut Timer, With<SelectTimer>>,
|
||||||
mut q: Query<(&Contributor, &Handle<ColorMaterial>, &mut Transform)>,
|
mut query: Query<(&Contributor, &Handle<ColorMaterial>, &mut Transform)>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
) {
|
) {
|
||||||
let mut timer_fired = false;
|
let mut timer_fired = false;
|
||||||
for mut t in tq.iter_mut() {
|
for mut timer in timer_query.iter_mut() {
|
||||||
if !t.tick(time.delta()).just_finished() {
|
if !timer.tick(time.delta()).just_finished() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
t.reset();
|
timer.reset();
|
||||||
timer_fired = true;
|
timer_fired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,26 +184,38 @@ fn select_system(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let prev = sel.idx;
|
let prev = contributor_selection.idx;
|
||||||
|
|
||||||
if (sel.idx + 1) < sel.order.len() {
|
if (contributor_selection.idx + 1) < contributor_selection.order.len() {
|
||||||
sel.idx += 1;
|
contributor_selection.idx += 1;
|
||||||
} else {
|
} else {
|
||||||
sel.idx = 0;
|
contributor_selection.idx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let (_, e) = &sel.order[prev];
|
let (_, entity) = &contributor_selection.order[prev];
|
||||||
if let Ok((c, handle, mut tr)) = q.get_mut(*e) {
|
if let Ok((contributor, handle, mut transform)) = query.get_mut(*entity) {
|
||||||
deselect(&mut *materials, handle.clone(), c, &mut *tr);
|
deselect(
|
||||||
|
&mut *materials,
|
||||||
|
handle.clone(),
|
||||||
|
contributor,
|
||||||
|
&mut *transform,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (name, e) = &sel.order[sel.idx];
|
let (name, entity) = &contributor_selection.order[contributor_selection.idx];
|
||||||
|
|
||||||
if let Ok((c, handle, mut tr)) = q.get_mut(*e) {
|
if let Ok((contributor, handle, mut transform)) = query.get_mut(*entity) {
|
||||||
if let Some(mut text) = dq.iter_mut().next() {
|
if let Some(mut text) = text_query.iter_mut().next() {
|
||||||
select(&mut *materials, handle, c, &mut *tr, &mut *text, name);
|
select(
|
||||||
|
&mut *materials,
|
||||||
|
handle,
|
||||||
|
contributor,
|
||||||
|
&mut *transform,
|
||||||
|
&mut *text,
|
||||||
|
name,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,20 +224,25 @@ fn select_system(
|
||||||
/// bring the object to the front and display the name.
|
/// bring the object to the front and display the name.
|
||||||
fn select(
|
fn select(
|
||||||
materials: &mut Assets<ColorMaterial>,
|
materials: &mut Assets<ColorMaterial>,
|
||||||
mat_handle: &Handle<ColorMaterial>,
|
material_handle: &Handle<ColorMaterial>,
|
||||||
cont: &Contributor,
|
contributor: &Contributor,
|
||||||
trans: &mut Transform,
|
transform: &mut Transform,
|
||||||
text: &mut Text,
|
text: &mut Text,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let mat = materials.get_mut(mat_handle)?;
|
let material = materials.get_mut(material_handle)?;
|
||||||
mat.color = Color::hsla(cont.hue, SATURATION_SELECTED, LIGHTNESS_SELECTED, ALPHA);
|
material.color = Color::hsla(
|
||||||
|
contributor.hue,
|
||||||
|
SATURATION_SELECTED,
|
||||||
|
LIGHTNESS_SELECTED,
|
||||||
|
ALPHA,
|
||||||
|
);
|
||||||
|
|
||||||
trans.translation.z = 100.0;
|
transform.translation.z = 100.0;
|
||||||
|
|
||||||
text.sections[0].value = "Contributor: ".to_string();
|
text.sections[0].value = "Contributor: ".to_string();
|
||||||
text.sections[1].value = name.to_string();
|
text.sections[1].value = name.to_string();
|
||||||
text.sections[1].style.color = mat.color;
|
text.sections[1].style.color = material.color;
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
@ -221,24 +251,29 @@ fn select(
|
||||||
/// the object to the back.
|
/// the object to the back.
|
||||||
fn deselect(
|
fn deselect(
|
||||||
materials: &mut Assets<ColorMaterial>,
|
materials: &mut Assets<ColorMaterial>,
|
||||||
mat_handle: Handle<ColorMaterial>,
|
material_handle: Handle<ColorMaterial>,
|
||||||
cont: &Contributor,
|
contributor: &Contributor,
|
||||||
trans: &mut Transform,
|
transform: &mut Transform,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let mat = materials.get_mut(mat_handle)?;
|
let material = materials.get_mut(material_handle)?;
|
||||||
mat.color = Color::hsla(cont.hue, SATURATION_DESELECTED, LIGHTNESS_DESELECTED, ALPHA);
|
material.color = Color::hsla(
|
||||||
|
contributor.hue,
|
||||||
|
SATURATION_DESELECTED,
|
||||||
|
LIGHTNESS_DESELECTED,
|
||||||
|
ALPHA,
|
||||||
|
);
|
||||||
|
|
||||||
trans.translation.z = 0.0;
|
transform.translation.z = 0.0;
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies gravity to all entities with velocity
|
/// Applies gravity to all entities with velocity
|
||||||
fn velocity_system(time: Res<Time>, mut q: Query<&mut Velocity>) {
|
fn velocity_system(time: Res<Time>, mut velocity_query: Query<&mut Velocity>) {
|
||||||
let delta = time.delta_seconds();
|
let delta = time.delta_seconds();
|
||||||
|
|
||||||
for mut v in q.iter_mut() {
|
for mut velocity in velocity_query.iter_mut() {
|
||||||
v.translation += Vec3::new(0.0, GRAVITY * delta, 0.0);
|
velocity.translation += Vec3::new(0.0, GRAVITY * delta, 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,78 +283,85 @@ fn velocity_system(time: Res<Time>, mut q: Query<&mut Velocity>) {
|
||||||
/// velocity. On collision with the ground it applies an upwards
|
/// velocity. On collision with the ground it applies an upwards
|
||||||
/// force.
|
/// force.
|
||||||
fn collision_system(
|
fn collision_system(
|
||||||
wins: Res<Windows>,
|
windows: Res<Windows>,
|
||||||
mut q: Query<(&mut Velocity, &mut Transform), With<Contributor>>,
|
mut query: Query<(&mut Velocity, &mut Transform), With<Contributor>>,
|
||||||
) {
|
) {
|
||||||
let mut rnd = rand::thread_rng();
|
let mut rnd = rand::thread_rng();
|
||||||
|
|
||||||
let win = wins.get_primary().unwrap();
|
let window = windows.get_primary().unwrap();
|
||||||
|
|
||||||
let ceiling = win.height() / 2.;
|
let ceiling = window.height() / 2.;
|
||||||
let ground = -(win.height() / 2.);
|
let ground = -(window.height() / 2.);
|
||||||
|
|
||||||
let wall_left = -(win.width() / 2.);
|
let wall_left = -(window.width() / 2.);
|
||||||
let wall_right = win.width() / 2.;
|
let wall_right = window.width() / 2.;
|
||||||
|
|
||||||
for (mut v, mut t) in q.iter_mut() {
|
for (mut velocity, mut transform) in query.iter_mut() {
|
||||||
let left = t.translation.x - SPRITE_SIZE / 2.0;
|
let left = transform.translation.x - SPRITE_SIZE / 2.0;
|
||||||
let right = t.translation.x + SPRITE_SIZE / 2.0;
|
let right = transform.translation.x + SPRITE_SIZE / 2.0;
|
||||||
let top = t.translation.y + SPRITE_SIZE / 2.0;
|
let top = transform.translation.y + SPRITE_SIZE / 2.0;
|
||||||
let bottom = t.translation.y - SPRITE_SIZE / 2.0;
|
let bottom = transform.translation.y - SPRITE_SIZE / 2.0;
|
||||||
|
|
||||||
// clamp the translation to not go out of the bounds
|
// clamp the translation to not go out of the bounds
|
||||||
if bottom < ground {
|
if bottom < ground {
|
||||||
t.translation.y = ground + SPRITE_SIZE / 2.0;
|
transform.translation.y = ground + SPRITE_SIZE / 2.0;
|
||||||
// apply an impulse upwards
|
// apply an impulse upwards
|
||||||
v.translation.y = rnd.gen_range(700.0..1000.0);
|
velocity.translation.y = rnd.gen_range(700.0..1000.0);
|
||||||
}
|
}
|
||||||
if top > ceiling {
|
if top > ceiling {
|
||||||
t.translation.y = ceiling - SPRITE_SIZE / 2.0;
|
transform.translation.y = ceiling - SPRITE_SIZE / 2.0;
|
||||||
}
|
}
|
||||||
// on side walls flip the horizontal velocity
|
// on side walls flip the horizontal velocity
|
||||||
if left < wall_left {
|
if left < wall_left {
|
||||||
t.translation.x = wall_left + SPRITE_SIZE / 2.0;
|
transform.translation.x = wall_left + SPRITE_SIZE / 2.0;
|
||||||
v.translation.x *= -1.0;
|
velocity.translation.x *= -1.0;
|
||||||
v.rotation *= -1.0;
|
velocity.rotation *= -1.0;
|
||||||
}
|
}
|
||||||
if right > wall_right {
|
if right > wall_right {
|
||||||
t.translation.x = wall_right - SPRITE_SIZE / 2.0;
|
transform.translation.x = wall_right - SPRITE_SIZE / 2.0;
|
||||||
v.translation.x *= -1.0;
|
velocity.translation.x *= -1.0;
|
||||||
v.rotation *= -1.0;
|
velocity.rotation *= -1.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply velocity to positions and rotations.
|
/// Apply velocity to positions and rotations.
|
||||||
fn move_system(time: Res<Time>, mut q: Query<(&Velocity, &mut Transform)>) {
|
fn move_system(time: Res<Time>, mut query: Query<(&Velocity, &mut Transform)>) {
|
||||||
let delta = time.delta_seconds();
|
let delta = time.delta_seconds();
|
||||||
|
|
||||||
for (v, mut t) in q.iter_mut() {
|
for (velocity, mut transform) in query.iter_mut() {
|
||||||
t.translation += delta * v.translation;
|
transform.translation += delta * velocity.translation;
|
||||||
t.rotate(Quat::from_rotation_z(v.rotation * delta));
|
transform.rotate(Quat::from_rotation_z(velocity.rotation * delta));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum LoadContributorsError {
|
||||||
|
IO(io::Error),
|
||||||
|
Var(VarError),
|
||||||
|
Stdout,
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the names of all contributors from the git log.
|
/// Get the names of all contributors from the git log.
|
||||||
///
|
///
|
||||||
/// The names are deduplicated.
|
/// The names are deduplicated.
|
||||||
/// This function only works if `git` is installed and
|
/// This function only works if `git` is installed and
|
||||||
/// the program is run through `cargo`.
|
/// the program is run through `cargo`.
|
||||||
fn contributors() -> Contributors {
|
fn contributors() -> Result<Contributors, LoadContributorsError> {
|
||||||
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
|
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").map_err(LoadContributorsError::Var)?;
|
||||||
.expect("This example needs to run through `cargo run --example`.");
|
|
||||||
|
|
||||||
let mut cmd = std::process::Command::new("git")
|
let mut cmd = std::process::Command::new("git")
|
||||||
.args(&["--no-pager", "log", "--pretty=format:%an"])
|
.args(&["--no-pager", "log", "--pretty=format:%an"])
|
||||||
.current_dir(manifest_dir)
|
.current_dir(manifest_dir)
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("`git` needs to be installed.");
|
.map_err(LoadContributorsError::IO)?;
|
||||||
|
|
||||||
let stdout = cmd.stdout.take().expect("`Child` should have a stdout.");
|
let stdout = cmd.stdout.take().ok_or(LoadContributorsError::Stdout)?;
|
||||||
|
|
||||||
BufReader::new(stdout)
|
let contributors = BufReader::new(stdout)
|
||||||
.lines()
|
.lines()
|
||||||
.filter_map(|x| x.ok())
|
.filter_map(|x| x.ok())
|
||||||
.collect()
|
.collect();
|
||||||
|
|
||||||
|
Ok(contributors)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue