cannon components and systems
This commit is contained in:
parent
71f6b549b7
commit
30412844b8
5 changed files with 253 additions and 2 deletions
46
src/states/linear/components_cannon.rs
Normal file
46
src/states/linear/components_cannon.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
use crate::states::linear::RoundBallType;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct LinearCannon;
|
||||||
|
|
||||||
|
#[derive(Resource, Debug, Clone)]
|
||||||
|
pub struct LinearCannonState {
|
||||||
|
pub shot: RoundBallType,
|
||||||
|
pub swap: RoundBallType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LinearCannonState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
shot: RoundBallType::random_from_4(),
|
||||||
|
swap: RoundBallType::random_from_4(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinearCannonState {
|
||||||
|
pub fn fire(&mut self) {
|
||||||
|
self.shot = self.swap;
|
||||||
|
self.swap = RoundBallType::random_from_4();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cycle(&mut self) {
|
||||||
|
mem::swap(&mut self.shot, &mut self.swap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct RoundBallProjectile {
|
||||||
|
pub velocity: Vec2,
|
||||||
|
pub previous_position: Vec2,
|
||||||
|
pub ball_type: RoundBallType,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct ShotMarker;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct SwapMarker;
|
||||||
|
|
@ -11,6 +11,9 @@ pub const SCALE: f32 = 2.0;
|
||||||
|
|
||||||
// Z-INDEXES
|
// Z-INDEXES
|
||||||
pub const ROUND_BALL_Z: f32 = 10.0;
|
pub const ROUND_BALL_Z: f32 = 10.0;
|
||||||
|
pub const ROUND_SHOT_Z: f32 = 11.0;
|
||||||
|
pub const LINEAR_CANNON_Z: f32 = 20.0;
|
||||||
|
pub const LINEAR_CANNON_BALL_Z: f32 = 15.0;
|
||||||
|
|
||||||
// COLORS
|
// COLORS
|
||||||
pub const PINK: Color = Color::srgb_u8(250, 0, 155);
|
pub const PINK: Color = Color::srgb_u8(250, 0, 155);
|
||||||
|
|
|
||||||
|
|
@ -21,5 +21,10 @@ pub use constants::*;
|
||||||
|
|
||||||
mod ui;
|
mod ui;
|
||||||
pub use ui::*;
|
pub use ui::*;
|
||||||
|
mod components_cannon;
|
||||||
|
pub use components_cannon::*;
|
||||||
|
mod systems_cannon;
|
||||||
|
pub use systems_cannon::*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ pub enum LinearUpdateSet {
|
||||||
impl Plugin for LinearPlayPlugin {
|
impl Plugin for LinearPlayPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app
|
app
|
||||||
.add_systems(OnEnter(LinearPlayState), setup)
|
.add_systems(OnEnter(LinearPlayState), (setup, spawn_linear_cannon).chain())
|
||||||
.add_systems(OnEnter(LinearGameRestart), linear_restart)
|
.add_systems(OnEnter(LinearGameRestart), linear_restart)
|
||||||
.add_plugins(LinearUIPlugin)
|
.add_plugins(LinearUIPlugin)
|
||||||
.configure_sets(
|
.configure_sets(
|
||||||
|
|
@ -26,8 +26,14 @@ impl Plugin for LinearPlayPlugin {
|
||||||
(
|
(
|
||||||
draw_track_gizmos,
|
draw_track_gizmos,
|
||||||
draw_grid,
|
draw_grid,
|
||||||
|
//несколько систем с соблюдением порядка
|
||||||
|
linear_move_projectiles,
|
||||||
|
cycle_cannon_balls,
|
||||||
|
update_linear_cannon_preview,
|
||||||
|
spawn_projectile_from_cannon,
|
||||||
spawn_round_ball,
|
spawn_round_ball,
|
||||||
move_round_balls,
|
move_round_balls,
|
||||||
|
rotate_linear_cannon,
|
||||||
)
|
)
|
||||||
.in_set(LinearUpdateSet::Track),
|
.in_set(LinearUpdateSet::Track),
|
||||||
)
|
)
|
||||||
|
|
@ -41,6 +47,7 @@ fn setup(mut commands: Commands) {
|
||||||
|
|
||||||
commands.insert_resource(build_track);
|
commands.insert_resource(build_track);
|
||||||
commands.insert_resource(precalculated_track);
|
commands.insert_resource(precalculated_track);
|
||||||
|
commands.insert_resource(build_linear_cannon_state());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cleanup(mut commands: Commands, query: Query<Entity, With<LinearStateMarker>>) {
|
fn cleanup(mut commands: Commands, query: Query<Entity, With<LinearStateMarker>>) {
|
||||||
|
|
@ -50,8 +57,9 @@ fn cleanup(mut commands: Commands, query: Query<Entity, With<LinearStateMarker>>
|
||||||
}
|
}
|
||||||
|
|
||||||
// зачищаем ресурсы по типу
|
// зачищаем ресурсы по типу
|
||||||
commands.remove_resource::<Track>();
|
commands.remove_resource::<Track>(); //зачистить сразу после калькуляции
|
||||||
commands.remove_resource::<PrecalculatedTrack>();
|
commands.remove_resource::<PrecalculatedTrack>();
|
||||||
|
commands.remove_resource::<LinearCannonState>();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn linear_restart(mut next_state: ResMut<NextState<AppState>>) {
|
fn linear_restart(mut next_state: ResMut<NextState<AppState>>) {
|
||||||
|
|
|
||||||
189
src/states/linear/systems_cannon.rs
Normal file
189
src/states/linear/systems_cannon.rs
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
use crate::states::linear::*;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::window::PrimaryWindow;
|
||||||
|
use std::f32::consts::FRAC_PI_2;
|
||||||
|
use bevy::asset::ErasedAssetLoader;
|
||||||
|
use crate::{FACTOR, HEIGHT, WIDTH};
|
||||||
|
use crate::states::level::{BallProjectile, BallType, Cannon, CannonState, CurrentPreviewMarker, NextPreviewMarker, CURRENT_SHOT_Z_INDEX, NEXT_SHOT_Z_INDEX, SLOT_SIZE};
|
||||||
|
|
||||||
|
pub fn spawn_linear_cannon(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
let image: Handle<Image> = asset_server.load("sprites/cannon/cannon.png");
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
Sprite {
|
||||||
|
image,
|
||||||
|
custom_size: Some(Vec2::new(STEP * 2.5, STEP * 4.0)),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Transform::from_xyz(6.0 * STEP, 3.0 * STEP, LINEAR_CANNON_Z),
|
||||||
|
LinearStateMarker,
|
||||||
|
LinearCannon,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotate_linear_cannon(
|
||||||
|
mut query: Query<&mut Transform, With<LinearCannon>>,
|
||||||
|
window_query: Query<&Window, With<PrimaryWindow>>,
|
||||||
|
camera_query: Query<(&Camera, &GlobalTransform)>,
|
||||||
|
) {
|
||||||
|
let Ok(mut cannon_tf) = query.single_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(window) = window_query.single() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(cursor_pos) = window.cursor_position() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok((camera, camera_tf)) = camera_query.single() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(world_cursor_pos) = camera.viewport_to_world_2d(camera_tf, cursor_pos) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let cannon_pos = Vec2::new(cannon_tf.translation.x, cannon_tf.translation.y);
|
||||||
|
let direction = world_cursor_pos - cannon_pos;
|
||||||
|
|
||||||
|
let angle = direction.to_angle() - FRAC_PI_2;
|
||||||
|
cannon_tf.rotation = Quat::from_rotation_z(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_projectile_from_cannon(
|
||||||
|
mut commands: Commands,
|
||||||
|
cannon_query: Query<&Transform, With<LinearCannon>>,
|
||||||
|
mouse_input: Res<ButtonInput<MouseButton>>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
mut cannon_state: ResMut<LinearCannonState>
|
||||||
|
) {
|
||||||
|
if !mouse_input.just_pressed(MouseButton::Left) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Ok(cannon_tf) = cannon_query.single() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let ball_type = cannon_state.shot;
|
||||||
|
let image: Handle<Image> = asset_server.load(ball_type.asset_path());
|
||||||
|
|
||||||
|
let offset = 100.0; // сдвиг от центра к дулу пушки
|
||||||
|
let direction = cannon_tf.rotation.mul_vec3(Vec3::Y).truncate().normalize();
|
||||||
|
let spawn_pos_2d = cannon_tf.translation.truncate() + direction * offset;
|
||||||
|
let spawn_pos = spawn_pos_2d.extend(ROUND_SHOT_Z);
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
Sprite {
|
||||||
|
image,
|
||||||
|
custom_size: Some(Vec2::splat(STEP)),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Transform::from_translation(spawn_pos),
|
||||||
|
RoundBallProjectile {
|
||||||
|
velocity: direction * 800.0,
|
||||||
|
previous_position: spawn_pos_2d,
|
||||||
|
ball_type,
|
||||||
|
},
|
||||||
|
LinearStateMarker,
|
||||||
|
));
|
||||||
|
cannon_state.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_linear_cannon_state() -> LinearCannonState {
|
||||||
|
LinearCannonState {
|
||||||
|
shot: RoundBallType::random_from_4(),
|
||||||
|
swap: RoundBallType::random_from_4(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_linear_cannon_preview(
|
||||||
|
mut commands: Commands,
|
||||||
|
cannon_state: Res<LinearCannonState>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
cannon_query: Query<Entity, With<LinearCannon>>,
|
||||||
|
shot_query: Query<Entity, With<ShotMarker>>,
|
||||||
|
swap_query: Query<Entity, With<SwapMarker>>,
|
||||||
|
) {
|
||||||
|
let Ok(cannon_entity) = cannon_query.single() else { return };
|
||||||
|
|
||||||
|
let shot_image: Handle<Image> = asset_server.load(cannon_state.shot.asset_path());
|
||||||
|
|
||||||
|
if let Ok(shot_entity) = shot_query.single() {
|
||||||
|
// усли шарик у дула есть, то обновляем спрайт
|
||||||
|
commands.entity(shot_entity).insert(
|
||||||
|
Sprite{
|
||||||
|
image: shot_image,
|
||||||
|
custom_size: Some(Vec2::splat(STEP)),
|
||||||
|
..default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
//иначе создаем
|
||||||
|
let preview_entity = commands.spawn((
|
||||||
|
Sprite{
|
||||||
|
image: shot_image,
|
||||||
|
custom_size: Some(Vec2::splat(STEP)),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Transform::from_xyz(0.0, STEP * 2.2, LINEAR_CANNON_BALL_Z),
|
||||||
|
ShotMarker
|
||||||
|
)).id();
|
||||||
|
commands.entity(cannon_entity).add_child(preview_entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// следующий шарик в хвосте
|
||||||
|
let swap_image = asset_server.load(cannon_state.swap.asset_path());
|
||||||
|
|
||||||
|
if let Ok(swap_entity) = swap_query.single() {
|
||||||
|
commands.entity(swap_entity).insert(Sprite{
|
||||||
|
image: swap_image,
|
||||||
|
custom_size: Some(Vec2::splat(STEP)),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let preview_entity = commands.spawn((
|
||||||
|
Sprite{
|
||||||
|
image: swap_image,
|
||||||
|
custom_size: Some(Vec2::splat(STEP)),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Transform::from_xyz(0.0, -STEP * 1.5, LINEAR_CANNON_BALL_Z),
|
||||||
|
SwapMarker,
|
||||||
|
)).id();
|
||||||
|
commands.entity(cannon_entity).add_child(preview_entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cycle_cannon_balls(
|
||||||
|
mut cannon_state: ResMut<LinearCannonState>,
|
||||||
|
mouse_input: Res<ButtonInput<MouseButton>>,
|
||||||
|
) {
|
||||||
|
if mouse_input.just_pressed(MouseButton::Right) {
|
||||||
|
cannon_state.cycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn linear_move_projectiles(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut projectiles: Query<(Entity, &mut RoundBallProjectile, &mut Transform)>,
|
||||||
|
time: Res<Time>,
|
||||||
|
) {
|
||||||
|
for (entity, mut proj, mut transform) in projectiles.iter_mut() {
|
||||||
|
let delta = proj.velocity * time.delta_secs();
|
||||||
|
let new_pos = proj.previous_position + delta;
|
||||||
|
|
||||||
|
transform.translation = new_pos.extend(transform.translation.z);
|
||||||
|
proj.previous_position = new_pos;
|
||||||
|
|
||||||
|
// удаляем снаряды, улетевшие за экран
|
||||||
|
if new_pos.x.abs() > 1.5 * (WIDTH * FACTOR) as f32 || new_pos.y.abs() > 1.5 * (HEIGHT * FACTOR) as f32 {
|
||||||
|
commands.entity(entity).despawn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn detect_track_hit(){} //TODO
|
||||||
Loading…
Add table
Add a link
Reference in a new issue