aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-23 07:52:14 +0100
committerLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-23 08:19:02 +0100
commit0c1251e4d9dd4ae64fb06e4caea87c5306ffb39c (patch)
tree6a3b94ab815415799da318defea1a3811fc90eaa
parent7f962b034f888135ce618e03bea40a08b57a68c7 (diff)
feat: movement collision
-rw-r--r--src/server.rs148
1 files changed, 111 insertions, 37 deletions
diff --git a/src/server.rs b/src/server.rs
index b09e96b..0a5e4cf 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -20,9 +20,11 @@ use crate::shared::shape::*;
use crate::shared::stats::*;
use crate::shared::tower::*;
use bevy::prelude::*;
+use bevy::utils::hashbrown::HashMap;
use bevy::utils::*;
use lightyear::prelude::*;
use lightyear::server::events::MessageEvent;
+use rand::rngs::ThreadRng;
use rand::Rng;
pub mod entity_map;
@@ -100,59 +102,51 @@ impl Plugin for ServerPlugin {
.before(imperative_attack_approach),
)
.add_systems(FixedUpdate, minion_despawn)
- .add_systems(FixedUpdate, (nexus_tick, nexus_spawn_minions));
+ .add_systems(FixedUpdate, (nexus_tick, nexus_spawn_minions))
+ .add_systems(
+ FixedUpdate,
+ movement_collision
+ .after(imperative_attack_approach)
+ .after(imperative_walk_to),
+ );
}
}
fn setup(mut commands: Commands, mut entity_map: ResMut<EntityMap>) {
- {
- let client_id = 1;
+ let [blue_tower_1, blue_tower_2, blue_tower_3, blue_nexus, red_tower_1, red_tower_2, red_tower_3, red_nexus] =
+ generate_client_ids::<8>(&entity_map).into();
+
+ for (n, client_id) in [(1, blue_tower_1), (2, blue_tower_2), (3, blue_tower_3)].iter() {
let entity = commands.spawn(TowerBundle::new(
- client_id,
- Vec2::new(0., 100.),
+ *client_id,
+ Vec2::new(0., *n as f32 * 250.),
Faction::Blue,
));
- entity_map.0.insert(client_id, entity.id());
+ entity_map.0.insert(*client_id, entity.id());
}
{
- let client_id = 2;
- let entity = commands.spawn(TowerBundle::new(
- client_id,
- Vec2::new(0., -100.),
- Faction::Red,
- ));
- entity_map.0.insert(client_id, entity.id());
- }
- for client_id in 3..=8 {
- let entity = commands.spawn(MinionBundle::new(
+ let client_id = blue_nexus;
+ let entity = commands.spawn(NexusBundle::new(
client_id,
- Vec2::new((client_id - 2) as f32 * 25., 0.),
+ Vec2::new(0., 1000.0),
Faction::Blue,
));
entity_map.0.insert(client_id, entity.id());
}
- for client_id in 9..=14 {
- let entity = commands.spawn(MinionBundle::new(
- client_id,
- Vec2::new((client_id - 8) as f32 * -25., 0.),
+
+ for (n, client_id) in [(1, red_tower_1), (2, red_tower_2), (3, red_tower_3)].iter() {
+ let entity = commands.spawn(TowerBundle::new(
+ *client_id,
+ Vec2::new(0., *n as f32 * -250.),
Faction::Red,
));
- entity_map.0.insert(client_id, entity.id());
- }
- {
- let client_id = 15;
- let entity = commands.spawn(NexusBundle::new(
- client_id,
- Vec2::new(100., 100.0),
- Faction::Blue,
- ));
- entity_map.0.insert(client_id, entity.id());
+ entity_map.0.insert(*client_id, entity.id());
}
{
- let client_id = 16;
+ let client_id = red_nexus;
let entity = commands.spawn(NexusBundle::new(
client_id,
- Vec2::new(-100., -100.0),
+ Vec2::new(0., -1000.0),
Faction::Red,
));
entity_map.0.insert(client_id, entity.id());
@@ -858,7 +852,7 @@ fn nexus_spawn_minions(
) {
for (nexus_nexus, nexus_player_position, nexus_faction) in nexuses.iter() {
if nexus_nexus.spawn_minions.just_finished() {
- let client_ids = generate_client_ids(5, &entity_map);
+ let client_ids = generate_client_ids::<5>(&entity_map);
for client_id in client_ids {
let entity = commands.spawn(MinionBundle::new(
client_id,
@@ -871,11 +865,11 @@ fn nexus_spawn_minions(
}
}
-fn generate_client_ids(n: u64, entity_map: &ResMut<EntityMap>) -> Vec<u64> {
+fn generate_client_ids<const N: usize>(entity_map: &ResMut<EntityMap>) -> [u64; N] {
let mut rng = rand::thread_rng();
let mut k = 0;
let mut client_ids = vec![];
- while k < n {
+ while k < N {
let client_id = rng.gen();
if entity_map.0.contains_key(&client_id) {
continue;
@@ -883,7 +877,7 @@ fn generate_client_ids(n: u64, entity_map: &ResMut<EntityMap>) -> Vec<u64> {
client_ids.push(client_id);
k += 1;
}
- client_ids
+ client_ids.try_into().unwrap()
}
fn nexus_tick(mut nexuses: Query<&mut Nexus>, time: Res<Time>) {
@@ -892,3 +886,83 @@ fn nexus_tick(mut nexuses: Query<&mut Nexus>, time: Res<Time>) {
nexus.spawn_minions.tick(dt);
}
}
+
+pub fn movement_collision(
+ mut entities: Query<(
+ Entity,
+ &mut PlayerPosition,
+ &Shape,
+ Option<&Immovable>,
+ Option<&Imperative>,
+ )>,
+) {
+ let mut corrections = HashMap::<Entity, Vec2>::new();
+ let mut rng = rand::thread_rng();
+ for (entity, entity_player_position, entity_shape, entity_immovable, entity_imperative) in
+ entities.iter()
+ {
+ let stationary = entity_immovable.is_some() || {
+ entity_imperative
+ .map(|imperative| match imperative {
+ Imperative::WalkTo(_) => false,
+ _ => true,
+ })
+ .unwrap_or(true)
+ };
+ for (
+ other_entity,
+ other_entity_player_position,
+ other_entity_shape,
+ other_entity_immovable,
+ other_entity_imperative,
+ ) in entities.iter()
+ {
+ if other_entity == entity {
+ continue;
+ }
+ let other_stationary = other_entity_immovable.is_some() || {
+ other_entity_imperative
+ .map(|other_imperative| match other_imperative {
+ Imperative::WalkTo(_) => false,
+ _ => true,
+ })
+ .unwrap_or(true)
+ };
+ let distance = entity_player_position
+ .0
+ .distance(other_entity_player_position.0);
+ let overlap_distance = entity_shape.radius + other_entity_shape.radius - distance;
+ let is_intersecting = overlap_distance > 0.;
+ if !is_intersecting {
+ continue;
+ }
+ let correction_direction = (entity_player_position.0 - other_entity_player_position.0)
+ .try_normalize()
+ .unwrap_or(random_unit_vector(&mut rng));
+ let displacement_factor = if stationary {
+ 0.
+ } else if other_stationary {
+ 1.
+ } else {
+ other_entity_shape.radius / (entity_shape.radius + other_entity_shape.radius)
+ };
+ let force = displacement_factor * overlap_distance * correction_direction;
+ if let Some(existing_force) = corrections.get_mut(&entity) {
+ *existing_force += force;
+ } else {
+ corrections.insert(entity, force);
+ }
+ }
+ }
+ for (entity, mut entity_player_position, _, _, _) in entities.iter_mut() {
+ entity_player_position.0 += *(corrections.get(&entity).unwrap_or(&Vec2::ZERO));
+ }
+}
+
+fn random_unit_vector(rng: &mut ThreadRng) -> Vec2 {
+ loop {
+ if let Some(v) = Vec2::new(rng.gen(), rng.gen()).try_normalize() {
+ return v;
+ }
+ }
+}