Extract systems

This commit is contained in:
Michael Bradley 2025-05-19 00:27:12 -04:00
parent cef21f4a00
commit 214e0ceedc
Signed by: MichaelBradley
SSH key fingerprint: SHA256:BKO2eI2LPsCbQS3n3i5SdwZTAIV3F1lHezR07qP+Ob0
9 changed files with 199 additions and 191 deletions

1
.vscode/tasks.json vendored
View file

@ -5,6 +5,7 @@
"label": "Launch", "label": "Launch",
"type": "cargo", "type": "cargo",
"command": "run", "command": "run",
"args": ["--", "--seed", "gargamel"],
"options": { "options": {
"env": { "env": {
"RUST_BACKTRACE": "full" "RUST_BACKTRACE": "full"

View file

@ -1 +1,4 @@
pub mod objects; pub mod objects;
pub mod runtime;
pub mod seed;
pub mod setup;

40
src/game/runtime.rs Normal file
View file

@ -0,0 +1,40 @@
use avian2d::prelude::*;
use bevy::prelude::*;
use super::objects::Player;
pub fn move_player(
time: Res<Time>,
keyboard_input: Res<ButtonInput<KeyCode>>,
mut velocity: Single<&mut LinearVelocity, With<Player>>,
) -> Result {
let acceleration = 500.0;
let delta_time = time.delta_secs();
if keyboard_input.any_pressed([KeyCode::KeyW, KeyCode::ArrowUp]) {
velocity.y += acceleration * delta_time;
}
if keyboard_input.any_pressed([KeyCode::KeyS, KeyCode::ArrowDown]) {
velocity.y -= acceleration * delta_time;
}
if keyboard_input.any_pressed([KeyCode::KeyA, KeyCode::ArrowLeft]) {
velocity.x -= acceleration * delta_time;
}
if keyboard_input.any_pressed([KeyCode::KeyD, KeyCode::ArrowRight]) {
velocity.x += acceleration * delta_time;
}
Ok(())
}
pub fn quit(mut exit: EventWriter<AppExit>) {
exit.write(AppExit::Success);
}
pub fn move_camera(
mut camera: Single<&mut Transform, (Without<Player>, With<IsDefaultUiCamera>)>,
player: Single<&Transform, (With<Player>, Without<IsDefaultUiCamera>)>,
) {
camera.translation = camera.translation.lerp(player.translation, 0.05);
}

110
src/game/setup.rs Normal file
View file

@ -0,0 +1,110 @@
use std::{f32::consts::PI, ops::Range};
use avian2d::prelude::*;
use bevy::{
color::palettes::{
css::WHITE,
tailwind::{LIME_400, RED_400},
},
prelude::*,
};
use bevy_rand::prelude::{GlobalEntropy, WyRand};
use rand::Rng;
use wyrand::WyRand as LocalRng;
use super::objects::{Ball, Player, Wall};
const BALL_COUNT: u8 = 32;
const BALL_SIZES: Range<f32> = 10.0..25.0;
const DIMENSION_SIZES: Range<f32> = 500.0..2000.0;
#[derive(Resource)]
pub struct PlayableArea(f32, f32);
#[derive(Resource)]
pub struct PlayerSize(f32);
pub fn setup_pseudo_random(mut commands: Commands, mut rng: GlobalEntropy<WyRand>) {
commands.insert_resource(PlayerSize(rng.random_range(BALL_SIZES)));
commands.insert_resource(PlayableArea(
rng.random_range(DIMENSION_SIZES),
rng.random_range(DIMENSION_SIZES),
));
}
pub fn setup_ui(mut commands: Commands) {
commands.spawn((Name::new("Camera"), Camera2d, IsDefaultUiCamera));
}
pub fn setup_player(
mut commands: Commands,
mut materials: ResMut<Assets<ColorMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
radius: Res<PlayerSize>,
) {
let circle = Circle::new(radius.0);
commands.spawn((
Player,
Collider::from(circle),
Mesh2d(meshes.add(circle)),
MeshMaterial2d(materials.add(Color::from(LIME_400))),
));
}
pub fn setup_balls(
mut commands: Commands,
mut materials: ResMut<Assets<ColorMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
region: Res<PlayableArea>,
) {
let mut random = LocalRng::new(Default::default());
for _ in 0..BALL_COUNT {
let circle = Circle::new(random.random_range(BALL_SIZES));
commands.spawn((
Ball,
Collider::from(circle),
Mesh2d(meshes.add(circle)),
MeshMaterial2d(materials.add(Color::from(RED_400))),
Transform::from_xyz(
random.random_range(
(-region.0 / 2.0 + circle.radius)..(region.0 / 2.0 - circle.radius),
),
random.random_range(
(-region.1 / 2.0 + circle.radius)..(region.1 / 2.0 - circle.radius),
),
0.0,
),
));
}
}
pub fn setup_walls(
mut commands: Commands,
mut materials: ResMut<Assets<ColorMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
region: Res<PlayableArea>,
) {
let thickness = 20.0;
for i in 0..4 {
let (offset, length) = if i % 2 == 0 {
(region.0, region.1 + thickness)
} else {
(region.1, region.0 + thickness)
};
let mut transform = Transform::from_xyz(0.0, offset / 2.0, 0.0);
transform.rotate_around(
Vec3::ZERO,
Quat::from_rotation_z(((i + 1) as f32) * PI / 2.0),
);
commands.spawn((
Wall,
Collider::rectangle(length, thickness),
Mesh2d(meshes.add(Rectangle::new(length, thickness))),
MeshMaterial2d(materials.add(Color::from(WHITE))),
transform,
));
}
}

View file

@ -1,183 +1,51 @@
use std::{f32::consts::PI, ops::Range};
use avian2d::{math::Vector, prelude::*}; use avian2d::{math::Vector, prelude::*};
use bevy::{ use bevy::{input::common_conditions::input_pressed, prelude::*};
color::palettes::{ use bevy_rand::prelude::{EntropyPlugin, WyRand};
css::WHITE, use clap::Parser;
tailwind::{LIME_400, RED_400},
},
input::common_conditions::input_pressed,
prelude::*,
};
use bevy_rand::prelude::{EntropyPlugin, GlobalEntropy, WyRand};
use rand::Rng;
use wyrand::WyRand as LocalRng;
mod game; mod game;
use game::objects::{Ball, Player, Wall}; use game::{
mod utils; runtime::{move_camera, move_player, quit},
pub use utils::AppSettings; seed::Seed,
setup::{setup_balls, setup_player, setup_pseudo_random, setup_ui, setup_walls},
};
const BALL_COUNT: u8 = 32; #[derive(Parser)]
const BALL_SIZES: Range<f32> = 10.0..25.0; #[command(version, about)]
const DIMENSION_SIZES: Range<f32> = 500.0..2000.0; pub struct AppSettings {
#[arg(short, long, default_value = ":)")]
pub seed: Seed,
}
#[derive(Resource)] impl Plugin for AppSettings {
struct PlayableArea(f32, f32); fn build(&self, app: &mut App) {
app.insert_resource(Gravity(Vector::ZERO))
#[derive(Resource)] .insert_resource(self.seed.clone())
struct PlayerSize(f32); .add_plugins((
DefaultPlugins.set(WindowPlugin {
pub fn run_app(settings: AppSettings) -> AppExit { primary_window: Window {
App::new() title: "Distributed physics test".into(),
.insert_resource(Gravity(Vector::ZERO)) fit_canvas_to_parent: true,
.insert_resource(settings.seed.clone()) ..default()
.add_plugins(( }
DefaultPlugins.set(WindowPlugin { .into(),
primary_window: Window {
title: "Distributed physics test".into(),
fit_canvas_to_parent: true,
..default() ..default()
} }),
.into(), PhysicsPlugins::default().with_length_unit(50.0),
..default() EntropyPlugin::<WyRand>::with_seed(self.seed.clone().into()),
}), ))
PhysicsPlugins::default().with_length_unit(50.0), .add_systems(
EntropyPlugin::<WyRand>::with_seed(settings.seed.into()), Startup,
)) (
.add_systems( setup_pseudo_random,
Startup, setup_ui,
( (setup_player, setup_balls, setup_walls).after(setup_pseudo_random),
setup_pseudo_random,
setup_ui,
(setup_player, setup_balls, setup_walls).after(setup_pseudo_random),
),
)
.add_systems(
Update,
(move_player, quit.run_if(input_pressed(KeyCode::KeyQ))),
)
.add_systems(PostUpdate, move_camera)
.run()
}
fn setup_pseudo_random(mut commands: Commands, mut rng: GlobalEntropy<WyRand>) {
commands.insert_resource(PlayerSize(rng.random_range(BALL_SIZES)));
commands.insert_resource(PlayableArea(
rng.random_range(DIMENSION_SIZES),
rng.random_range(DIMENSION_SIZES),
));
}
fn setup_ui(mut commands: Commands) {
commands.spawn((Name::new("Camera"), Camera2d, IsDefaultUiCamera));
}
fn setup_player(
mut commands: Commands,
mut materials: ResMut<Assets<ColorMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
radius: Res<PlayerSize>,
) {
let circle = Circle::new(radius.0);
commands.spawn((
Player,
Collider::from(circle),
Mesh2d(meshes.add(circle)),
MeshMaterial2d(materials.add(Color::from(LIME_400))),
));
}
fn setup_balls(
mut commands: Commands,
mut materials: ResMut<Assets<ColorMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
region: Res<PlayableArea>,
) {
let mut random = LocalRng::new(Default::default());
for _ in 0..BALL_COUNT {
let circle = Circle::new(random.random_range(BALL_SIZES));
commands.spawn((
Ball,
Collider::from(circle),
Mesh2d(meshes.add(circle)),
MeshMaterial2d(materials.add(Color::from(RED_400))),
Transform::from_xyz(
random.random_range(
(-region.0 / 2.0 + circle.radius)..(region.0 / 2.0 - circle.radius),
), ),
random.random_range( )
(-region.1 / 2.0 + circle.radius)..(region.1 / 2.0 - circle.radius), .add_systems(
), Update,
0.0, (move_player, quit.run_if(input_pressed(KeyCode::KeyQ))),
), )
)); .add_systems(PostUpdate, move_camera);
} }
} }
fn setup_walls(
mut commands: Commands,
mut materials: ResMut<Assets<ColorMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
region: Res<PlayableArea>,
) {
let thickness = 20.0;
for i in 0..4 {
let (offset, length) = if i % 2 == 0 {
(region.0, region.1 + thickness)
} else {
(region.1, region.0 + thickness)
};
let mut transform = Transform::from_xyz(0.0, offset / 2.0, 0.0);
transform.rotate_around(
Vec3::ZERO,
Quat::from_rotation_z(((i + 1) as f32) * PI / 2.0),
);
commands.spawn((
Wall,
Collider::rectangle(length, thickness),
Mesh2d(meshes.add(Rectangle::new(length, thickness))),
MeshMaterial2d(materials.add(Color::from(WHITE))),
transform,
));
}
}
fn move_player(
time: Res<Time>,
keyboard_input: Res<ButtonInput<KeyCode>>,
mut velocity: Single<&mut LinearVelocity, With<Player>>,
) -> Result {
let acceleration = 500.0;
let delta_time = time.delta_secs();
if keyboard_input.any_pressed([KeyCode::KeyW, KeyCode::ArrowUp]) {
velocity.y += acceleration * delta_time;
}
if keyboard_input.any_pressed([KeyCode::KeyS, KeyCode::ArrowDown]) {
velocity.y -= acceleration * delta_time;
}
if keyboard_input.any_pressed([KeyCode::KeyA, KeyCode::ArrowLeft]) {
velocity.x -= acceleration * delta_time;
}
if keyboard_input.any_pressed([KeyCode::KeyD, KeyCode::ArrowRight]) {
velocity.x += acceleration * delta_time;
}
Ok(())
}
fn quit(mut exit: EventWriter<AppExit>) {
exit.write(AppExit::Success);
}
fn move_camera(
mut camera: Single<&mut Transform, (Without<Player>, With<IsDefaultUiCamera>)>,
player: Single<&Transform, (With<Player>, Without<IsDefaultUiCamera>)>,
) {
camera.translation = camera.translation.lerp(player.translation, 0.05);
}

View file

@ -1,8 +1,8 @@
use bevy::prelude::AppExit; use bevy::prelude::{App, AppExit};
use clap::Parser; use clap::Parser;
use distributed_physics_test::{AppSettings, run_app}; use distributed_physics_test::AppSettings;
fn main() -> AppExit { fn main() -> AppExit {
run_app(AppSettings::parse()) App::new().add_plugins(AppSettings::parse()).run()
} }

View file

@ -1,10 +0,0 @@
use clap::Parser;
use super::Seed;
#[derive(Parser)]
#[command(version, about)]
pub struct AppSettings {
#[arg(short, long, default_value = "")]
pub seed: Seed,
}

View file

@ -1,4 +0,0 @@
mod seed;
pub use seed::Seed;
mod app_settings;
pub use app_settings::AppSettings;