-
-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Relates to #24.
- Loading branch information
Showing
3 changed files
with
198 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
use std::time::Duration; | ||
|
||
use bevy::{ | ||
prelude::*, | ||
render::mesh::{Indices, PrimitiveTopology}, | ||
}; | ||
use de_core::state::GameState; | ||
use iyes_loopless::prelude::*; | ||
use parry3d::query::Ray; | ||
|
||
use crate::AttackingLabels; | ||
|
||
/// All but bottom vertex of a hexagon. The hexagon lies on plane perpendicular | ||
/// to X axis. The vertices start with the bottom-right point. | ||
/// | ||
/// It looks like this: | ||
/// | ||
/// /\ | ||
/// / \ | ||
/// | | | ||
/// | | | ||
const HEXAGON_VERTICES: [[f32; 3]; 5] = [ | ||
[0., -0.25, 0.433], | ||
[0., 0.25, 0.433], | ||
[0., 0.5, 0.], | ||
[0., 0.25, -0.433], | ||
[0., -0.25, -0.433], | ||
]; | ||
|
||
/// Outwards normals of a hexagon whose base is given by [`HEXAGON_VERTICES`]. | ||
/// The bottom two edges are / surfaces are not included. It starts with normal | ||
/// of the right (largest Z coordinate) surface. | ||
const HEXAGON_NORMALS: [[f32; 3]; 4] = [ | ||
[0., 0., 1.], | ||
[0., 0.866_025_4, 0.5], | ||
[0., 0.866_025_4, -0.5], | ||
[0., 0., -1.], | ||
]; | ||
|
||
const BEAM_COLOR: Color = Color::rgba(0.2, 0., 1., 0.4); | ||
const BEAM_DURATION: Duration = Duration::from_millis(500); | ||
|
||
pub(crate) struct BeamPlugin; | ||
|
||
impl Plugin for BeamPlugin { | ||
fn build(&self, app: &mut App) { | ||
app.add_event::<SpawnBeamEvent>() | ||
.add_enter_system(GameState::Playing, setup) | ||
.add_system_set_to_stage( | ||
CoreStage::Update, | ||
SystemSet::new() | ||
.with_system( | ||
spawn | ||
.run_in_state(GameState::Playing) | ||
.label(AttackingLabels::Beam), | ||
) | ||
.with_system(despawn.run_in_state(GameState::Playing)), | ||
); | ||
} | ||
} | ||
|
||
pub(crate) struct SpawnBeamEvent(Ray); | ||
|
||
impl SpawnBeamEvent { | ||
/// Send this event to spawn a new beam. The beam will automatically | ||
/// disappear after a moment. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `ray` - the beam originates at the ray origin. The beam ends at the | ||
/// `ray.origin + ray.dir`. | ||
pub(crate) fn new(ray: Ray) -> Self { | ||
Self(ray) | ||
} | ||
|
||
fn ray(&self) -> &Ray { | ||
&self.0 | ||
} | ||
} | ||
|
||
struct BeamHandles { | ||
material: Handle<StandardMaterial>, | ||
mesh: Handle<Mesh>, | ||
} | ||
|
||
#[derive(Component)] | ||
struct Beam { | ||
timer: Timer, | ||
} | ||
|
||
impl Beam { | ||
fn new() -> Self { | ||
Self { | ||
timer: Timer::new(BEAM_DURATION, false), | ||
} | ||
} | ||
|
||
fn tick(&mut self, duration: Duration) -> bool { | ||
self.timer.tick(duration); | ||
self.timer.finished() | ||
} | ||
} | ||
|
||
fn spawn( | ||
mut commands: Commands, | ||
handles: Res<BeamHandles>, | ||
mut events: EventReader<SpawnBeamEvent>, | ||
) { | ||
for event in events.iter() { | ||
commands | ||
.spawn_bundle(PbrBundle { | ||
mesh: handles.mesh.clone(), | ||
material: handles.material.clone(), | ||
transform: Transform { | ||
translation: event.ray().origin.into(), | ||
rotation: Quat::from_rotation_arc(Vec3::X, event.ray().dir.normalize().into()), | ||
scale: Vec3::new(event.ray().dir.norm(), 0.1, 0.1), | ||
}, | ||
..Default::default() | ||
}) | ||
.insert(Beam::new()); | ||
} | ||
} | ||
|
||
fn despawn(mut commands: Commands, time: Res<Time>, mut query: Query<(Entity, &mut Beam)>) { | ||
for (entity, mut beam) in query.iter_mut() { | ||
if beam.tick(time.delta()) { | ||
commands.entity(entity).despawn_recursive(); | ||
} | ||
} | ||
} | ||
|
||
fn setup( | ||
mut commands: Commands, | ||
mut meshes: ResMut<Assets<Mesh>>, | ||
mut materials: ResMut<Assets<StandardMaterial>>, | ||
) { | ||
let material = materials.add(StandardMaterial { | ||
base_color: BEAM_COLOR, | ||
alpha_mode: AlphaMode::Blend, | ||
unlit: true, | ||
..Default::default() | ||
}); | ||
let mesh = meshes.add(generate_beam_mesh()); | ||
commands.insert_resource(BeamHandles { material, mesh }); | ||
} | ||
|
||
/// This generates a beam mesh, consisting of a hexagonal prism without the | ||
/// bottom two faces. | ||
/// | ||
/// Width (along Z axis) of the hexagon is 1. Length (along X axis) of the beam | ||
/// is 1. | ||
fn generate_beam_mesh() -> Mesh { | ||
let mut positions = Vec::with_capacity(16); | ||
let mut normals = Vec::with_capacity(positions.len()); | ||
let mut uvs = Vec::with_capacity(positions.len()); | ||
|
||
// First, add base of the beam (a hexagon). | ||
for i in 0..4 { | ||
positions.push(HEXAGON_VERTICES[i]); | ||
positions.push(HEXAGON_VERTICES[i + 1]); | ||
normals.push(HEXAGON_NORMALS[i]); | ||
normals.push(HEXAGON_NORMALS[i]); | ||
uvs.push([0., i as f32 / 4.]); | ||
uvs.push([0., (i + 1) as f32 / 4.]); | ||
} | ||
// Second, add the other end of the beam. | ||
for i in 0..positions.len() { | ||
positions.push([1., positions[i][1], positions[i][2]]); | ||
normals.push(normals[i]); | ||
uvs.push([1., uvs[i][1]]); | ||
} | ||
let indices = Indices::U16( | ||
(0..8) | ||
.step_by(2) | ||
.flat_map(|i| [i, i + 1, i + 8, i + 1, i + 9, i + 8]) | ||
.collect(), | ||
); | ||
|
||
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); | ||
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); | ||
mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); | ||
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); | ||
mesh.set_indices(Some(indices)); | ||
mesh | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters