collectables
Some checks failed
Build Bevy Game (Linux + Windows) / build-windows (push) Has been cancelled
Build Bevy Game (Linux + Windows) / build-linux (push) Has been cancelled

This commit is contained in:
Crizomb 2026-01-02 21:15:21 +01:00
parent 467f147142
commit 5cafda10af
11 changed files with 162 additions and 19 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 B

View file

@ -21,7 +21,7 @@ pub fn bubble_spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
let texture: Handle<Image> = asset_server.load("sprites/bubble.png");
let mut rng = rand::rng();
for _ in 0..500 {
for _ in 0..50 {
let angle = rng.random_range(0.0..TAU);
let pos = Vec2::from_angle(angle) * 500.0;

118
src/core/collectible.rs Normal file
View file

@ -0,0 +1,118 @@
use std::path::Path;
use std::time::Duration;
#[derive(Component)]
struct kirbyTimer(Timer);
use crate::core::counter::BubbleKilledTotalCount;
use crate::core::kirby::{Kirby, kirby_spawn};
use crate::physics::sphere_collider::SphereCollider;
use bevy::prelude::*;
const ASSETS_NAME: [&str; 3] = ["speed_item.png", "shield_item.png", "kirby_item.png"];
const COLLECTABLE_COLLIDER_RADIUS: f32 = 50.0;
const KIRBY_SPAWN_TIMER_DURATION: Duration = Duration::from_secs(2);
#[derive(Clone, Copy)]
pub enum CollectableType {
SpeedBoost = 0,
ShieldBoost = 1,
NewKirby = 2,
}
#[derive(Component)]
#[require(SphereCollider, Sprite)]
pub struct Collectable(CollectableType);
// Not a system.
pub fn collectable_spawn(
commands: &mut Commands,
asset_server: &Res<AssetServer>,
collectable_type: CollectableType,
pos: Vec3,
) {
let asset_name = ASSETS_NAME[collectable_type as usize];
let asset_path = Path::new("sprites").join(asset_name);
let sprite = Sprite::from_image(asset_server.load(asset_path));
commands.spawn((
Collectable(collectable_type),
SphereCollider::new(COLLECTABLE_COLLIDER_RADIUS),
Transform::from_translation(pos).with_scale(Vec3::splat(5.0)),
sprite,
));
}
pub struct CollectableSpawner;
pub fn collectable_spawner_init(mut commands: Commands, asset_server: Res<AssetServer>) {
collectable_spawn(&mut commands, &asset_server, CollectableType::NewKirby, Vec3::new(250.0, 250.0, 0.0));
}
pub fn collectable_actions_system(
mut commands: Commands,
mut kirby_query: Query<(&mut Kirby, &SphereCollider, &Transform)>,
collectable_query: Query<(Entity, &Collectable, &SphereCollider, &Transform)>,
) {
for (mut kirby, kirby_collider, kirby_tf) in &mut kirby_query {
for (collectible_entity, collectible, c_collider, collectable_tf) in collectable_query {
let collides = c_collider.collides(collectable_tf.translation, kirby_collider, kirby_tf.translation);
let kirby_pos = kirby_tf.translation;
if !collides {
continue;
}
match &collectible.0 {
CollectableType::SpeedBoost => speed_boost(&mut kirby),
CollectableType::ShieldBoost => shield_boost(&mut kirby),
CollectableType::NewKirby => new_kirby(kirby_pos, &mut commands),
}
commands.entity(collectible_entity).despawn();
}
}
}
fn speed_boost(kirby: &mut Kirby) {
kirby.speed_force *= 1.2;
}
fn shield_boost(kirby: &mut Kirby) {
kirby.shield_factor *= 0.8;
}
fn new_kirby(kirby_pos: Vec3, commands: &mut Commands) {
commands.spawn(
(KirbySpawnHandler { timer: Timer::new(KIRBY_SPAWN_TIMER_DURATION, TimerMode::Once), position: kirby_pos }),
);
}
#[derive(Component)]
struct KirbySpawnHandler {
timer: Timer,
position: Vec3,
}
// After player get new_kirby item, it should spawn a new_kirby after a certain time
pub fn kirby_spawn_timer(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
query: Query<(&mut KirbySpawnHandler, Entity)>,
time: Res<Time>,
) {
for (mut kirby_spawn_handler, kirby_spawn_entity) in query {
kirby_spawn_handler.timer.tick(time.delta());
if kirby_spawn_handler.timer.is_finished() {
kirby_spawn(&mut commands, &asset_server, &mut texture_atlas_layouts, kirby_spawn_handler.position);
commands.entity(kirby_spawn_entity).despawn();
}
}
}
pub struct CollectablePlugin;
impl Plugin for CollectablePlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, collectable_spawner_init)
.add_systems(FixedUpdate, collectable_actions_system)
.add_systems(Update, kirby_spawn_timer);
}
}

View file

@ -3,7 +3,7 @@ use derive_more::{Deref, DerefMut};
#[derive(Resource, Default)]
pub struct Counter {
pub count: u32,
count: u32,
}
impl Counter {
@ -14,7 +14,14 @@ impl Counter {
pub fn reset(&mut self) {
self.count = 0;
}
pub fn get_count(&self) -> u32 {
self.count
}
}
#[derive(Resource, Default)]
pub struct BubbleKilledTotalCount(pub Counter);
#[derive(Resource, Default, Deref, DerefMut)]
pub struct BubbleExplodedCountThisFrame(pub Counter);
@ -26,13 +33,15 @@ pub struct BubbleSuckedCountThisFrame(pub Counter);
pub struct KirbyHitCountThisFrame(pub Counter);
fn reset_frame_counters(
mut bubble_sucked: ResMut<BubbleSuckedCountThisFrame>,
mut bubble_exploded: ResMut<BubbleExplodedCountThisFrame>,
mut kirby_hit: ResMut<KirbyHitCountThisFrame>,
mut bubble_sucked_this_frame: ResMut<BubbleSuckedCountThisFrame>,
mut bubble_exploded_this_frame: ResMut<BubbleExplodedCountThisFrame>,
mut kirby_hit_count_this_frame: ResMut<KirbyHitCountThisFrame>,
mut total_killed: ResMut<BubbleKilledTotalCount>,
) {
bubble_sucked.reset();
bubble_exploded.reset();
kirby_hit.reset();
total_killed.0.count += bubble_sucked_this_frame.count + bubble_exploded_this_frame.count;
bubble_sucked_this_frame.reset();
bubble_exploded_this_frame.reset();
kirby_hit_count_this_frame.reset();
}
pub struct CounterPlugin;
@ -42,6 +51,7 @@ impl Plugin for CounterPlugin {
app.insert_resource(BubbleSuckedCountThisFrame::default())
.insert_resource(BubbleExplodedCountThisFrame::default())
.insert_resource(KirbyHitCountThisFrame::default())
.insert_resource(BubbleKilledTotalCount::default())
.add_systems(FixedPostUpdate, reset_frame_counters);
}
}

View file

@ -16,14 +16,25 @@ pub struct SuckArea;
#[derive(Component)]
#[require(Sprite, PhysicsBody, SphereCollider, AnimationIndices, Life)]
pub struct Kirby {
speed_force: f32,
pub speed_force: f32,
pub is_sucking: bool,
pub shield_factor: f32, // 1.0 receive full damage, 0.1 receive 10% of damage
}
pub fn kirby_spawn(
pub fn kirby_spawn_start(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
) {
kirby_spawn(&mut commands, &asset_server, &mut texture_atlas_layouts, Vec3::ZERO);
// kirby_spawn(&mut commands, &asset_server, &mut texture_atlas_layouts, Vec3 { x: 500.0, y: 0.0, z: 0.0 });
}
pub fn kirby_spawn(
commands: &mut Commands,
asset_server: &Res<AssetServer>,
texture_atlas_layouts: &mut ResMut<Assets<TextureAtlasLayout>>,
pos: Vec3,
) {
let texture: Handle<Image> = asset_server.load("sprites/Sprite_kirby-Sheet.png");
let layout = TextureAtlasLayout::from_grid(UVec2::splat(32), 3, 3, None, None);
@ -32,7 +43,7 @@ pub fn kirby_spawn(
let kirby_entity = commands
.spawn((
Kirby { speed_force: 10000.0, is_sucking: false },
Kirby { speed_force: 10000.0, is_sucking: false, shield_factor: 1.0 },
Sprite::from_atlas_image(
texture,
TextureAtlas { layout: texture_atlas_layout, index: animation_indices.first },
@ -42,7 +53,7 @@ pub fn kirby_spawn(
animation_indices,
AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)),
Life::new(1000.0),
Transform::from_scale(Vec3::splat(KIRBY_SCALE)),
Transform::from_translation(pos).with_scale(Vec3::splat(KIRBY_SCALE)),
))
.id();
@ -108,6 +119,6 @@ pub struct KirbyPlugin;
impl Plugin for KirbyPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, kirby_spawn).add_systems(FixedUpdate, kirby_actions);
app.add_systems(Startup, kirby_spawn_start).add_systems(FixedUpdate, kirby_actions);
}
}

View file

@ -86,16 +86,16 @@ pub fn bubble_receive_damage(
pub fn kirby_receive_damage(
mut bubble_exploded: ResMut<BubbleExplodedCountThisFrame>,
mut kirby_hit: ResMut<KirbyHitCountThisFrame>,
mut kirby_query: Query<(&GlobalTransform, &mut Life), (With<Kirby>, Without<Bubble>)>,
mut kirby_query: Query<(&Kirby, &GlobalTransform, &mut Life), (Without<Bubble>)>,
mut bubble_query: Query<
(&GlobalTransform, &SphereCollider, &DamageDealer, &mut Life),
(With<Bubble>, Without<Kirby>),
>,
) {
for (kirby_transform, mut kirby_life) in &mut kirby_query {
for (kirby, kirby_transform, mut kirby_life) in &mut kirby_query {
for (bubble_transform, bubble_sphere_collider, bubble_damage_dealer, mut bubble_life) in &mut bubble_query {
if bubble_sphere_collider.point_inside(kirby_transform.translation(), bubble_transform.translation()) {
kirby_life.damage(bubble_damage_dealer.damage_strength);
kirby_life.damage(bubble_damage_dealer.damage_strength * kirby.shield_factor);
bubble_life.damage(1000000.0); // Bubble explode
// println!("{:?}", kirby_life.current_life)
bubble_exploded.increase(1);

View file

@ -1,4 +1,5 @@
pub mod bubble;
pub mod collectible;
pub mod counter;
pub mod kirby;
pub mod life;

View file

@ -32,7 +32,7 @@ pub fn camera_shake(
let rand_angle = rand::random_range(0.0..TAU);
let rand_vec = Vec2::from_angle(rand_angle);
let shaking_strength = kirby_hit_count.count as f32 * parent.shaking_intensity_factor;
let shaking_strength = kirby_hit_count.get_count() as f32 * parent.shaking_intensity_factor;
let shaking_vec = rand_vec * shaking_strength;
let mut child_translation = child_transform.translation.clone();
child_translation += shaking_vec.extend(0.0);

View file

@ -7,19 +7,19 @@ use physics::physics_body::physics_integrate;
mod core;
use core::bubble::BubblePlugin;
use core::collectible::CollectablePlugin;
use core::counter::CounterPlugin;
use core::kirby::KirbyPlugin;
use core::life::{bubble_receive_damage, despawn_if_dead, kirby_receive_damage};
mod map;
use map::map::MAP_SIZE;
use map::map::MapBounds;
mod juice;
use juice::animation::animate_sprite;
use juice::camera::MyCameraPlugin;
const MAP_SIZE: u32 = 1000;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()).set(WindowPlugin {
@ -36,6 +36,7 @@ fn main() {
.add_plugins(DensityGridPlugin)
.add_plugins(BubblePlugin)
.add_plugins(MyCameraPlugin)
.add_plugins(CollectablePlugin)
.insert_resource(Time::<Fixed>::from_seconds(1.0 / 60.0))
.insert_resource(MapBounds { min: -Vec2::ONE * MAP_SIZE as f32 / 2.0, max: Vec2::ONE * MAP_SIZE as f32 / 2.0 })
.add_systems(Update, animate_sprite)

View file

@ -1,5 +1,7 @@
use bevy::prelude::*;
pub const MAP_SIZE: u32 = 1000;
#[derive(Resource)]
pub struct MapBounds {
pub min: Vec2,