feat: Collision groups and states

This commit is contained in:
Alexey 2026-04-13 17:08:25 +03:00
commit 88a73275ff
8 changed files with 108 additions and 3 deletions

View file

@ -5,6 +5,8 @@ use std::time::Duration;
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
use crate::{GROUP_ATTACK, GROUP_INTERACTIVE, GROUP_STATIC};
/// Contains logic for attack-related calculations
#[derive(Component, Clone, Debug, Reflect)]
#[reflect(Component, Clone, Debug)]
@ -33,6 +35,7 @@ impl AttackArea {
max_distance,
}
}
/// Returns attack area bundle with everything needed
pub fn bundle(
damage: f32,
@ -41,6 +44,7 @@ impl AttackArea {
position: Vec2,
half_area: Vec2,
facing_left: bool,
affinity: Group,
) -> impl Bundle {
let origin_x = if facing_left { position.x - half_area.x } else { position.x + half_area.x };
(
@ -49,6 +53,10 @@ impl AttackArea {
// Collision
Transform::from_xyz(position.x, position.y, 0.),
CollisionGroups::new(
GROUP_ATTACK,
GROUP_STATIC | GROUP_INTERACTIVE | affinity,
),
Collider::cuboid(half_area.x, half_area.y),
// AttackAreaOrigin
@ -73,3 +81,12 @@ impl AttackArea {
self.damage * EaseFunction::QuinticOut.sample_unchecked(total_multiplier)
}
}
/// System that updates AttackArea timers and despawns those who timed out
pub fn update_attack_areas(mut commands: Commands, time: Res<Time>, areas: Query<(Entity, &mut AttackArea)>) {
for (area_id, mut area) in areas {
if area.tick(time.delta()) {
commands.entity(area_id).despawn();
}
}
}

View file

@ -1,3 +1,25 @@
//! This module contains core concepts of the combat system
use bevy::prelude::*;
pub mod attack;
/// Combat health component
#[derive(Component, Clone, Copy, PartialEq, Debug, Default, Reflect)]
#[reflect(Component, Clone, PartialEq, Debug, Default)]
pub struct Health {
/// Current health value
pub current: f32,
/// Maximum health value
pub max: f32,
}
impl Health {
/// Constructs new health component
pub fn new(max: f32) -> Self {
Self {
current: max,
max,
}
}
}

View file

@ -10,14 +10,27 @@ pub mod graph;
pub mod input;
pub mod player;
pub mod plugin;
pub mod states;
pub mod timer;
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
use crate::{player::Player, timer::BpmTimer};
const PIXELS_PER_METER: f32 = 16.;
/// Collision group for static entities (walls, floors, etc)
pub const GROUP_STATIC: Group = Group::GROUP_1;
/// Collision group for interactive entities (doors, buttons, etc)
pub const GROUP_INTERACTIVE: Group = Group::GROUP_2;
/// Collision group for player and player-related entities
pub const GROUP_FRIENDLY: Group = Group::GROUP_3;
/// Collision group for enemy-related entities
pub const GROUP_ENEMY: Group = Group::GROUP_4;
/// Collision group for attack-related entities
pub const GROUP_ATTACK: Group = Group::GROUP_5;
/// Returns pixel measurement for given length in meters
#[inline(always)] pub const fn meters(length: f32) -> f32 { PIXELS_PER_METER * length }

View file

@ -5,11 +5,10 @@ use bevy_rapier2d::prelude::*;
use leafwing_input_manager::prelude::*;
use crate::{
meters,
input::{
GROUP_ATTACK, GROUP_ENEMY, GROUP_FRIENDLY, GROUP_INTERACTIVE, GROUP_STATIC, input::{
DefaultInputMap,
PlayerInput,
}
}, meters
};
pub mod systems;
@ -51,11 +50,19 @@ impl Player {
RigidBody::KinematicPositionBased,
KinematicCharacterController::default(),
ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_STATIC,
CollisionGroups::new(
GROUP_FRIENDLY,
GROUP_STATIC | GROUP_INTERACTIVE | GROUP_ENEMY | GROUP_ATTACK,
),
Collider::cuboid(meters(0.3), meters(0.9)),
ActiveEvents::COLLISION_EVENTS,
Sleeping::disabled(),
// State
crate::states::Free,
Children::spawn((
// Camera
Spawn((
Name::new("Player camera"),
Camera2d,

View file

@ -3,6 +3,7 @@
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
use leafwing_input_manager::prelude::*;
use seldom_state::prelude::*;
use crate::*;
@ -18,6 +19,8 @@ impl Plugin for GamePlugin {
InputManagerPlugin::<input::PlayerInput>::default(),
InputManagerPlugin::<input::DebugInput>::default(),
StateMachinePlugin::default(),
))
.add_systems(Startup, setup)
.add_systems(Update, (

24
src/states.rs Normal file
View file

@ -0,0 +1,24 @@
//! Commonly used entity states
use bevy::prelude::*;
/// Entity is not doing anything special
#[derive(Component, Clone, Copy, PartialEq, Eq, Debug, Default, Reflect)]
#[reflect(Component, Clone, PartialEq, Debug, Default)]
pub struct Free;
/// Entity is acting and cannot do something else
#[derive(Component, Clone, Copy, PartialEq, Eq, Debug, Default, Reflect)]
#[reflect(Component, Clone, PartialEq, Debug, Default)]
pub struct Acting {
/// How much time left before action ends
pub beats_remaining: u32,
}
/// Entity is awaiting a specific set of actions
#[derive(Component, Clone, Copy, PartialEq, Eq, Debug, Default, Reflect)]
#[reflect(Component, Clone, PartialEq, Debug, Default)]
pub struct ActionWindow {
/// How much time left before window ends
pub beats_remaining: u32,
}