Wiring up animations (#47)
This commit is contained in:
@@ -1,9 +1,8 @@
|
|||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState, global_observer, heads::ActiveHeads, heads_database::HeadsDatabase,
|
GameState, global_observer, heads::ActiveHeads, heads_database::HeadsDatabase,
|
||||||
hitpoints::Hitpoints, loading_assets::AudioAssets,
|
hitpoints::Hitpoints, loading_assets::AudioAssets,
|
||||||
};
|
};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Healing(pub Entity);
|
pub struct Healing(pub Entity);
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ mod healing;
|
|||||||
mod missile;
|
mod missile;
|
||||||
mod thrown;
|
mod thrown;
|
||||||
|
|
||||||
pub use healing::Healing;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState,
|
GameState,
|
||||||
aim::AimTarget,
|
aim::AimTarget,
|
||||||
@@ -19,6 +17,7 @@ use crate::{
|
|||||||
sounds::PlaySound,
|
sounds::PlaySound,
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
pub use healing::Healing;
|
||||||
use healing::HealingStateChanged;
|
use healing::HealingStateChanged;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState,
|
GameState,
|
||||||
abilities::{HeadAbility, TriggerData, TriggerThrow},
|
abilities::{HeadAbility, TriggerData, TriggerThrow},
|
||||||
@@ -8,6 +6,7 @@ use crate::{
|
|||||||
heads_database::HeadsDatabase,
|
heads_database::HeadsDatabase,
|
||||||
player::Player,
|
player::Player,
|
||||||
};
|
};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
|
|||||||
127
src/animation.rs
Normal file
127
src/animation.rs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
use crate::{
|
||||||
|
GameState, character::CharacterAnimations, head::ActiveHead, heads_database::HeadsDatabase,
|
||||||
|
};
|
||||||
|
use bevy::{animation::RepeatAnimation, ecs::query::QueryData, prelude::*};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.register_type::<AnimationFlags>();
|
||||||
|
|
||||||
|
app.add_systems(
|
||||||
|
Update,
|
||||||
|
update_animation.run_if(in_state(GameState::Playing)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Default, Reflect)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct AnimationFlags {
|
||||||
|
pub any_direction: bool,
|
||||||
|
pub jumping: bool,
|
||||||
|
pub just_jumped: bool,
|
||||||
|
pub shooting: bool,
|
||||||
|
pub hit: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(QueryData)]
|
||||||
|
#[query_data(mutable)]
|
||||||
|
pub struct AnimationController {
|
||||||
|
pub transitions: &'static mut AnimationTransitions,
|
||||||
|
pub player: &'static mut AnimationPlayer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimationController {
|
||||||
|
pub fn play_inner(
|
||||||
|
player: &mut AnimationPlayer,
|
||||||
|
transitions: &mut AnimationTransitions,
|
||||||
|
animation: AnimationNodeIndex,
|
||||||
|
transition: Duration,
|
||||||
|
repeat: RepeatAnimation,
|
||||||
|
) {
|
||||||
|
transitions
|
||||||
|
.play(player, animation, transition)
|
||||||
|
.set_repeat(repeat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimationControllerItem<'_> {
|
||||||
|
pub fn play(
|
||||||
|
&mut self,
|
||||||
|
animation: AnimationNodeIndex,
|
||||||
|
transition: Duration,
|
||||||
|
repeat: RepeatAnimation,
|
||||||
|
) {
|
||||||
|
AnimationController::play_inner(
|
||||||
|
&mut self.player,
|
||||||
|
&mut self.transitions,
|
||||||
|
animation,
|
||||||
|
transition,
|
||||||
|
repeat,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_playing(&self, index: AnimationNodeIndex) -> bool {
|
||||||
|
self.player.is_playing_animation(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_TRANSITION_DURATION: Duration = Duration::from_millis(100);
|
||||||
|
|
||||||
|
fn update_animation(
|
||||||
|
mut animated: Query<(
|
||||||
|
AnimationController,
|
||||||
|
&CharacterAnimations,
|
||||||
|
&mut AnimationFlags,
|
||||||
|
)>,
|
||||||
|
) {
|
||||||
|
for (mut controller, anims, mut flags) in animated.iter_mut() {
|
||||||
|
if flags.shooting && flags.any_direction && anims.run_shoot.is_some() {
|
||||||
|
if !controller.is_playing(anims.run_shoot.unwrap()) {
|
||||||
|
controller.play(
|
||||||
|
anims.run_shoot.unwrap(),
|
||||||
|
DEFAULT_TRANSITION_DURATION,
|
||||||
|
RepeatAnimation::Forever,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if flags.shooting && anims.shoot.is_some() {
|
||||||
|
if !controller.is_playing(anims.shoot.unwrap()) {
|
||||||
|
controller.play(
|
||||||
|
anims.shoot.unwrap(),
|
||||||
|
DEFAULT_TRANSITION_DURATION,
|
||||||
|
RepeatAnimation::Forever,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if flags.hit {
|
||||||
|
if !controller.is_playing(anims.hit) {
|
||||||
|
controller.play(
|
||||||
|
anims.hit,
|
||||||
|
DEFAULT_TRANSITION_DURATION,
|
||||||
|
RepeatAnimation::Never,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if flags.jumping {
|
||||||
|
if !controller.is_playing(anims.jump) || flags.just_jumped {
|
||||||
|
controller.play(
|
||||||
|
anims.jump,
|
||||||
|
DEFAULT_TRANSITION_DURATION,
|
||||||
|
RepeatAnimation::Never,
|
||||||
|
);
|
||||||
|
flags.just_jumped = false;
|
||||||
|
}
|
||||||
|
} else if flags.any_direction {
|
||||||
|
if !controller.player.is_playing_animation(anims.run) {
|
||||||
|
controller.play(
|
||||||
|
anims.run,
|
||||||
|
DEFAULT_TRANSITION_DURATION,
|
||||||
|
RepeatAnimation::Forever,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if !controller.is_playing(anims.idle) {
|
||||||
|
controller.play(
|
||||||
|
anims.idle,
|
||||||
|
DEFAULT_TRANSITION_DURATION,
|
||||||
|
RepeatAnimation::Forever,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,9 +5,8 @@ use crate::{
|
|||||||
cash::CashCollectEvent, global_observer, head_drop::HeadCollected, heads::HeadState,
|
cash::CashCollectEvent, global_observer, head_drop::HeadCollected, heads::HeadState,
|
||||||
heads_database::HeadsDatabase,
|
heads_database::HeadsDatabase,
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
pub use backpack_ui::BackpackAction;
|
pub use backpack_ui::BackpackAction;
|
||||||
|
use bevy::prelude::*;
|
||||||
pub use ui_head_state::UiHeadState;
|
pub use ui_head_state::UiHeadState;
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use avian3d::prelude::*;
|
|
||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState, control::ControlState, loading_assets::UIAssets, physics_layers::GameLayer,
|
GameState, control::ControlState, loading_assets::UIAssets, physics_layers::GameLayer,
|
||||||
};
|
};
|
||||||
|
use avian3d::prelude::*;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
#[derive(Component, Reflect, Debug)]
|
#[derive(Component, Reflect, Debug)]
|
||||||
pub struct CameraTarget;
|
pub struct CameraTarget;
|
||||||
|
|||||||
100
src/character.rs
100
src/character.rs
@@ -1,8 +1,14 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
GameState, heads_database::HeadsDatabase, loading_assets::GameAssets, utils::trail::Trail,
|
GameState,
|
||||||
|
animation::{AnimationController, AnimationFlags},
|
||||||
|
heads_database::HeadsDatabase,
|
||||||
|
hitpoints::Hitpoints,
|
||||||
|
loading_assets::GameAssets,
|
||||||
|
utils::trail::Trail,
|
||||||
};
|
};
|
||||||
use bevy::{
|
use bevy::{
|
||||||
ecs::system::SystemParam, platform::collections::HashMap, prelude::*, scene::SceneInstanceReady,
|
animation::RepeatAnimation, ecs::system::SystemParam, platform::collections::HashMap,
|
||||||
|
prelude::*, scene::SceneInstanceReady,
|
||||||
};
|
};
|
||||||
use std::{f32::consts::PI, time::Duration};
|
use std::{f32::consts::PI, time::Duration};
|
||||||
|
|
||||||
@@ -39,14 +45,31 @@ impl CharacterHierarchy<'_, '_> {
|
|||||||
|
|
||||||
#[derive(Component, Debug, Reflect)]
|
#[derive(Component, Debug, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
|
#[relationship(relationship_target = HasCharacterAnimations)]
|
||||||
|
#[require(AnimationFlags)]
|
||||||
pub struct CharacterAnimations {
|
pub struct CharacterAnimations {
|
||||||
|
#[relationship]
|
||||||
|
pub of_character: Entity,
|
||||||
pub idle: AnimationNodeIndex,
|
pub idle: AnimationNodeIndex,
|
||||||
pub run: AnimationNodeIndex,
|
pub run: AnimationNodeIndex,
|
||||||
pub jump: AnimationNodeIndex,
|
pub jump: AnimationNodeIndex,
|
||||||
pub shooting: Option<AnimationNodeIndex>,
|
pub shoot: Option<AnimationNodeIndex>,
|
||||||
|
pub run_shoot: Option<AnimationNodeIndex>,
|
||||||
|
pub hit: AnimationNodeIndex,
|
||||||
pub graph: Handle<AnimationGraph>,
|
pub graph: Handle<AnimationGraph>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ANIM_IDLE: &str = "idle";
|
||||||
|
const ANIM_RUN: &str = "run";
|
||||||
|
const ANIM_JUMP: &str = "jump";
|
||||||
|
const ANIM_SHOOT: &str = "shoot";
|
||||||
|
const ANIM_RUN_SHOOT: &str = "run_shoot";
|
||||||
|
const ANIM_HIT: &str = "hit";
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[relationship_target(relationship = CharacterAnimations)]
|
||||||
|
pub struct HasCharacterAnimations(Entity);
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
@@ -142,18 +165,25 @@ fn setup_once_loaded(
|
|||||||
mut query: Query<(Entity, &mut AnimationPlayer), Added<AnimationPlayer>>,
|
mut query: Query<(Entity, &mut AnimationPlayer), Added<AnimationPlayer>>,
|
||||||
parent: Query<&ChildOf>,
|
parent: Query<&ChildOf>,
|
||||||
animated_character: Query<(&AnimatedCharacter, &AnimatedCharacterAsset)>,
|
animated_character: Query<(&AnimatedCharacter, &AnimatedCharacterAsset)>,
|
||||||
|
characters: Query<Entity, With<Hitpoints>>,
|
||||||
gltf_assets: Res<Assets<Gltf>>,
|
gltf_assets: Res<Assets<Gltf>>,
|
||||||
mut graphs: ResMut<Assets<AnimationGraph>>,
|
mut graphs: ResMut<Assets<AnimationGraph>>,
|
||||||
) {
|
) {
|
||||||
for (entity, mut player) in &mut query {
|
for (entity, mut player) in query.iter_mut() {
|
||||||
let Some((_character, asset)) = parent
|
let Some((_, asset)) = parent
|
||||||
.iter_ancestors(entity)
|
.iter_ancestors(entity)
|
||||||
.find_map(|ancestor| animated_character.get(ancestor).ok())
|
.find_map(|ancestor| animated_character.get(ancestor).ok())
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let Some(character) = parent
|
||||||
|
.iter_ancestors(entity)
|
||||||
|
.find_map(|ancestor| characters.get(ancestor).ok())
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
let asset = gltf_assets.get(asset.0.id()).unwrap();
|
let asset = gltf_assets.get(asset.0.id()).unwrap();
|
||||||
|
|
||||||
let animations = asset
|
let animations = asset
|
||||||
@@ -162,41 +192,45 @@ fn setup_once_loaded(
|
|||||||
.map(|(name, animation)| (name.to_string(), animation.clone()))
|
.map(|(name, animation)| (name.to_string(), animation.clone()))
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
let mut clips = vec![
|
let mut graph = AnimationGraph::new();
|
||||||
animations["idle"].clone(),
|
let root = graph.root;
|
||||||
animations["run"].clone(),
|
let idle = graph.add_clip(animations[ANIM_IDLE].clone(), 1.0, root);
|
||||||
animations["jump"].clone(),
|
let run = graph.add_clip(animations[ANIM_RUN].clone(), 1.0, root);
|
||||||
];
|
let jump = graph.add_clip(animations[ANIM_JUMP].clone(), 1.0, root);
|
||||||
|
let shoot = animations
|
||||||
if let Some(shooting_animation) = animations.get("shoot") {
|
.get(ANIM_SHOOT)
|
||||||
clips.push(shooting_animation.clone());
|
.map(|it| graph.add_clip(it.clone(), 1.0, root));
|
||||||
}
|
let run_shoot = animations
|
||||||
|
.get(ANIM_RUN_SHOOT)
|
||||||
let (graph, node_indices) = AnimationGraph::from_clips(clips);
|
.map(|it| graph.add_clip(it.clone(), 1.0, root));
|
||||||
|
let hit = graph.add_clip(animations[ANIM_HIT].clone(), 1.0, root);
|
||||||
|
|
||||||
// Insert a resource with the current scene information
|
// Insert a resource with the current scene information
|
||||||
let graph_handle = graphs.add(graph);
|
let graph_handle = graphs.add(graph);
|
||||||
let animations = CharacterAnimations {
|
let animations = CharacterAnimations {
|
||||||
idle: node_indices[0],
|
of_character: character,
|
||||||
run: node_indices[1],
|
idle,
|
||||||
jump: node_indices[2],
|
run,
|
||||||
shooting: if node_indices.len() == 4 {
|
jump,
|
||||||
Some(node_indices[3])
|
shoot,
|
||||||
} else {
|
run_shoot,
|
||||||
None
|
hit,
|
||||||
},
|
|
||||||
graph: graph_handle.clone(),
|
graph: graph_handle.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut transitions = AnimationTransitions::new();
|
let mut transitions = AnimationTransitions::new();
|
||||||
transitions
|
AnimationController::play_inner(
|
||||||
.play(&mut player, animations.idle, Duration::ZERO)
|
&mut player,
|
||||||
.repeat();
|
&mut transitions,
|
||||||
commands
|
animations.idle,
|
||||||
.entity(entity)
|
Duration::ZERO,
|
||||||
.insert(AnimationGraphHandle(animations.graph.clone()))
|
RepeatAnimation::Forever,
|
||||||
.insert(transitions)
|
);
|
||||||
.insert(animations);
|
commands.entity(entity).insert((
|
||||||
|
AnimationGraphHandle(animations.graph.clone()),
|
||||||
|
transitions,
|
||||||
|
animations,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,26 @@
|
|||||||
|
use super::{ControllerSet, ControllerSwitchEvent};
|
||||||
|
use crate::{
|
||||||
|
GameState,
|
||||||
|
control::{SelectedController, controls::ControllerSettings},
|
||||||
|
heads_database::HeadControls,
|
||||||
|
player::PlayerBodyMesh,
|
||||||
|
};
|
||||||
use avian3d::{math::*, prelude::*};
|
use avian3d::{math::*, prelude::*};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use happy_feet::KinematicVelocity;
|
use happy_feet::{
|
||||||
use happy_feet::ground::{Grounding, GroundingConfig};
|
KinematicVelocity,
|
||||||
use happy_feet::prelude::{
|
ground::{Grounding, GroundingConfig},
|
||||||
|
prelude::{
|
||||||
Character, CharacterDrag, CharacterFriction, CharacterGravity, CharacterMovement,
|
Character, CharacterDrag, CharacterFriction, CharacterGravity, CharacterMovement,
|
||||||
CharacterPlugin, MoveInput, SteppingBehaviour, SteppingConfig,
|
CharacterPlugin, MoveInput, SteppingBehaviour, SteppingConfig,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::GameState;
|
|
||||||
use crate::control::SelectedController;
|
|
||||||
use crate::control::controls::ControllerSettings;
|
|
||||||
use crate::heads_database::HeadControls;
|
|
||||||
use crate::player::PlayerBodyMesh;
|
|
||||||
|
|
||||||
use super::{ControllerSet, ControllerSwitchEvent};
|
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_plugins(CharacterPlugin::default());
|
app.add_plugins(CharacterPlugin::default());
|
||||||
|
|
||||||
app.register_type::<MovementSpeedFactor>();
|
app.register_type::<MovementSpeedFactor>();
|
||||||
|
|
||||||
app.init_resource::<PlayerMovement>();
|
|
||||||
|
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
reset_upon_switch
|
reset_upon_switch
|
||||||
@@ -78,7 +77,7 @@ fn decelerate(
|
|||||||
&ControllerSettings,
|
&ControllerSettings,
|
||||||
)>,
|
)>,
|
||||||
) {
|
) {
|
||||||
for (mut velocity, input, grounding, settings) in &mut character {
|
for (mut velocity, input, grounding, settings) in character.iter_mut() {
|
||||||
let direction = input.value.normalize();
|
let direction = input.value.normalize();
|
||||||
let ground_normal = grounding
|
let ground_normal = grounding
|
||||||
.and_then(|it| it.normal())
|
.and_then(|it| it.normal())
|
||||||
@@ -105,12 +104,6 @@ fn decelerate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
|
||||||
pub struct PlayerMovement {
|
|
||||||
pub any_direction: bool,
|
|
||||||
pub shooting: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct MovementSpeedFactor(pub f32);
|
pub struct MovementSpeedFactor(pub f32);
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
use std::f32::consts::PI;
|
use super::{ControlState, ControllerSet};
|
||||||
|
use crate::{GameState, control::controller_common::MovementSpeedFactor, player::PlayerBodyMesh};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use happy_feet::prelude::MoveInput;
|
use happy_feet::prelude::MoveInput;
|
||||||
|
use std::f32::consts::PI;
|
||||||
use crate::GameState;
|
|
||||||
use crate::control::controller_common::MovementSpeedFactor;
|
|
||||||
use crate::player::PlayerBodyMesh;
|
|
||||||
|
|
||||||
use super::{ControlState, ControllerSet};
|
|
||||||
|
|
||||||
pub struct CharacterControllerPlugin;
|
pub struct CharacterControllerPlugin;
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
use super::{ControlState, ControllerSet, Controls};
|
use super::{ControlState, ControllerSet, Controls};
|
||||||
use crate::control::controller_common::MovementSpeedFactor;
|
use crate::{
|
||||||
use crate::control::controls::ControllerSettings;
|
GameState,
|
||||||
use crate::{GameState, abilities::TriggerStateRes, player::PlayerBodyMesh};
|
abilities::TriggerStateRes,
|
||||||
|
animation::AnimationFlags,
|
||||||
|
character::HasCharacterAnimations,
|
||||||
|
control::{controller_common::MovementSpeedFactor, controls::ControllerSettings},
|
||||||
|
player::{Player, PlayerBodyMesh},
|
||||||
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use happy_feet::prelude::{Grounding, KinematicVelocity, MoveInput};
|
use happy_feet::prelude::{Grounding, KinematicVelocity, MoveInput};
|
||||||
|
|
||||||
use super::controller_common::PlayerMovement;
|
|
||||||
|
|
||||||
pub struct CharacterControllerPlugin;
|
pub struct CharacterControllerPlugin;
|
||||||
|
|
||||||
impl Plugin for CharacterControllerPlugin {
|
impl Plugin for CharacterControllerPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
(set_movement_flag, rotate_view, apply_controls)
|
(set_animation_flags, rotate_view, apply_controls)
|
||||||
.chain()
|
.chain()
|
||||||
.in_set(ControllerSet::ApplyControlsRun)
|
.in_set(ControllerSet::ApplyControlsRun)
|
||||||
.run_if(in_state(GameState::Playing)),
|
.run_if(in_state(GameState::Playing)),
|
||||||
@@ -22,28 +25,38 @@ impl Plugin for CharacterControllerPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the movement flag, which is an indicator for the rig animation and the braking system.
|
/// Sets the movement flag, which is an indicator for the rig animation and the braking system.
|
||||||
fn set_movement_flag(
|
fn set_animation_flags(
|
||||||
mut player_movement: ResMut<PlayerMovement>,
|
mut flags: Query<&mut AnimationFlags>,
|
||||||
controls: Res<Controls>,
|
controls: Res<Controls>,
|
||||||
trigger: Res<TriggerStateRes>,
|
trigger: Res<TriggerStateRes>,
|
||||||
|
player: Single<(&Grounding, &HasCharacterAnimations), With<Player>>,
|
||||||
) {
|
) {
|
||||||
let mut direction = controls.keyboard_state.move_dir;
|
let mut direction = controls.keyboard_state.move_dir;
|
||||||
let deadzone = 0.2;
|
let deadzone = 0.2;
|
||||||
|
|
||||||
|
let (grounding, has_anims) = *player;
|
||||||
|
let mut flags = flags.get_mut(*has_anims.collection()).unwrap();
|
||||||
|
|
||||||
if let Some(gamepad) = controls.gamepad_state {
|
if let Some(gamepad) = controls.gamepad_state {
|
||||||
direction += gamepad.move_dir;
|
direction += gamepad.move_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
if player_movement.any_direction {
|
if flags.any_direction {
|
||||||
if direction.length_squared() < deadzone {
|
if direction.length_squared() < deadzone {
|
||||||
player_movement.any_direction = false;
|
flags.any_direction = false;
|
||||||
}
|
}
|
||||||
} else if direction.length_squared() > deadzone {
|
} else if direction.length_squared() > deadzone {
|
||||||
player_movement.any_direction = true;
|
flags.any_direction = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if player_movement.shooting != trigger.is_active() {
|
if flags.shooting != trigger.is_active() {
|
||||||
player_movement.shooting = trigger.is_active();
|
flags.shooting = trigger.is_active();
|
||||||
|
}
|
||||||
|
|
||||||
|
// `apply_controls` sets the jump flag when the player actually jumps.
|
||||||
|
// Unset the flag on hitting the ground
|
||||||
|
if grounding.is_grounded() {
|
||||||
|
flags.jumping = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +68,7 @@ fn rotate_view(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for mut tr in &mut player {
|
for mut tr in player.iter_mut() {
|
||||||
tr.rotate_y(controls.look_dir.x * -0.001);
|
tr.rotate_y(controls.look_dir.x * -0.001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,15 +81,19 @@ fn apply_controls(
|
|||||||
&mut KinematicVelocity,
|
&mut KinematicVelocity,
|
||||||
&ControllerSettings,
|
&ControllerSettings,
|
||||||
&MovementSpeedFactor,
|
&MovementSpeedFactor,
|
||||||
|
&HasCharacterAnimations,
|
||||||
)>,
|
)>,
|
||||||
rig_transform_q: Option<Single<&GlobalTransform, With<PlayerBodyMesh>>>,
|
rig_transform_q: Option<Single<&GlobalTransform, With<PlayerBodyMesh>>>,
|
||||||
|
mut anim_flags: Query<&mut AnimationFlags>,
|
||||||
) {
|
) {
|
||||||
let Ok((mut move_input, mut grounding, mut velocity, settings, move_factor)) =
|
let Ok((mut move_input, mut grounding, mut velocity, settings, move_factor, has_anims)) =
|
||||||
character.single_mut()
|
character.single_mut()
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut flags = anim_flags.get_mut(*has_anims.collection()).unwrap();
|
||||||
|
|
||||||
let mut direction = -controls.move_dir.extend(0.0).xzy();
|
let mut direction = -controls.move_dir.extend(0.0).xzy();
|
||||||
|
|
||||||
if let Some(ref rig_transform) = rig_transform_q {
|
if let Some(ref rig_transform) = rig_transform_q {
|
||||||
@@ -92,6 +109,8 @@ fn apply_controls(
|
|||||||
move_input.set(direction * move_factor.0);
|
move_input.set(direction * move_factor.0);
|
||||||
|
|
||||||
if controls.jump && grounding.is_grounded() {
|
if controls.jump && grounding.is_grounded() {
|
||||||
|
flags.jumping = true;
|
||||||
|
flags.just_jumped = true;
|
||||||
happy_feet::movement::jump(settings.jump_force, &mut velocity, &mut grounding, Dir3::Y)
|
happy_feet::movement::jump(settings.jump_force, &mut velocity, &mut grounding, Dir3::Y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use crate::control::ControllerSet;
|
use super::{ControlState, Controls};
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState,
|
GameState,
|
||||||
abilities::{TriggerCashHeal, TriggerState},
|
abilities::{TriggerCashHeal, TriggerState},
|
||||||
backpack::BackpackAction,
|
backpack::BackpackAction,
|
||||||
|
control::ControllerSet,
|
||||||
heads::SelectActiveHead,
|
heads::SelectActiveHead,
|
||||||
};
|
};
|
||||||
use bevy::{
|
use bevy::{
|
||||||
@@ -14,8 +15,6 @@ use bevy::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{ControlState, Controls};
|
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.init_resource::<Controls>();
|
app.init_resource::<Controls>();
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState,
|
GameState,
|
||||||
head::ActiveHead,
|
head::ActiveHead,
|
||||||
heads_database::{HeadControls, HeadsDatabase},
|
heads_database::{HeadControls, HeadsDatabase},
|
||||||
};
|
};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
pub mod controller_common;
|
pub mod controller_common;
|
||||||
pub mod controller_flying;
|
pub mod controller_flying;
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
|
use super::{ActiveHeads, HEAD_SLOTS, HeadsImages};
|
||||||
use crate::{GameState, backpack::UiHeadState, loading_assets::UIAssets, player::Player};
|
use crate::{GameState, backpack::UiHeadState, loading_assets::UIAssets, player::Player};
|
||||||
use bevy::{ecs::spawn::SpawnIter, prelude::*};
|
use bevy::{ecs::spawn::SpawnIter, prelude::*};
|
||||||
use bevy_ui_gradients::{AngularColorStop, BackgroundGradient, ConicGradient, Gradient, Position};
|
use bevy_ui_gradients::{AngularColorStop, BackgroundGradient, ConicGradient, Gradient, Position};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
use super::{ActiveHeads, HEAD_SLOTS, HeadsImages};
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default)]
|
#[derive(Component, Reflect, Default)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
struct HeadSelector(pub usize);
|
struct HeadSelector(pub usize);
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, heads: Res<Head
|
|||||||
|
|
||||||
fn sync_hp(mut query: Query<(&mut ActiveHeads, &Hitpoints)>) {
|
fn sync_hp(mut query: Query<(&mut ActiveHeads, &Hitpoints)>) {
|
||||||
for (mut active_heads, hp) in query.iter_mut() {
|
for (mut active_heads, hp) in query.iter_mut() {
|
||||||
if active_heads.hp() != *hp {
|
if active_heads.hp().get() != hp.get() {
|
||||||
active_heads.set_hitpoint(hp);
|
active_heads.set_hitpoint(hp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
|
use crate::{
|
||||||
|
GameState,
|
||||||
|
animation::AnimationFlags,
|
||||||
|
character::{CharacterAnimations, HasCharacterAnimations},
|
||||||
|
sounds::PlaySound,
|
||||||
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::sounds::PlaySound;
|
|
||||||
|
|
||||||
#[derive(Event, Reflect)]
|
#[derive(Event, Reflect)]
|
||||||
pub struct Kill;
|
pub struct Kill;
|
||||||
|
|
||||||
@@ -10,15 +14,20 @@ pub struct Hit {
|
|||||||
pub damage: u32,
|
pub damage: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Reflect, Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Component, Reflect, Debug, Clone, Copy)]
|
||||||
pub struct Hitpoints {
|
pub struct Hitpoints {
|
||||||
max: u32,
|
max: u32,
|
||||||
current: u32,
|
current: u32,
|
||||||
|
last_hit_timestamp: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hitpoints {
|
impl Hitpoints {
|
||||||
pub fn new(v: u32) -> Self {
|
pub fn new(v: u32) -> Self {
|
||||||
Self { max: v, current: v }
|
Self {
|
||||||
|
max: v,
|
||||||
|
current: v,
|
||||||
|
last_hit_timestamp: f32::NEG_INFINITY,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_health(mut self, v: u32) -> Self {
|
pub fn with_health(mut self, v: u32) -> Self {
|
||||||
@@ -45,10 +54,17 @@ impl Hitpoints {
|
|||||||
pub fn max(&self) -> bool {
|
pub fn max(&self) -> bool {
|
||||||
self.current == self.max
|
self.current == self.max
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn time_since_hit(&self, time: &Time) -> f32 {
|
||||||
|
time.elapsed_secs() - self.last_hit_timestamp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_systems(Update, on_hp_added);
|
app.add_systems(Update, on_hp_added).add_systems(
|
||||||
|
PreUpdate,
|
||||||
|
reset_hit_animation_flag.run_if(in_state(GameState::Playing)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_hp_added(mut commands: Commands, query: Query<Entity, Added<Hitpoints>>) {
|
fn on_hp_added(mut commands: Commands, query: Query<Entity, Added<Hitpoints>>) {
|
||||||
@@ -57,18 +73,52 @@ fn on_hp_added(mut commands: Commands, query: Query<Entity, Added<Hitpoints>>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_hit(trigger: Trigger<Hit>, mut commands: Commands, mut query: Query<&mut Hitpoints>) {
|
fn on_hit(
|
||||||
|
trigger: Trigger<Hit>,
|
||||||
|
mut commands: Commands,
|
||||||
|
mut query: Query<(&mut Hitpoints, Option<&HasCharacterAnimations>)>,
|
||||||
|
mut anim_flags: Query<&mut AnimationFlags>,
|
||||||
|
time: Res<Time>,
|
||||||
|
) {
|
||||||
let Hit { damage } = trigger.event();
|
let Hit { damage } = trigger.event();
|
||||||
|
|
||||||
let Ok(mut hp) = query.get_mut(trigger.target()) else {
|
let Ok((mut hp, has_anims)) = query.get_mut(trigger.target()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
commands.trigger(PlaySound::Hit);
|
commands.trigger(PlaySound::Hit);
|
||||||
|
|
||||||
|
if let Some(has_anims) = has_anims {
|
||||||
|
let mut flags = anim_flags.get_mut(*has_anims.collection()).unwrap();
|
||||||
|
flags.hit = true;
|
||||||
|
}
|
||||||
|
|
||||||
hp.current = hp.current.saturating_sub(*damage);
|
hp.current = hp.current.saturating_sub(*damage);
|
||||||
|
hp.last_hit_timestamp = time.elapsed_secs();
|
||||||
|
|
||||||
if hp.current == 0 {
|
if hp.current == 0 {
|
||||||
commands.trigger_targets(Kill, trigger.target());
|
commands.trigger_targets(Kill, trigger.target());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset_hit_animation_flag(
|
||||||
|
query: Query<(&Hitpoints, &HasCharacterAnimations)>,
|
||||||
|
mut animations: Query<(
|
||||||
|
&AnimationGraphHandle,
|
||||||
|
&CharacterAnimations,
|
||||||
|
&mut AnimationFlags,
|
||||||
|
)>,
|
||||||
|
graphs: Res<Assets<AnimationGraph>>,
|
||||||
|
clips: Res<Assets<AnimationClip>>,
|
||||||
|
time: Res<Time>,
|
||||||
|
) {
|
||||||
|
for (hp, anims) in query.iter() {
|
||||||
|
let (graph_handle, anims, mut flags) = animations.get_mut(*anims.collection()).unwrap();
|
||||||
|
let graph = graphs.get(graph_handle.id()).unwrap();
|
||||||
|
let hit_anim = match graph.get(anims.hit).unwrap().node_type {
|
||||||
|
AnimationNodeType::Clip(ref handle) => clips.get(handle.id()).unwrap(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
flags.hit = hp.time_since_hit(&time) < hit_anim.duration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
mod abilities;
|
mod abilities;
|
||||||
mod ai;
|
mod ai;
|
||||||
mod aim;
|
mod aim;
|
||||||
|
mod animation;
|
||||||
mod backpack;
|
mod backpack;
|
||||||
mod camera;
|
mod camera;
|
||||||
mod cash;
|
mod cash;
|
||||||
@@ -29,6 +30,7 @@ mod tb_entities;
|
|||||||
mod utils;
|
mod utils;
|
||||||
mod water;
|
mod water;
|
||||||
|
|
||||||
|
use crate::utils::{auto_rotate, explosions};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::{
|
use bevy::{
|
||||||
audio::{PlaybackMode, Volume},
|
audio::{PlaybackMode, Volume},
|
||||||
@@ -47,8 +49,6 @@ use loading_assets::AudioAssets;
|
|||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use utils::{billboards, sprite_3d_animation, squish_animation, trail};
|
use utils::{billboards, sprite_3d_animation, squish_animation, trail};
|
||||||
|
|
||||||
use crate::utils::{auto_rotate, explosions};
|
|
||||||
|
|
||||||
#[derive(Resource, Reflect, Debug)]
|
#[derive(Resource, Reflect, Debug)]
|
||||||
#[reflect(Resource)]
|
#[reflect(Resource)]
|
||||||
struct DebugVisuals {
|
struct DebugVisuals {
|
||||||
@@ -143,6 +143,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.add_plugins(ai::plugin);
|
app.add_plugins(ai::plugin);
|
||||||
|
app.add_plugins(animation::plugin);
|
||||||
app.add_plugins(character::plugin);
|
app.add_plugins(character::plugin);
|
||||||
app.add_plugins(cash::plugin);
|
app.add_plugins(cash::plugin);
|
||||||
app.add_plugins(player::plugin);
|
app.add_plugins(player::plugin);
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use crate::{
|
|||||||
GameState,
|
GameState,
|
||||||
camera::{CameraArmRotation, CameraTarget},
|
camera::{CameraArmRotation, CameraTarget},
|
||||||
cash::{Cash, CashCollectEvent},
|
cash::{Cash, CashCollectEvent},
|
||||||
character::{AnimatedCharacter, CharacterAnimations},
|
character::AnimatedCharacter,
|
||||||
control::controller_common::{CharacterControllerBundle, PlayerMovement},
|
control::controller_common::CharacterControllerBundle,
|
||||||
global_observer,
|
global_observer,
|
||||||
head::ActiveHead,
|
head::ActiveHead,
|
||||||
head_drop::HeadDrops,
|
head_drop::HeadDrops,
|
||||||
@@ -22,14 +22,10 @@ use bevy::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
window::{CursorGrabMode, PrimaryWindow},
|
window::{CursorGrabMode, PrimaryWindow},
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
#[derive(Component, Default)]
|
||||||
pub struct Player;
|
pub struct Player;
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
|
||||||
struct PlayerAnimations;
|
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
#[derive(Component, Default)]
|
||||||
#[require(Transform, Visibility)]
|
#[require(Transform, Visibility)]
|
||||||
pub struct PlayerBodyMesh;
|
pub struct PlayerBodyMesh;
|
||||||
@@ -41,7 +37,6 @@ pub fn plugin(app: &mut App) {
|
|||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
collect_cash,
|
collect_cash,
|
||||||
toggle_animation,
|
|
||||||
setup_animations_marker_for_player,
|
setup_animations_marker_for_player,
|
||||||
toggle_cursor_system.run_if(input_just_pressed(KeyCode::Escape)),
|
toggle_cursor_system.run_if(input_just_pressed(KeyCode::Escape)),
|
||||||
)
|
)
|
||||||
@@ -172,57 +167,19 @@ fn collect_cash(
|
|||||||
fn setup_animations_marker_for_player(
|
fn setup_animations_marker_for_player(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
animation_handles: Query<Entity, Added<AnimationGraphHandle>>,
|
animation_handles: Query<Entity, Added<AnimationGraphHandle>>,
|
||||||
children: Query<&ChildOf>,
|
child_of: Query<&ChildOf>,
|
||||||
player: Query<&PlayerBodyMesh>,
|
player_rig: Query<&ChildOf, With<PlayerBodyMesh>>,
|
||||||
) {
|
) {
|
||||||
for entity in animation_handles.iter() {
|
for animation_rig in animation_handles.iter() {
|
||||||
for ancestor in children.iter_ancestors(entity) {
|
for ancestor in child_of.iter_ancestors(animation_rig) {
|
||||||
if player.contains(ancestor) {
|
if let Ok(rig_child_of) = player_rig.get(ancestor) {
|
||||||
commands.entity(entity).insert(PlayerAnimations);
|
commands.entity(rig_child_of.parent());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_animation(
|
|
||||||
mut transitions: Query<
|
|
||||||
(
|
|
||||||
&mut AnimationTransitions,
|
|
||||||
&mut AnimationPlayer,
|
|
||||||
&CharacterAnimations,
|
|
||||||
),
|
|
||||||
With<PlayerAnimations>,
|
|
||||||
>,
|
|
||||||
movement: Res<PlayerMovement>,
|
|
||||||
) {
|
|
||||||
if movement.is_changed() {
|
|
||||||
for (mut transition, mut player, animations) in &mut transitions {
|
|
||||||
let index = if movement.any_direction {
|
|
||||||
animations.run
|
|
||||||
} else {
|
|
||||||
animations.idle
|
|
||||||
};
|
|
||||||
|
|
||||||
let (mut index, mut duration) = (index, Duration::from_millis(100));
|
|
||||||
|
|
||||||
if movement.shooting {
|
|
||||||
if let Some(shoot_anim) = animations.shooting {
|
|
||||||
(index, duration) = (shoot_anim, Duration::from_millis(10));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if transition
|
|
||||||
.get_main_animation()
|
|
||||||
.map(|playing| playing != index)
|
|
||||||
.unwrap_or_default()
|
|
||||||
{
|
|
||||||
transition.play(&mut player, index, duration).repeat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_update_head_mesh(
|
fn on_update_head_mesh(
|
||||||
trigger: Trigger<HeadChanged>,
|
trigger: Trigger<HeadChanged>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::f32::consts::PI;
|
use crate::{cash::Cash, loading_assets::GameAssets, physics_layers::GameLayer};
|
||||||
|
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::{
|
use bevy::{
|
||||||
ecs::{component::HookContext, world::DeferredWorld},
|
ecs::{component::HookContext, world::DeferredWorld},
|
||||||
@@ -8,10 +7,7 @@ use bevy::{
|
|||||||
};
|
};
|
||||||
use bevy_trenchbroom::prelude::*;
|
use bevy_trenchbroom::prelude::*;
|
||||||
use happy_feet::prelude::PhysicsMover;
|
use happy_feet::prelude::PhysicsMover;
|
||||||
|
use std::f32::consts::PI;
|
||||||
use crate::cash::Cash;
|
|
||||||
use crate::loading_assets::GameAssets;
|
|
||||||
use crate::physics_layers::GameLayer;
|
|
||||||
|
|
||||||
#[derive(PointClass, Component, Reflect, Default)]
|
#[derive(PointClass, Component, Reflect, Default)]
|
||||||
#[reflect(QuakeClass, Component)]
|
#[reflect(QuakeClass, Component)]
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
|
use crate::{global_observer, hitpoints::Hit, physics_layers::GameLayer};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::{global_observer, hitpoints::Hit, physics_layers::GameLayer};
|
|
||||||
|
|
||||||
#[derive(Event, Debug)]
|
#[derive(Event, Debug)]
|
||||||
pub struct Explosion {
|
pub struct Explosion {
|
||||||
pub position: Vec3,
|
pub position: Vec3,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
use crate::GameState;
|
use crate::GameState;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Trail {
|
pub struct Trail {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use crate::control::controller_common::MovementSpeedFactor;
|
use crate::{
|
||||||
use crate::{GameState, global_observer, player::Player, tb_entities::Water};
|
GameState, control::controller_common::MovementSpeedFactor, global_observer, player::Player,
|
||||||
|
tb_entities::Water,
|
||||||
|
};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user