Compare commits
No commits in common. "245dfde91e861f24ee7a3dfb9d9159ce2e185d3e" and "763acd0ad8aa07214d18b540f729d0c9df36b678" have entirely different histories.
245dfde91e
...
763acd0ad8
8 changed files with 220 additions and 272 deletions
1
.vscode/tasks.json
vendored
1
.vscode/tasks.json
vendored
|
@ -5,7 +5,6 @@
|
|||
"label": "Launch",
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
"args": ["--", "--seed", "gargamel"],
|
||||
"options": {
|
||||
"env": {
|
||||
"RUST_BACKTRACE": "full"
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
pub mod objects;
|
||||
pub mod runtime;
|
||||
pub mod seed;
|
||||
pub mod setup;
|
|
@ -1,18 +0,0 @@
|
|||
use avian2d::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Component, Default)]
|
||||
#[require(Collider, Mesh2d, MeshMaterial2d<ColorMaterial>, Restitution = Restitution::new(1.0), RigidBody, TransformInterpolation, Transform)]
|
||||
struct GameObject;
|
||||
|
||||
#[derive(Component, Default)]
|
||||
#[require(GameObject, RigidBody = RigidBody::Dynamic)]
|
||||
pub struct Ball;
|
||||
|
||||
#[derive(Component, Default)]
|
||||
#[require(Ball)]
|
||||
pub struct Player;
|
||||
|
||||
#[derive(Component, Default)]
|
||||
#[require(GameObject, RigidBody = RigidBody::Static)]
|
||||
pub struct Wall;
|
|
@ -1,58 +0,0 @@
|
|||
use avian2d::prelude::*;
|
||||
use bevy::{
|
||||
input::mouse::{AccumulatedMouseScroll, MouseScrollUnit},
|
||||
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);
|
||||
}
|
||||
|
||||
pub fn zoom_camera(
|
||||
mut camera: Single<&mut Projection, With<IsDefaultUiCamera>>,
|
||||
scroll: Res<AccumulatedMouseScroll>,
|
||||
) -> Result {
|
||||
let Projection::Orthographic(ref mut projection) = **camera else {
|
||||
return Err("Default camera was not orthographic".into());
|
||||
};
|
||||
let scroll_type_multiplier = match scroll.unit {
|
||||
MouseScrollUnit::Line => 0.01,
|
||||
MouseScrollUnit::Pixel => 0.001,
|
||||
};
|
||||
projection.scale = (projection.scale - scroll.delta.y * scroll_type_multiplier).clamp(0.1, 2.5);
|
||||
Ok(())
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Clone, Resource)]
|
||||
pub struct Seed(u64);
|
||||
|
||||
impl From<String> for Seed {
|
||||
fn from(value: String) -> Self {
|
||||
Self(value.parse::<u64>().unwrap_or_else(|_| {
|
||||
let mut state = DefaultHasher::new();
|
||||
value.hash(&mut state);
|
||||
state.finish()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Seed> for [u8; 8] {
|
||||
fn from(value: Seed) -> Self {
|
||||
value.0.to_le_bytes()
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
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,
|
||||
));
|
||||
}
|
||||
}
|
55
src/lib.rs
55
src/lib.rs
|
@ -1,55 +0,0 @@
|
|||
use avian2d::{math::Vector, prelude::*};
|
||||
use bevy::{input::common_conditions::input_pressed, prelude::*};
|
||||
use bevy_rand::prelude::{EntropyPlugin, WyRand};
|
||||
use clap::Parser;
|
||||
|
||||
mod game;
|
||||
use game::{
|
||||
runtime::{move_camera, move_player, quit, zoom_camera},
|
||||
seed::Seed,
|
||||
setup::{setup_balls, setup_player, setup_pseudo_random, setup_ui, setup_walls},
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version, about)]
|
||||
pub struct AppSettings {
|
||||
#[arg(short, long, default_value = ":)")]
|
||||
pub seed: Seed,
|
||||
}
|
||||
|
||||
impl Plugin for AppSettings {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_resource(Gravity(Vector::ZERO))
|
||||
.insert_resource(self.seed.clone())
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Window {
|
||||
title: "Distributed physics test".into(),
|
||||
fit_canvas_to_parent: true,
|
||||
..default()
|
||||
}
|
||||
.into(),
|
||||
..default()
|
||||
}),
|
||||
PhysicsPlugins::default().with_length_unit(50.0),
|
||||
EntropyPlugin::<WyRand>::with_seed(self.seed.clone().into()),
|
||||
))
|
||||
.add_systems(
|
||||
Startup,
|
||||
(
|
||||
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)),
|
||||
zoom_camera,
|
||||
),
|
||||
)
|
||||
.add_systems(PostUpdate, move_camera);
|
||||
}
|
||||
}
|
224
src/main.rs
224
src/main.rs
|
@ -1,8 +1,224 @@
|
|||
use bevy::prelude::{App, AppExit};
|
||||
use clap::Parser;
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
hash::{DefaultHasher, Hash, Hasher},
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
use distributed_physics_test::AppSettings;
|
||||
use avian2d::{math::Vector, prelude::*};
|
||||
use bevy::{
|
||||
color::palettes::{
|
||||
css::WHITE,
|
||||
tailwind::{LIME_400, RED_400},
|
||||
},
|
||||
input::common_conditions::input_pressed,
|
||||
prelude::*,
|
||||
};
|
||||
use bevy_rand::prelude::{EntropyPlugin, GlobalEntropy, WyRand};
|
||||
use clap::Parser;
|
||||
use rand::Rng;
|
||||
use wyrand::WyRand as LocalRng;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version, about)]
|
||||
struct AppSettings {
|
||||
#[arg(short, long, default_value = "")]
|
||||
seed: String,
|
||||
}
|
||||
|
||||
fn main() -> AppExit {
|
||||
App::new().add_plugins(AppSettings::parse()).run()
|
||||
run_app(AppSettings::parse())
|
||||
}
|
||||
|
||||
const BALL_COUNT: u8 = 32;
|
||||
const BALL_SIZES: Range<f32> = 10.0..25.0;
|
||||
const DIMENSION_SIZES: Range<f32> = 500.0..2000.0;
|
||||
|
||||
#[derive(Clone, Resource)]
|
||||
struct Seed(u64);
|
||||
|
||||
impl From<String> for Seed {
|
||||
fn from(value: String) -> Self {
|
||||
Self(value.parse::<u64>().unwrap_or_else(|_| {
|
||||
let mut state = DefaultHasher::new();
|
||||
value.hash(&mut state);
|
||||
state.finish()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct PlayableArea(f32, f32);
|
||||
|
||||
#[derive(Resource)]
|
||||
struct PlayerSize(f32);
|
||||
|
||||
fn run_app(settings: AppSettings) -> AppExit {
|
||||
let seed = Seed::from(settings.seed);
|
||||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Window {
|
||||
title: "Distributed physics test".into(),
|
||||
fit_canvas_to_parent: true,
|
||||
..default()
|
||||
}
|
||||
.into(),
|
||||
..default()
|
||||
}),
|
||||
PhysicsPlugins::default().with_length_unit(50.0),
|
||||
EntropyPlugin::<WyRand>::with_seed(seed.0.to_le_bytes()), // TODO: Tried to make an Into<[u8; 8]> for this but it wants to consume the value
|
||||
))
|
||||
.add_systems(
|
||||
Startup,
|
||||
(
|
||||
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)
|
||||
.insert_resource(Gravity(Vector::ZERO))
|
||||
.insert_resource(seed)
|
||||
.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));
|
||||
}
|
||||
|
||||
#[derive(Component, Default)]
|
||||
#[require(Collider, Mesh2d, MeshMaterial2d<ColorMaterial>, Restitution = Restitution::new(1.0), RigidBody, TransformInterpolation, Transform)]
|
||||
struct GameObject;
|
||||
|
||||
#[derive(Component, Default)]
|
||||
#[require(GameObject, RigidBody = RigidBody::Dynamic)]
|
||||
struct Ball;
|
||||
|
||||
#[derive(Component, Default)]
|
||||
#[require(Ball)]
|
||||
struct Player;
|
||||
|
||||
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),
|
||||
),
|
||||
0.0,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Default)]
|
||||
#[require(GameObject, RigidBody = RigidBody::Static)]
|
||||
struct Wall;
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue