add generation
This commit is contained in:
parent
b204118b82
commit
b5c6067979
10 changed files with 250 additions and 130 deletions
23
Cargo.lock
generated
23
Cargo.lock
generated
|
@ -1127,6 +1127,16 @@ version = "0.8.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cursor-icon"
|
||||
version = "1.1.0"
|
||||
|
@ -1150,6 +1160,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"bevy",
|
||||
"bevy_eventwork",
|
||||
"ctrlc",
|
||||
"itermore",
|
||||
"rand",
|
||||
"serde",
|
||||
|
@ -1817,6 +1828,18 @@ dependencies = [
|
|||
"jni-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases 0.2.1",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nonmax"
|
||||
version = "0.5.5"
|
||||
|
|
|
@ -8,6 +8,7 @@ bevy = { version = "0.14.2", default-features = false, features = [
|
|||
"multi_threaded",
|
||||
] }
|
||||
bevy_eventwork = "0.9.0"
|
||||
ctrlc = "3.4.5"
|
||||
itermore = { version = "0.7.1", features = ["array_chunks"] }
|
||||
rand = "0.8.5"
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
use bevy_eventwork::{tcp::TcpProvider, ConnectionId, Network};
|
||||
use bevy_eventwork::{tcp::TcpProvider, Network};
|
||||
use std::{
|
||||
io::{stdin, stdout, Stdout},
|
||||
io::{stdin, stdout},
|
||||
process,
|
||||
sync::{mpsc, Arc, Mutex},
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
use termion::{
|
||||
cursor::{self, HideCursor},
|
||||
event::Key,
|
||||
input::TermRead,
|
||||
raw::{IntoRawMode, RawTerminal},
|
||||
thread,
|
||||
};
|
||||
use termion::{clear, cursor, event::Key, input::TermRead, raw::IntoRawMode};
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
|
@ -22,7 +17,7 @@ pub struct InputPlugin;
|
|||
|
||||
impl Plugin for InputPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
let (rec, _thread) = spawn_reader();
|
||||
let rec = spawn_reader();
|
||||
let rec = Arc::new(Mutex::new(rec));
|
||||
app.insert_resource(InputReceiver(rec))
|
||||
.add_event::<KeyEvent>()
|
||||
|
@ -37,23 +32,36 @@ pub struct InputReceiver(Arc<Mutex<mpsc::Receiver<Key>>>);
|
|||
#[derive(Debug, Event)]
|
||||
pub struct KeyEvent(Key);
|
||||
|
||||
fn spawn_reader() -> (mpsc::Receiver<Key>, JoinHandle<()>) {
|
||||
fn spawn_reader() -> mpsc::Receiver<Key> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let thread = thread::spawn(move || {
|
||||
let stdout = stdout().into_raw_mode().unwrap();
|
||||
|
||||
let (exit_tx, exit_rx) = mpsc::channel();
|
||||
thread::spawn(move || {
|
||||
let raw_mode = stdout().into_raw_mode().unwrap();
|
||||
println!("{}", cursor::Hide);
|
||||
exit_rx.recv().ok();
|
||||
drop(raw_mode);
|
||||
println!("{}{}", cursor::Restore, clear::All);
|
||||
process::exit(0);
|
||||
});
|
||||
|
||||
let exit_tx_ = exit_tx.clone();
|
||||
ctrlc::set_handler(move || {
|
||||
exit_tx_.send(()).ok();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
thread::spawn(move || {
|
||||
let mut keys = stdin().keys();
|
||||
while let Some(Ok(key)) = keys.next() {
|
||||
if key == Key::Esc {
|
||||
println!("exitting.");
|
||||
println!("{}", cursor::Restore);
|
||||
drop(stdout);
|
||||
process::exit(0);
|
||||
exit_tx.send(()).ok();
|
||||
}
|
||||
tx.send(key).ok();
|
||||
}
|
||||
});
|
||||
(rx, thread)
|
||||
|
||||
rx
|
||||
}
|
||||
|
||||
fn try_read_keys(receiver: Res<InputReceiver>, mut writer: EventWriter<KeyEvent>) {
|
||||
|
|
|
@ -3,7 +3,7 @@ use bevy::{prelude::*, utils::hashbrown::HashSet};
|
|||
use bevy_eventwork::{tcp::TcpProvider, ConnectionId, Network};
|
||||
|
||||
use super::{
|
||||
physics::{update_physics_collisions, Pos},
|
||||
physics::{self, Pos},
|
||||
player::Player,
|
||||
};
|
||||
|
||||
|
@ -13,7 +13,7 @@ impl Plugin for DisplayPlugin {
|
|||
fn build(&self, app: &mut App) {
|
||||
app.add_event::<RenderUpdate>()
|
||||
// .add_systems(Update, rerender_players)
|
||||
.add_systems(Update, on_updates.after(update_physics_collisions));
|
||||
.add_systems(Update, on_updates.after(physics::update_physics_pos));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,8 +35,8 @@ fn on_updates(
|
|||
let mut to_render = HashSet::new();
|
||||
for RenderUpdate(event_pos) in updates.read() {
|
||||
for (Player(connection_id), player_pos) in &players {
|
||||
if player_pos.pos().distance_squared(*event_pos) <= RENDER_DISTANCE_SQ {
|
||||
to_render.insert((*connection_id, player_pos.pos()));
|
||||
if player_pos.0.distance_squared(*event_pos) <= RENDER_DISTANCE_SQ {
|
||||
to_render.insert((*connection_id, player_pos.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,14 +54,14 @@ fn render(
|
|||
) {
|
||||
let updates = displayables
|
||||
.iter()
|
||||
.filter(|(pos, _)| pos.pos().distance_squared(player_pos) <= RENDER_DISTANCE_SQ)
|
||||
.map(|(pos, Display(display))| (pos.pos() - player_pos, *display))
|
||||
.filter(|(pos, _)| pos.0.distance_squared(player_pos) <= RENDER_DISTANCE_SQ)
|
||||
.map(|(pos, Display(display))| (pos.0 - player_pos, *display))
|
||||
.collect();
|
||||
net.send_message(id, MapUpdates(updates)).ok();
|
||||
}
|
||||
|
||||
fn rerender_players(players: Query<&Pos, With<Player>>, mut writer: EventWriter<RenderUpdate>) {
|
||||
for Pos(pos) in &players {
|
||||
writer.send(RenderUpdate(pos.as_ivec2()));
|
||||
writer.send(RenderUpdate(*pos));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,94 @@
|
|||
use bevy::{prelude::*, utils::HashSet};
|
||||
|
||||
use super::physics::Pos;
|
||||
use super::{
|
||||
display::RenderUpdate,
|
||||
map::{self, spawn_block},
|
||||
physics::Pos,
|
||||
};
|
||||
|
||||
pub struct GenerationPlugin;
|
||||
|
||||
impl Plugin for GenerationPlugin {
|
||||
fn build(&self, app: &mut bevy::prelude::App) {
|
||||
app.insert_resource(GeneratedRegistry::default())
|
||||
// .add_systems(schedule, systems)
|
||||
;
|
||||
.add_systems(Update, chunk_loading);
|
||||
}
|
||||
}
|
||||
|
||||
fn chunk_loading(cmd: &mut Commands, loaders: Query<&Pos, With<Loader>>, reg: ResMut<GeneratedRegistry>) {
|
||||
for pos in &loaders {
|
||||
chunks_surrounding(&pos.pos());
|
||||
fn chunk_loading(
|
||||
mut cmd: Commands,
|
||||
loaders: Query<&Pos, With<Loader>>,
|
||||
mut reg: ResMut<GeneratedRegistry>,
|
||||
mut updater: EventWriter<RenderUpdate>,
|
||||
) {
|
||||
for loader in &loaders {
|
||||
for pos in chunks_surrounding(&loader.0, GEN_DISTANCE) {
|
||||
reg.load_chunk(&mut cmd, &mut updater, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const GEN_DISTANCE: i32 = 1;
|
||||
const CHUNK_WIDTH: i32 = 8;
|
||||
|
||||
fn chunks_surrounding(pos: &IVec2) -> Vec<IVec2> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn snap(value: i32, increment: i32) -> i32 {
|
||||
todo!()
|
||||
fn chunks_surrounding(pos: &IVec2, radius: i32) -> Vec<IVec2> {
|
||||
let center_chunk = pos.div_euclid(IVec2::splat(CHUNK_WIDTH));
|
||||
(-radius..=radius)
|
||||
.flat_map(move |x| (-radius..=radius).map(move |y| center_chunk + IVec2::new(x, y)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_snap() {
|
||||
// assert_eq!();
|
||||
fn test_euclid() {
|
||||
assert_eq!((5_i32).div_euclid(2), 2);
|
||||
assert_eq!((4_i32).div_euclid(2), 2);
|
||||
assert_eq!((3_i32).div_euclid(2), 1);
|
||||
assert_eq!((2_i32).div_euclid(2), 1);
|
||||
assert_eq!((1_i32).div_euclid(2), 0);
|
||||
assert_eq!((0_i32).div_euclid(2), 0);
|
||||
assert_eq!((-1_i32).div_euclid(2), -1);
|
||||
assert_eq!((-2_i32).div_euclid(2), -1);
|
||||
assert_eq!((-3_i32).div_euclid(2), -2);
|
||||
assert_eq!((-4_i32).div_euclid(2), -2);
|
||||
assert_eq!((-5_i32).div_euclid(2), -3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_snap_mod() {
|
||||
assert_eq!((5_i32).rem_euclid(2), 1);
|
||||
assert_eq!((4_i32).rem_euclid(2), 0);
|
||||
assert_eq!((3_i32).rem_euclid(2), 1);
|
||||
assert_eq!((2_i32).rem_euclid(2), 0);
|
||||
assert_eq!((1_i32).rem_euclid(2), 1);
|
||||
assert_eq!((0_i32).rem_euclid(2), 0);
|
||||
assert_eq!((-1_i32).rem_euclid(2), 1);
|
||||
assert_eq!((-2_i32).rem_euclid(2), 0);
|
||||
assert_eq!((-3_i32).rem_euclid(2), 1);
|
||||
assert_eq!((-4_i32).rem_euclid(2), 0);
|
||||
assert_eq!((-5_i32).rem_euclid(2), 1);
|
||||
}
|
||||
|
||||
#[derive(Debug, Resource, Default)]
|
||||
pub struct GeneratedRegistry(HashSet<IVec2>);
|
||||
|
||||
impl GeneratedRegistry {
|
||||
pub fn load_chunk(&mut self, cmd: &mut Commands, rerender: &mut EventWriter<RenderUpdate>, chunk: IVec2) {
|
||||
if self.0.contains(&chunk) {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("Loading chunk indexed at position {chunk:?}.");
|
||||
let from = chunk * CHUNK_WIDTH;
|
||||
let to = from + IVec2::splat(CHUNK_WIDTH);
|
||||
for (pos, spawnable) in generate(from, to) {
|
||||
match spawnable {
|
||||
Spawnable::Block(display, mass) => spawn_block(cmd, rerender, pos, display, mass),
|
||||
}
|
||||
}
|
||||
self.0.insert(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Loader;
|
||||
|
||||
|
@ -44,3 +97,19 @@ pub struct Generated;
|
|||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Chunk(IVec2);
|
||||
|
||||
enum Spawnable {
|
||||
Block((char, char), usize),
|
||||
}
|
||||
|
||||
fn generate(from: IVec2, _to: IVec2) -> Vec<(IVec2, Spawnable)> {
|
||||
let structure = "
|
||||
XX XX
|
||||
|
||||
XX XX
|
||||
";
|
||||
map::str_to_struct(structure)
|
||||
.into_iter()
|
||||
.map(|(pos, display, mass)| (pos + from, Spawnable::Block(display, mass)))
|
||||
.collect()
|
||||
}
|
||||
|
|
|
@ -4,15 +4,22 @@ use bevy::prelude::*;
|
|||
use itermore::IterArrayChunks;
|
||||
|
||||
use super::{
|
||||
display::Display,
|
||||
physics::{Mass, Pos},
|
||||
display::{Display, RenderUpdate},
|
||||
physics::{Forces, Mass, Pos},
|
||||
};
|
||||
|
||||
fn spawn_block(cmd: &mut Commands, pos: IVec2, display: (char, char), mass: usize) {
|
||||
cmd.spawn((Pos(pos.as_vec2()), Mass(mass), Display(display)));
|
||||
pub fn spawn_block(
|
||||
cmd: &mut Commands,
|
||||
rerender: &mut EventWriter<RenderUpdate>,
|
||||
pos: IVec2,
|
||||
display: (char, char),
|
||||
mass: usize,
|
||||
) {
|
||||
cmd.spawn((Forces::zero(), Pos(pos), Mass(mass), Display(display)));
|
||||
rerender.send(RenderUpdate(pos));
|
||||
}
|
||||
|
||||
fn str_to_struct(text: &str) -> Vec<(IVec2, (char, char), usize)> {
|
||||
pub fn str_to_struct(text: &str) -> Vec<(IVec2, (char, char), usize)> {
|
||||
text.lines()
|
||||
.enumerate()
|
||||
.flat_map(|(y, line)| {
|
||||
|
@ -25,17 +32,17 @@ fn str_to_struct(text: &str) -> Vec<(IVec2, (char, char), usize)> {
|
|||
.collect()
|
||||
}
|
||||
|
||||
fn mass_of(tiles: (char, char)) -> usize {
|
||||
pub fn mass_of(tiles: (char, char)) -> usize {
|
||||
match tiles {
|
||||
('#', '#') => 100,
|
||||
('X', 'X') => 10,
|
||||
('[', ']') => 5,
|
||||
('(', ')') => 1,
|
||||
('[', ']') => 8,
|
||||
('(', ')') => 5,
|
||||
_ => 20,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_debug_map(mut cmd: Commands) {
|
||||
pub fn spawn_debug_map(mut cmd: Commands, mut rerender: EventWriter<RenderUpdate>) {
|
||||
let raw = "
|
||||
()
|
||||
|
||||
|
@ -49,6 +56,6 @@ pub fn spawn_debug_map(mut cmd: Commands) {
|
|||
";
|
||||
|
||||
for (pos, display, mass) in str_to_struct(raw) {
|
||||
spawn_block(&mut cmd, pos, display, mass);
|
||||
spawn_block(&mut cmd, &mut rerender, pos, display, mass);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,12 @@ impl Plugin for MetricsPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
fn show_metrics(time: Res<Time>, mut sys: ResMut<SysRes>, players: Query<(), With<Player>>) {
|
||||
sys.0.refresh_all();
|
||||
let cpu = sys.0.global_cpu_usage();
|
||||
fn show_metrics(time: Res<Time>, mut sys: ResMut<SysRes>, players: Query<(), With<Player>>, entities: Query<Entity>) {
|
||||
let player_count = players.iter().count();
|
||||
let entity_count = entities.iter().count();
|
||||
let time_delta = time.delta_seconds();
|
||||
|
||||
let cpu = sys.0.global_cpu_usage();
|
||||
sys.0.refresh_all();
|
||||
let up = cursor::Up(1);
|
||||
println!(" | players {player_count:2} | tick {time_delta:2.3} | cpu {cpu:2.1}{up} ");
|
||||
println!(" | ent {entity_count:2} | ply {player_count:2} | tick {time_delta:2.3} | cpu {cpu:2.1}{up} ");
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ fn respond_greet(
|
|||
net.send_message(source, ServerGreeting).ok();
|
||||
for (pos, player) in &players {
|
||||
if player.0 == source {
|
||||
render_update.send(RenderUpdate(pos.pos()));
|
||||
render_update.send(RenderUpdate(pos.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,92 +6,103 @@ pub struct PhysicsPlugin;
|
|||
|
||||
impl Plugin for PhysicsPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(
|
||||
Update,
|
||||
(
|
||||
update_physics_forces.after(player::handle_move),
|
||||
update_physics_center.after(update_physics_forces),
|
||||
update_physics_collisions.after(update_physics_center),
|
||||
),
|
||||
);
|
||||
app.add_systems(Update, update_physics_forces.after(player::handle_move))
|
||||
.add_systems(Update, update_physics_pos.after(update_physics_forces));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Component, Clone)]
|
||||
pub struct Pos(pub Vec2);
|
||||
|
||||
impl Pos {
|
||||
pub fn fpos(&self) -> Vec2 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn pos_center(&self) -> Vec2 {
|
||||
self.pos().as_vec2()
|
||||
}
|
||||
|
||||
pub fn pos(&self) -> IVec2 {
|
||||
self.0.round().as_ivec2()
|
||||
}
|
||||
|
||||
pub fn dir_to_center(&self) -> Vec2 {
|
||||
self.pos_center() - self.fpos()
|
||||
}
|
||||
}
|
||||
pub struct Pos(pub IVec2);
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Mass(pub usize);
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Forces(Vec2);
|
||||
pub struct Forces(pub IVec2);
|
||||
|
||||
impl Forces {
|
||||
pub fn zero() -> Self {
|
||||
Self::new(Vec2::ZERO)
|
||||
Self::new(IVec2::ZERO)
|
||||
}
|
||||
pub fn new(forces: Vec2) -> Self {
|
||||
|
||||
pub fn new(forces: IVec2) -> Self {
|
||||
Self(forces)
|
||||
}
|
||||
pub fn add(&mut self, force: Vec2) {
|
||||
|
||||
pub fn add(&mut self, force: IVec2) {
|
||||
self.0 += force;
|
||||
}
|
||||
|
||||
pub fn movement(&self, mass: usize) -> IVec2 {
|
||||
self.0 / mass as i32
|
||||
}
|
||||
|
||||
pub fn movement_step(&self, mass: usize) -> IVec2 {
|
||||
self.movement(mass).clamp(IVec2::new(-1, -1), IVec2::new(1, 1))
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_MOVEMENT_PER_TICK: f32 = 0.3;
|
||||
// 1. reduce force of adjascent objects.
|
||||
pub fn update_physics_forces(mut forces: Query<(&mut Forces, &Pos, &Mass)>) {
|
||||
let mut combinations = forces.iter_combinations_mut();
|
||||
while let Some([left, right]) = combinations.fetch_next() {
|
||||
let (mut l_forces, l_pos, Mass(l_mass)) = left;
|
||||
let (mut r_forces, r_pos, Mass(r_mass)) = right;
|
||||
let l_step = l_forces.movement_step(*l_mass);
|
||||
let r_step = r_forces.movement_step(*r_mass);
|
||||
let (l_mass, r_mass) = (*l_mass as i32, *r_mass as i32);
|
||||
let same_step_x = r_step.x == l_step.x;
|
||||
let same_step_y = r_step.y == l_step.y;
|
||||
|
||||
fn update_physics_forces(mut blocks: Query<(&mut Pos, &mut Forces)>, mut updater: EventWriter<RenderUpdate>) {
|
||||
for (mut pos, mut forces) in &mut blocks {
|
||||
let old_pos = pos.clone();
|
||||
if l_pos.0.distance_squared(r_pos.0) > 2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let applied = forces.0;
|
||||
let applied = applied.clamp_length_max(MAX_MOVEMENT_PER_TICK);
|
||||
pos.0 += applied;
|
||||
forces.0 -= applied;
|
||||
if !same_step_x {
|
||||
let l_will_overlap_x = l_pos.0 + IVec2::X * l_step.x == r_pos.0;
|
||||
let r_will_overlap_x = r_pos.0 + IVec2::X * r_step.x == l_pos.0;
|
||||
|
||||
if old_pos.pos() != pos.pos() {
|
||||
updater.send(RenderUpdate(old_pos.pos()));
|
||||
if l_will_overlap_x {
|
||||
r_forces.0.x += l_step.x * l_mass;
|
||||
l_forces.0.x -= l_step.x * r_mass.min(l_mass);
|
||||
}
|
||||
|
||||
if r_will_overlap_x {
|
||||
l_forces.0.x += r_step.x * r_mass;
|
||||
r_forces.0.x -= r_step.x * l_mass.min(r_mass);
|
||||
}
|
||||
}
|
||||
|
||||
if !same_step_y {
|
||||
let l_will_overlap_y = l_pos.0 + IVec2::Y * l_step.y == r_pos.0;
|
||||
let r_will_overlap_y = r_pos.0 + IVec2::Y * r_step.y == l_pos.0;
|
||||
|
||||
if l_will_overlap_y {
|
||||
r_forces.0.y += l_step.y * l_mass;
|
||||
l_forces.0.y -= l_step.y * r_mass.min(l_mass);
|
||||
}
|
||||
|
||||
if r_will_overlap_y {
|
||||
l_forces.0.y += r_step.y * r_mass;
|
||||
r_forces.0.y -= r_step.y * l_mass.min(r_mass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_physics_collisions(mut blocks: Query<(&mut Pos, &Mass)>) {
|
||||
let mut combinations = blocks.iter_combinations_mut();
|
||||
while let Some([(mut a_pos, Mass(a_mass)), (mut b_pos, Mass(b_mass))]) = combinations.fetch_next() {
|
||||
if a_pos.pos() == b_pos.pos() {
|
||||
let a_to_b = (b_pos.fpos() - a_pos.fpos()).normalize_or(Vec2::Y);
|
||||
let total_mass = (a_mass + b_mass) as f32;
|
||||
let a_mass_frac = (*a_mass) as f32 / total_mass;
|
||||
let b_mass_frac = (*b_mass) as f32 / total_mass;
|
||||
let a_movement = (-a_to_b) * b_mass_frac;
|
||||
let b_movement = (a_to_b) * a_mass_frac;
|
||||
a_pos.0 += a_movement;
|
||||
b_pos.0 += b_movement;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. apply 1 step of forces.
|
||||
|
||||
pub fn update_physics_center(mut blocks: Query<(&Pos, &mut Forces)>) {
|
||||
for (pos, mut forces) in &mut blocks {
|
||||
let dir = pos.pos_center() - pos.0;
|
||||
forces.add(dir * 0.75);
|
||||
pub fn update_physics_pos(mut bodies: Query<(&mut Forces, &mut Pos, &Mass)>, mut updater: EventWriter<RenderUpdate>) {
|
||||
for (mut forces, mut pos, Mass(mass)) in &mut bodies {
|
||||
let step = forces.movement_step(*mass);
|
||||
|
||||
if step == IVec2::ZERO {
|
||||
continue;
|
||||
}
|
||||
|
||||
updater.send(RenderUpdate(pos.0));
|
||||
pos.0 += step;
|
||||
forces.0 -= step * *mass as i32;
|
||||
updater.send(RenderUpdate(pos.0));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use bevy_eventwork::{tcp::TcpProvider, AppNetworkMessage, ConnectionId, NetworkD
|
|||
|
||||
use super::{
|
||||
display::{Display, RenderUpdate},
|
||||
generation::Loader,
|
||||
physics::{Forces, Mass, Pos},
|
||||
};
|
||||
|
||||
|
@ -32,14 +33,17 @@ fn player_display_for_dir(dir: IVec2) -> (char, char) {
|
|||
}
|
||||
}
|
||||
|
||||
const PLAYER_MASS: usize = 10;
|
||||
|
||||
pub fn spawn_player(cmd: &mut Commands, conn_id: ConnectionId, pos: IVec2) {
|
||||
cmd.spawn((
|
||||
Player(conn_id),
|
||||
Pos(pos.as_vec2()),
|
||||
Loader,
|
||||
Pos(pos),
|
||||
Display(player_display_for_dir(IVec2::X)),
|
||||
Dir(IVec2::X),
|
||||
Forces::zero(),
|
||||
Mass(10),
|
||||
Mass(PLAYER_MASS),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -52,21 +56,18 @@ pub fn remove_player(cmd: &mut Commands, conn_id: ConnectionId, players: &Query<
|
|||
}
|
||||
|
||||
pub fn handle_move(
|
||||
mut messages: EventReader<NetworkData<Move>>,
|
||||
mut players: Query<(&Player, &mut Forces, &Pos, &mut Dir, &mut Display)>,
|
||||
mut moves: EventReader<NetworkData<Move>>,
|
||||
mut players: Query<(&Player, &mut Forces, &mut Dir, &mut Display)>,
|
||||
) {
|
||||
for message in messages.read() {
|
||||
let conn_id = message.source();
|
||||
for (Player(id), mut forces, pos, mut dir, mut display) in &mut players {
|
||||
for move_ in moves.read() {
|
||||
let conn_id = move_.source();
|
||||
for (Player(id), mut forces, mut dir, mut display) in &mut players {
|
||||
if conn_id == id {
|
||||
let dir_movement = message.0.as_vec2().normalize_or_zero();
|
||||
let added_force = dir_movement + pos.dir_to_center();
|
||||
forces.add(added_force);
|
||||
|
||||
let idir = dir_movement.as_ivec2();
|
||||
if !(idir == IVec2::ZERO) {
|
||||
dir.0 = idir;
|
||||
display.0 = player_display_for_dir(idir);
|
||||
let mov_dir = move_.0.clamp(IVec2::splat(-1), IVec2::splat(1));
|
||||
forces.0 = mov_dir * PLAYER_MASS as i32;
|
||||
if !(mov_dir == IVec2::ZERO) {
|
||||
dir.0 = mov_dir;
|
||||
display.0 = player_display_for_dir(mov_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue