2 Commits

Author SHA1 Message Date
  Bergmann89 9fb731b856 Implemented ship obstacles 3 years ago
  Bergmann89 5a52cc04c4 Remove unneded system data from ships system 3 years ago
15 changed files with 276 additions and 119 deletions
Split View
  1. +14
    -0
      glc/src/matrix.rs
  2. +1
    -1
      space-crush-app/src/constants.rs
  3. +25
    -11
      space-crush-app/src/debug/ships.rs
  4. +4
    -1
      space-crush-app/src/lib.rs
  5. +61
    -68
      space-crush-app/src/main.rs
  6. +1
    -1
      space-crush-app/src/render/fleet_select.rs
  7. +3
    -1
      space-crush-common/src/components/mod.rs
  8. +9
    -0
      space-crush-common/src/components/obstacle.rs
  9. +3
    -3
      space-crush-common/src/components/position.rs
  10. +16
    -1
      space-crush-common/src/components/ship.rs
  11. +2
    -2
      space-crush-common/src/constants.rs
  12. +10
    -0
      space-crush-common/src/macros.rs
  13. +11
    -1
      space-crush-common/src/resources/global.rs
  14. +3
    -1
      space-crush-common/src/systems/movement.rs
  15. +113
    -28
      space-crush-common/src/systems/ships.rs

+ 14
- 0
glc/src/matrix.rs View File

@@ -323,6 +323,20 @@ where
Vector3::new(mul!(2, 0), mul!(2, 1), mul!(2, 2)),
)
}

#[inline]
pub fn transform<V: Into<Vector2<T>>>(&self, v: V) -> Vector2<T> {
let m = self;
let v = V::into(v);

macro_rules! mul {
($i:tt) => {
m[0][$i] * v[0] + m[1][$i] * v[1] + m[2][$i] * T::one()
};
}

Vector2::new(mul!(0), mul!(1))
}
}

impl<T> Matrix3<T>


+ 1
- 1
space-crush-app/src/constants.rs View File

@@ -5,7 +5,7 @@ use glc::vector::Vector4f;
pub const UNIFORM_BUFFER_INDEX_CAMERA: gl::GLuint = 0;
pub const UNIFORM_BUFFER_INDEX_UNIFORM: gl::GLuint = 1;

pub const SHIP_SIZE: f32 = 15.0;
pub const SHIP_SIZE: f32 = 25.0;
pub const PLANET_SIZE: f32 = 200.0;
pub const ASTEROID_SIZE: f32 = 100.0;



+ 25
- 11
space-crush-app/src/debug/ships.rs View File

@@ -1,5 +1,8 @@
use glc::vector::Vector4f;
use space_crush_common::components::{Position, Ship, Velocity};
use space_crush_common::{
components::{Position, Ship, ShipObstacle, Velocity},
continue_if_none,
};
use specs::{prelude::*, ReadStorage, System, World, WriteExpect};

use crate::resources::Geometry;
@@ -10,9 +13,9 @@ pub struct Ships;
#[derive(SystemData)]
pub struct ShipsData<'a> {
geometry: WriteExpect<'a, Geometry>,
position: ReadStorage<'a, Position>,
velocity: ReadStorage<'a, Velocity>,
ship: ReadStorage<'a, Ship>,
positions: ReadStorage<'a, Position>,
velocities: ReadStorage<'a, Velocity>,
ships: ReadStorage<'a, Ship>,
}

impl<'a> System<'a> for Ships {
@@ -21,24 +24,35 @@ impl<'a> System<'a> for Ships {
fn run(&mut self, data: Self::SystemData) {
let ShipsData {
mut geometry,
position,
velocity,
ship,
positions,
velocities,
ships,
} = data;

gl::enable(gl::BLEND);
gl::blend_func(gl::SRC_ALPHA, gl::ONE);

for (p, v, s) in (&position, &velocity, &ship).join() {
for (position, velocity, ship) in (&positions, &velocities, &ships).join() {
let ship_pos = position.pos;

geometry.render_lines(
Vector4f::new(0.0, 0.0, 1.0, 0.2),
&[p.pos, p.pos + v.dir * v.speed],
&[ship_pos, ship_pos + velocity.dir * velocity.speed],
);
geometry.render_lines(
Vector4f::new(1.0, 0.0, 0.0, 0.2),
&[p.pos, p.pos + s.target_dir * 100.0],
&[ship_pos, ship_pos + ship.target_dir * 100.0],
);
geometry.render_lines(
Vector4f::new(1.0, 1.0, 1.0, 0.2),
&[ship_pos, ship.target_pos],
);
geometry.render_lines(Vector4f::new(1.0, 1.0, 1.0, 0.2), &[p.pos, s.target_pos]);

if let ShipObstacle::Known(obstacle) = ship.obstacle {
let obstacle_pos = continue_if_none!(positions.get(obstacle)).pos;

geometry.render_lines(Vector4f::new(0.0, 1.0, 0.0, 0.2), &[ship_pos, obstacle_pos]);
}
}

gl::disable(gl::BLEND);


+ 4
- 1
space-crush-app/src/lib.rs View File

@@ -7,6 +7,7 @@ pub mod render;
pub mod resources;
pub mod systems;

use glc::matrix::Matrix4f;
use specs::{Dispatcher, DispatcherBuilder, Entity, World};

pub use error::Error;
@@ -30,12 +31,14 @@ impl<'a, 'b> App<'a, 'b> {
let events = Events::new(world)?;
let window = Window::new(events.handle(), &config)?;

let camera = Camera::new()?;
let mut camera = Camera::new()?;
let uniform = Uniform::new()?;
let geometry = Geometry::new(world)?;
let input_state = InputState::default();
let player_state = PlayerState::new(player_id);

camera.update(Matrix4f::scale(0.25))?;

world.insert(config);
world.insert(camera);
world.insert(uniform);


+ 61
- 68
space-crush-app/src/main.rs View File

@@ -60,8 +60,8 @@ fn create_world(world: &mut World, player_id: Entity) {
use space_crush_app::components::FleetInfo;
use space_crush_common::{
components::{
Asteroid, AsteroidType, Fleet, FleetOwned, Planet, Player, PlayerOwned, Position,
Shape, Ship, ShipType, Velocity,
Asteroid, AsteroidType, Fleet, FleetOwned, Obstacle, Planet, Player, PlayerOwned,
Position, Shape, Ship, ShipType, Velocity,
},
misc::{PersistWorld, Persistence},
};
@@ -79,88 +79,80 @@ fn create_world(world: &mut World, player_id: Entity) {
)
.unwrap();

let planet = world
.create_entity()
.marked::<<PersistWorld as Persistence>::Marker>()
.with(PlayerOwned { owner: player_id })
.with(Position {
pos: Vector2f::default(),
shape: Shape::Circle(250.0),
let planets = (0..3)
.map(|i| {
let x = 2000.0 * (i as f32 - 1.0);
let y = 0.0;

world
.create_entity()
.marked::<<PersistWorld as Persistence>::Marker>()
.with(PlayerOwned { owner: player_id })
.with(Position {
pos: Vector2f::new(x, y),
shape: Shape::Circle(250.0),
})
.with(Fleet {
orbit_min: 325.0,
orbit_max: 425.0,
})
.with(FleetInfo::default())
.with(Obstacle {})
.with(Planet {})
.build()
})
.with(Fleet {
orbit_min: 325.0,
orbit_max: 425.0,
})
.with(FleetInfo::default())
.with(Planet {})
.build();
.collect::<Vec<_>>();

world
.create_entity()
.marked::<<PersistWorld as Persistence>::Marker>()
.with(Position {
pos: Vector2f::new(2000.0, 0.0),
shape: Shape::Circle(250.0),
})
.with(Fleet {
orbit_min: 325.0,
orbit_max: 425.0,
})
.with(FleetInfo::default())
.with(Planet {})
.build();
for i in 0..4 {
let i_x: isize = if i & 1 == 0 { -1 } else { 1 };
let i_y: isize = if i & 2 == 0 { -1 } else { 1 };

world
.create_entity()
.marked::<<PersistWorld as Persistence>::Marker>()
.with(Position {
pos: Vector2f::new(1000.0, -1000.0),
shape: Shape::Circle(100.0),
})
.with(Fleet {
orbit_min: 125.0,
orbit_max: 175.0,
})
.with(FleetInfo::default())
.with(Asteroid {
type_: AsteroidType::Metal,
})
.build();
let x = 1000.0 * i_x as f32;
let y = 1000.0 * i_y as f32;

world
.create_entity()
.marked::<<PersistWorld as Persistence>::Marker>()
.with(Position {
pos: Vector2f::new(1000.0, 1000.0),
shape: Shape::Circle(100.0),
})
.with(Fleet {
orbit_min: 125.0,
orbit_max: 175.0,
})
.with(FleetInfo::default())
.with(Asteroid {
type_: AsteroidType::Crystal,
})
.build();
world
.create_entity()
.marked::<<PersistWorld as Persistence>::Marker>()
.with(Position {
pos: Vector2f::new(x, y),
shape: Shape::Circle(100.0),
})
.with(Fleet {
orbit_min: 125.0,
orbit_max: 175.0,
})
.with(FleetInfo::default())
.with(Obstacle {})
.with(Asteroid {
type_: match i_x * i_y {
-1 => AsteroidType::Metal,
1 => AsteroidType::Crystal,
_ => unreachable!(),
},
})
.build();
}

for i in 0..999 {
let r = 325.0 + 100.0 * random::<f32>();
let a = Angle::Deg(360.0 * random::<f32>());

let x = r * a.cos();
let y = r * a.sin();

for i in 0..30 {
world
.create_entity()
.marked::<<PersistWorld as Persistence>::Marker>()
.with(PlayerOwned { owner: player_id })
.with(Position {
pos: Vector2f::new(
500.0 * random::<f32>() - 250.0,
500.0 * random::<f32>() - 250.0,
),
pos: Vector2f::new(x, y),
shape: Shape::Dot,
})
.with(Velocity {
dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(),
speed: 100.0,
})
.with(FleetOwned { owner: planet })
.with(FleetOwned { owner: planets[1] })
.with(Ship {
type_: match i % 3 {
0 => ShipType::Fighter,
@@ -171,6 +163,7 @@ fn create_world(world: &mut World, player_id: Entity) {
agility: Angle::Deg(360.0),
target_pos: Default::default(),
target_dir: Default::default(),
obstacle: Default::default(),
})
.build();
}


+ 1
- 1
space-crush-app/src/render/fleet_select.rs View File

@@ -267,7 +267,7 @@ impl FleetSelect {

self.marker = d.camera.view_to_world(self.mouse_pos) - position.pos;
self.zoom = d.camera.view().axis_x.as_vec3().length();
self.shape_size = position.shape.radius().unwrap_or(0.0);
self.shape_size = position.shape.radius();
self.ring0 = self.shape_size + FLEET_SELECT_OFFSET / self.zoom;
self.ring1 = self.ring0 + FLEET_SELECT_WIDTH / self.zoom;



+ 3
- 1
space-crush-common/src/components/mod.rs View File

@@ -1,5 +1,6 @@
mod asteroid;
mod fleet;
mod obstacle;
mod planet;
mod player;
mod position;
@@ -8,8 +9,9 @@ mod velocity;

pub use asteroid::{Asteroid, Type as AsteroidType};
pub use fleet::{Fleet, Owned as FleetOwned};
pub use obstacle::Obstacle;
pub use planet::Planet;
pub use player::{Owned as PlayerOwned, Player};
pub use position::{Position, Shape};
pub use ship::{Count as ShipCount, Ship, Type as ShipType};
pub use ship::{Count as ShipCount, Obstacle as ShipObstacle, Ship, Type as ShipType};
pub use velocity::Velocity;

+ 9
- 0
space-crush-common/src/components/obstacle.rs View File

@@ -0,0 +1,9 @@
use serde::{Deserialize, Serialize};
use specs::{Component, NullStorage};

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Obstacle {}

impl Component for Obstacle {
type Storage = NullStorage<Self>;
}

+ 3
- 3
space-crush-common/src/components/position.rs View File

@@ -19,10 +19,10 @@ impl Component for Position {
}

impl Shape {
pub fn radius(&self) -> Option<f32> {
pub fn radius(&self) -> f32 {
match self {
Self::Dot => Some(0.0),
Self::Circle(r) => Some(*r),
Self::Dot => 0.0,
Self::Circle(r) => *r,
}
}



+ 16
- 1
space-crush-common/src/components/ship.rs View File

@@ -3,7 +3,7 @@ use std::ops::{Index, IndexMut, Mul};

use glc::{matrix::Angle, vector::Vector2f};
use serde::{Deserialize, Serialize};
use specs::{Component, VecStorage};
use specs::{Component, Entity, VecStorage};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Ship {
@@ -11,6 +11,15 @@ pub struct Ship {
pub agility: Angle<f32>,
pub target_pos: Vector2f,
pub target_dir: Vector2f,
#[serde(skip)]
pub obstacle: Obstacle,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Obstacle {
Known(Entity),
Search,
Done,
}

#[derive(Copy, Clone, Debug, Default)]
@@ -31,6 +40,12 @@ impl Component for Ship {
type Storage = VecStorage<Self>;
}

impl Default for Obstacle {
fn default() -> Self {
Self::Search
}
}

impl Count {
pub fn all() -> Self {
Self {


+ 2
- 2
space-crush-common/src/constants.rs View File

@@ -4,10 +4,10 @@ use glc::{matrix::Angle, vector::Vector2f};
pub const SHIP_ORBIT_DISTANCE_MAX: f32 = 1.10;

/// Minimum angle between old and new target position in orbit
pub const SHIP_ORBIT_ANGLE_DELTA_MIN: Angle<f32> = Angle::Deg(7000.0);
pub const SHIP_ORBIT_ANGLE_DELTA_MIN: Angle<f32> = Angle::Deg(5000.0);

/// Random angle between old and new target position in orbit
pub const SHIP_ORBIT_ANGLE_DELTA_RND: Angle<f32> = Angle::Deg(4000.0);
pub const SHIP_ORBIT_ANGLE_DELTA_RND: Angle<f32> = Angle::Deg(5000.0);

/// Agility of ships inside orbit
pub const SHIP_ORBIT_AGILITY: Angle<f32> = Angle::Deg(90.0);


+ 10
- 0
space-crush-common/src/macros.rs View File

@@ -17,3 +17,13 @@ macro_rules! break_if_none {
}
};
}

#[macro_export]
macro_rules! continue_if_none {
($value:expr) => {
match $value {
Some(value) => value,
None => continue,
}
};
}

+ 11
- 1
space-crush-common/src/resources/global.rs View File

@@ -1,5 +1,15 @@
#[derive(Default)]
pub struct Global {
pub fps: usize,
pub delta: f32,
pub world_speed: f32,
}

impl Default for Global {
fn default() -> Self {
Self {
fps: 0,
delta: 0.0,
world_speed: 1.0,
}
}
}

+ 3
- 1
space-crush-common/src/systems/movement.rs View File

@@ -30,7 +30,9 @@ impl<'a> System<'a> for Movement {
(&mut position, &velocity)
.par_join()
.for_each(|(position, velocity)| {
position.pos = position.pos + velocity.dir * velocity.speed * global.delta;
let delta = global.delta * global.world_speed;

position.pos = position.pos + velocity.dir * velocity.speed * delta;
});
}
}

+ 113
- 28
space-crush-common/src/systems/ships.rs View File

@@ -1,16 +1,17 @@
use glc::{
math::{linear_step, sqr},
matrix::Matrix3f,
vector::{Vector2f, Vector3f},
vector::{Angle, Vector2f, Vector3f},
};
use rand::random;
use shrev::ReaderId;
use specs::{
hibitset::BitSet, prelude::*, Entities, ParJoin, Read, ReadStorage, System, WriteStorage,
hibitset::BitSet, prelude::*, world::Index, Entities, ParJoin, Read, ReadStorage, System,
WriteStorage,
};

use crate::{
components::{Fleet, FleetOwned, Position, Ship, Velocity},
components::{Fleet, FleetOwned, Obstacle, Position, Ship, ShipObstacle, Velocity},
constants::{
SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND,
SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X,
@@ -33,12 +34,15 @@ pub struct ShipsData<'a> {
velocities: WriteStorage<'a, Velocity>,
fleet_owned: ReadStorage<'a, FleetOwned>,
positions: ReadStorage<'a, Position>,
obstacles: ReadStorage<'a, Obstacle>,
fleets: ReadStorage<'a, Fleet>,
}

struct Processor<'a> {
need_update: &'a BitSet,
entities: &'a Entities<'a>,
positions: &'a ReadStorage<'a, Position>,
obstacles: &'a ReadStorage<'a, Obstacle>,
fleets: &'a ReadStorage<'a, Fleet>,
delta: f32,
}
@@ -91,6 +95,7 @@ impl<'a> System<'a> for Ships {
mut velocities,
fleet_owned,
positions,
obstacles,
fleets,
} = data;

@@ -99,20 +104,24 @@ impl<'a> System<'a> for Ships {
/* update ships */
let processor = Processor {
need_update: &self.need_update,
entities: &entities,
positions: &positions,
obstacles: &obstacles,
fleets: &fleets,
delta: global.delta,
delta: global.delta * global.world_speed,
};
(
&entities,

let data = (
positions.mask(),
&mut ships,
&mut velocities,
&positions,
&fleet_owned,
)
.par_join()
.for_each(|(entity, ship, velocity, position, fleet_owned)| {
processor.progress_ship(entity, ship, velocity, position, fleet_owned);
);

data.par_join()
.for_each(|(id, ship, velocity, position, fleet_owned)| {
processor.progress_ship(id, ship, velocity, position, fleet_owned);
});
}
}
@@ -120,7 +129,7 @@ impl<'a> System<'a> for Ships {
impl Processor<'_> {
fn progress_ship(
&self,
entity: Entity,
id: Index,
ship: &mut Ship,
velocity: &mut Velocity,
position: &Position,
@@ -134,8 +143,8 @@ impl Processor<'_> {
let target_dir = ship.target_dir;

let orbit_to_target = target_pos - orbit_pos;
let orbit_to_ship = position.pos - orbit_pos;
let ship_to_target = target_pos - position.pos;
let orbit_to_ship = ship_pos - orbit_pos;
let mut ship_to_target = target_pos - position.pos;

let r_ship = orbit_to_ship.length_sqr();
let r_target = orbit_to_target.length_sqr();
@@ -146,7 +155,7 @@ impl Processor<'_> {
let target_in_orbit = (r_target <= sqr(fleet.orbit_max)) && (r_target >= sqr(orbit_min));
let ship_in_orbit = r_ship < sqr(SHIP_ORBIT_DISTANCE_MAX * orbit_max);

let need_update = self.need_update.contains(entity.id());
let need_update = self.need_update.contains(id);
let has_target = target_dir.length_sqr() != 0.0;
let passed_target = target_dir * ship_to_target <= 0.0;

@@ -162,27 +171,110 @@ impl Processor<'_> {
-1.0
};

let add = SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random();
let angle = orbit_to_ship.angle2(&VECTOR_2F_POS_X);
let angle = angle
+ (SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random()) * dir
/ fleet.orbit_max;
let radius =
fleet.orbit_min + (fleet.orbit_max - fleet.orbit_min) * random::<f32>();
let angle = angle + add * dir / orbit_max;
let radius = orbit_min + (orbit_max - orbit_min) * random::<f32>();

Vector2f::new(
orbit_pos.x + radius * angle.cos(),
orbit_pos.y + radius * angle.sin(),
)
} else {
ship.obstacle = ShipObstacle::Search;

orbit_pos
};

ship.target_pos = target_pos;
ship.target_dir = (target_pos - ship_pos).normalize();

ship_to_target = target_pos - ship_pos;
}

/* check if obstacle is still valid */
if ship_in_orbit {
ship.obstacle = ShipObstacle::Done;
} else if let ShipObstacle::Known(obstacle) = ship.obstacle {
if let Some(position) = self.positions.get(obstacle) {
let obstacle_fleet = self.fleets.get(obstacle).unwrap();
let obstacle_pos = position.pos;
let ship_to_obstacle = obstacle_pos - ship_pos;

let obstacle_angle = ship_to_target
.angle2(&ship_to_obstacle)
.into_deg()
.into_inner()
.abs();

let orbit_sqr = obstacle_fleet.orbit_max * obstacle_fleet.orbit_max;
if (obstacle_angle > 90.0 && ship_to_obstacle.length_sqr() > orbit_sqr)
|| obstacle_angle > 170.0
{
ship.obstacle = ShipObstacle::Search;
}
} else {
ship.obstacle = ShipObstacle::Search;
}
}

/* find obstacle */
if !ship_in_orbit && ship.obstacle == ShipObstacle::Search {
let mut dist_sqr = f32::MAX;
for (e, position, _) in (self.entities, self.positions, self.obstacles).join() {
let obstacle_pos = position.pos;
let ship_to_obstacle = obstacle_pos - ship_pos;

if ship_to_target * ship_to_obstacle < 0.0 {
continue; // obstacle is behind the ship
}

let len_sqr = ship_to_obstacle.length_sqr();
if len_sqr < dist_sqr {
dist_sqr = len_sqr;
ship.obstacle = ShipObstacle::Known(e);
}
}

if let ShipObstacle::Known(e) = ship.obstacle {
if e == fleet_owned.owner {
ship.obstacle = ShipObstacle::Done;
}
}
}

/* check the obstacle */
let mut expected_dir = ship_to_target;
if let ShipObstacle::Known(obstacle) = ship.obstacle {
let obstacle_pos = self.positions.get(obstacle).unwrap();
let obstacle_fleet = self.fleets.get(obstacle).unwrap();
let ship_to_obstacle = obstacle_pos.pos - ship_pos;

let orbit = ship_to_obstacle.length();
if orbit < obstacle_fleet.orbit_max {
let orbit_min = obstacle_fleet.orbit_min;
let orbit_max = obstacle_fleet.orbit_max;
let mut tangent = Vector2f::new(-ship_to_obstacle.y, ship_to_obstacle.x);

let radius = obstacle_pos.shape.radius();
let mut adjust_low = linear_step(orbit_min, radius, orbit);
let adjust_high = 1.0 - linear_step(orbit_max, orbit_min, orbit);

if ship_to_target * tangent < 0.0 {
tangent = -tangent;
} else {
adjust_low = -adjust_low;
}

let a_low = Angle::Deg(45.0);
let a_high = tangent.angle2(&ship_to_target);
let mat = Matrix3f::rotate(a_low * adjust_low + a_high * adjust_high);
expected_dir = mat.transform(tangent);
}
}

/* update ship direction */
let angle = ship_to_target.angle2(&velocity.dir);
let angle = expected_dir.angle2(&velocity.dir);
if angle.into_inner().abs() > 0.0001 {
let dir = angle.into_inner() / angle.abs().into_inner();
let agility = if ship_in_orbit {
@@ -192,14 +284,7 @@ impl Processor<'_> {
};
let rot_speed = agility * linear_step(0.0, 45.0, angle.abs().into_deg().into_inner());

let m = Matrix3f::new(
Vector3f::new(velocity.dir.y, -velocity.dir.x, 0.0),
Vector3f::new(velocity.dir.x, velocity.dir.y, 0.0),
Vector3f::new(0.0, 0.0, 1.0),
);
let m = m * Matrix3f::rotate(rot_speed * -dir * self.delta);

velocity.dir = Vector2f::new(m.axis_y.x, m.axis_y.y);
velocity.dir = Matrix3f::rotate(rot_speed * -dir * self.delta).transform(velocity.dir);
}
}
}

Loading…
Cancel
Save