kirby can suck bubbles
All checks were successful
Build Bevy Game (Linux) / build (push) Successful in 19m20s
All checks were successful
Build Bevy Game (Linux) / build (push) Successful in 19m20s
This commit is contained in:
parent
0eb55dbe58
commit
3dc47f958d
9 changed files with 182 additions and 76 deletions
|
|
@ -2,4 +2,5 @@ max_width = 120
|
|||
chain_width = 120
|
||||
fn_call_width = 120
|
||||
use_small_heuristics = "Max"
|
||||
|
||||
imports_granularity = "Crate"
|
||||
group_imports = "StdExternalCrate"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
use crate::density_grid::DensityObject;
|
||||
use crate::kirby::Kirby;
|
||||
use crate::physics_body::PhysicsBody;
|
||||
use std::{f32::consts::TAU, fmt::Debug};
|
||||
|
||||
use bevy::prelude::*;
|
||||
use rand::Rng;
|
||||
use std::f32::consts::TAU;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{
|
||||
density_grid::DensityObject,
|
||||
kirby::Kirby,
|
||||
life::{DamageDealer, Life},
|
||||
physics_body::PhysicsBody,
|
||||
};
|
||||
|
||||
#[derive(Component)]
|
||||
#[require(Sprite, PhysicsBody, DensityObject)]
|
||||
#[require(Sprite, PhysicsBody, DensityObject, Life, DamageDealer)]
|
||||
pub struct Bubble {
|
||||
move_force: f32,
|
||||
}
|
||||
|
|
@ -16,7 +20,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..1000 {
|
||||
for _ in 0..500 {
|
||||
let angle = rng.random_range(0.0..TAU);
|
||||
let pos = Vec2::from_angle(angle) * 500.0;
|
||||
|
||||
|
|
@ -25,27 +29,29 @@ pub fn bubble_spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
Sprite { image: texture.clone(), ..default() },
|
||||
Transform::from_translation(pos.extend(0.0)).with_scale(Vec3::splat(1.0)),
|
||||
PhysicsBody { mass: 10.0, force: Vec2::ZERO, velocity: Vec2::ZERO, drag: 0.05 },
|
||||
Life::new(10.0),
|
||||
DamageDealer::new(0.5),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bubble_move(
|
||||
mut lana_query: Query<(&mut PhysicsBody, &Transform, &Bubble), With<Bubble>>,
|
||||
mut bubble_query: Query<(&mut PhysicsBody, &Transform, &Bubble), With<Bubble>>,
|
||||
kirby_query: Query<(&Transform, &Kirby), With<Kirby>>,
|
||||
) {
|
||||
for (mut lana_body, lana_transform, lana) in &mut lana_query {
|
||||
for (mut bubble_body, bubble_transform, bubble) in &mut bubble_query {
|
||||
let mut nearest_kirby_pos = Vec3::ZERO;
|
||||
let mut nearest_squared = f32::MAX;
|
||||
|
||||
for (kirby_transform, _kirby) in kirby_query {
|
||||
let dist_squared = kirby_transform.translation.distance_squared(lana_transform.translation);
|
||||
let dist_squared = kirby_transform.translation.distance_squared(bubble_transform.translation);
|
||||
if (dist_squared) < nearest_squared {
|
||||
nearest_squared = dist_squared;
|
||||
nearest_kirby_pos = kirby_transform.translation;
|
||||
}
|
||||
}
|
||||
|
||||
let dir = (nearest_kirby_pos - lana_transform.translation).normalize().xy();
|
||||
lana_body.force += dir * lana.move_force;
|
||||
let dir = (nearest_kirby_pos - bubble_transform.translation).normalize().xy();
|
||||
bubble_body.force += dir * bubble.move_force;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use std::f32::consts::TAU;
|
||||
|
||||
use crate::globals::MAX_MAP_WIDTH;
|
||||
use crate::physics_body::PhysicsBody;
|
||||
use bevy::prelude::*;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{globals::MAX_MAP_WIDTH, physics_body::PhysicsBody};
|
||||
|
||||
const CELL_SIZE: usize = 8;
|
||||
const MAX_MAP_SIZE: usize = MAX_MAP_WIDTH * 2;
|
||||
const ARRAY_WIDTH: usize = MAX_MAP_SIZE / CELL_SIZE;
|
||||
|
|
|
|||
56
src/kirby.rs
56
src/kirby.rs
|
|
@ -1,13 +1,23 @@
|
|||
use crate::animation::{AnimationIndices, AnimationTimer};
|
||||
use crate::physics_body::PhysicsBody;
|
||||
use crate::sphere_collider::SphereCollider;
|
||||
use bevy::prelude::*;
|
||||
|
||||
const KIRBY_SCALE: f32 = 3.0;
|
||||
|
||||
use crate::{
|
||||
animation::{AnimationIndices, AnimationTimer},
|
||||
life::{DamageDealer, Life},
|
||||
physics_body::PhysicsBody,
|
||||
sphere_collider::SphereCollider,
|
||||
};
|
||||
|
||||
#[derive(Component)]
|
||||
#[require(Sprite, PhysicsBody, SphereCollider, AnimationIndices)]
|
||||
#[require(Transform, SphereCollider, DamageDealer)]
|
||||
pub struct SuckArea;
|
||||
|
||||
#[derive(Component)]
|
||||
#[require(Sprite, PhysicsBody, SphereCollider, AnimationIndices, Life)]
|
||||
pub struct Kirby {
|
||||
speed_force: f32,
|
||||
sucking: bool,
|
||||
pub is_sucking: bool,
|
||||
}
|
||||
|
||||
pub fn kirby_spawn(
|
||||
|
|
@ -21,17 +31,29 @@ pub fn kirby_spawn(
|
|||
let texture_atlas_layout = texture_atlas_layouts.add(layout);
|
||||
let animation_indices = AnimationIndices { first: 3, last: 5 }; // idle
|
||||
|
||||
commands.spawn((
|
||||
Kirby { speed_force: 10000.0, sucking: false },
|
||||
let kirby_entity = commands
|
||||
.spawn((
|
||||
Kirby { speed_force: 10000.0, is_sucking: false },
|
||||
Sprite::from_atlas_image(
|
||||
texture,
|
||||
TextureAtlas { layout: texture_atlas_layout, index: animation_indices.first },
|
||||
),
|
||||
Transform::from_scale(Vec3::splat(3.0)),
|
||||
Transform::from_scale(Vec3::splat(KIRBY_SCALE)),
|
||||
animation_indices,
|
||||
AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)),
|
||||
body,
|
||||
SphereCollider::new(50.0),
|
||||
))
|
||||
.id();
|
||||
|
||||
commands.entity(kirby_entity).with_children(|parent| {
|
||||
parent.spawn((
|
||||
Transform::from_xyz(40.0, 0.0, 0.0),
|
||||
SuckArea,
|
||||
SphereCollider::new(80.0),
|
||||
DamageDealer::new(10000.0),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_dir(keys: Res<ButtonInput<KeyCode>>) -> Vec2 {
|
||||
|
|
@ -55,26 +77,26 @@ pub fn get_dir(keys: Res<ButtonInput<KeyCode>>) -> Vec2 {
|
|||
|
||||
pub fn kirby_actions(
|
||||
keys: Res<ButtonInput<KeyCode>>,
|
||||
mut query: Query<(&mut PhysicsBody, &mut Kirby, &mut AnimationIndices, &mut Transform, &mut Sprite), With<Kirby>>,
|
||||
mut query: Query<(&mut PhysicsBody, &mut Kirby, &mut AnimationIndices, &mut Transform), With<Kirby>>,
|
||||
) {
|
||||
let space_just_pressed = keys.just_pressed(KeyCode::Space);
|
||||
let space_just_released = keys.just_released(KeyCode::Space);
|
||||
let dir = get_dir(keys);
|
||||
for (mut body, mut kirby, mut anim_indices, mut transform, mut sprite) in &mut query {
|
||||
for (mut body, mut kirby, mut anim_indices, mut transform) in &mut query {
|
||||
if space_just_released {
|
||||
kirby.sucking = false
|
||||
kirby.is_sucking = false
|
||||
};
|
||||
|
||||
if space_just_pressed {
|
||||
kirby.sucking = true;
|
||||
kirby.is_sucking = true;
|
||||
anim_indices.change(0, 2);
|
||||
};
|
||||
|
||||
if kirby.sucking {
|
||||
if kirby.is_sucking {
|
||||
continue;
|
||||
};
|
||||
if dir.x != 0.0 {
|
||||
sprite.flip_x = dir.x < 0.0;
|
||||
transform.scale.x = if dir.x < 0.0 { -KIRBY_SCALE } else { KIRBY_SCALE };
|
||||
}
|
||||
body.force += dir * kirby.speed_force;
|
||||
if dir == Vec2::ZERO {
|
||||
|
|
@ -84,9 +106,3 @@ pub fn kirby_actions(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[require(Sprite, Transform)]
|
||||
pub struct KirbySuction {
|
||||
box_size: Vec2,
|
||||
}
|
||||
|
|
|
|||
96
src/life.rs
Normal file
96
src/life.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
use crate::{
|
||||
bubble::Bubble,
|
||||
kirby::{Kirby, SuckArea},
|
||||
sphere_collider::SphereCollider,
|
||||
};
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Life {
|
||||
max_life: f32,
|
||||
current_life: f32,
|
||||
}
|
||||
|
||||
impl Life {
|
||||
pub fn new(max_life: f32) -> Self {
|
||||
Self { max_life, current_life: max_life }
|
||||
}
|
||||
|
||||
pub fn damage(&mut self, amount: f32) {
|
||||
self.current_life -= amount;
|
||||
}
|
||||
|
||||
pub fn heal(&mut self, amount: f32) {
|
||||
self.current_life = (self.current_life + amount).min(self.max_life);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Life {
|
||||
fn default() -> Self {
|
||||
Life::new(100.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn despawn_if_dead(mut commands: Commands, query: Query<(Entity, &Life)>) {
|
||||
for (entity, life) in query.iter() {
|
||||
if life.current_life < 0.0 {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Default)]
|
||||
#[require(SphereCollider)]
|
||||
pub struct DamageDealer {
|
||||
damage_strength: f32,
|
||||
}
|
||||
|
||||
impl DamageDealer {
|
||||
pub fn new(damage: f32) -> Self {
|
||||
DamageDealer { damage_strength: damage }
|
||||
}
|
||||
}
|
||||
|
||||
// Considerating ennemies are ponctual
|
||||
pub fn bubble_receive_damage(
|
||||
time: Res<Time<Fixed>>,
|
||||
|
||||
mut enemy_query: Query<(&GlobalTransform, &mut Life), With<Bubble>>,
|
||||
|
||||
kirby_query: Query<(&Kirby, &Children)>,
|
||||
suck_area_query: Query<(&GlobalTransform, &SphereCollider, &DamageDealer), With<SuckArea>>,
|
||||
) {
|
||||
for (kirby, children) in &kirby_query {
|
||||
if !kirby.is_sucking {
|
||||
continue;
|
||||
}
|
||||
|
||||
for child in children.iter() {
|
||||
let Ok((suck_transform, suck_collider, suck_damage)) = suck_area_query.get(child) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for (enemy_transform, mut enemy_life) in &mut enemy_query {
|
||||
if suck_collider.point_inside(suck_transform.translation(), enemy_transform.translation()) {
|
||||
enemy_life.damage(suck_damage.damage_strength * time.delta_secs());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Considerating allies are ponctual
|
||||
pub fn kirby_receive_damage(
|
||||
mut ally_query: Query<(&GlobalTransform, &mut Life), With<Kirby>>,
|
||||
mut enemy_query: Query<(&GlobalTransform, &SphereCollider, &DamageDealer, &mut Life), With<Bubble>>,
|
||||
) {
|
||||
for (ally_transform, mut ally_life) in &mut ally_query {
|
||||
for (enemy_transform, enemy_sphere_collider, enemy_damage_dealer, mut bubble_life) in &mut enemy_query {
|
||||
if enemy_sphere_collider.point_inside(ally_transform.translation(), enemy_transform.translation()) {
|
||||
ally_life.damage(enemy_damage_dealer.damage_strength);
|
||||
bubble_life.damage(1000000.0); // Bubble explode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/main.rs
18
src/main.rs
|
|
@ -1,23 +1,22 @@
|
|||
use animation::animate_sprite;
|
||||
use bevy::prelude::*;
|
||||
use bubble::bubble_move;
|
||||
use bubble::bubble_spawn;
|
||||
use bubble::{bubble_move, bubble_spawn};
|
||||
use camera::spawn_camera;
|
||||
use density_grid::DensityGrid;
|
||||
use density_grid::density_grid_force;
|
||||
use density_grid::density_grid_update;
|
||||
use density_grid::init_density_object;
|
||||
use kirby::kirby_actions;
|
||||
use kirby::kirby_spawn;
|
||||
use density_grid::{DensityGrid, density_grid_force, density_grid_update, init_density_object};
|
||||
use kirby::{kirby_actions, kirby_spawn};
|
||||
use life::despawn_if_dead;
|
||||
use map::MapBounds;
|
||||
use physics_body::integrate;
|
||||
|
||||
use crate::life::bubble_receive_damage;
|
||||
|
||||
mod animation;
|
||||
mod bubble;
|
||||
mod camera;
|
||||
mod density_grid;
|
||||
mod globals;
|
||||
mod kirby;
|
||||
mod life;
|
||||
mod map;
|
||||
mod physics_body;
|
||||
mod sphere_collider;
|
||||
|
|
@ -25,6 +24,7 @@ mod sphere_collider;
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
|
||||
.insert_resource(Time::<Fixed>::from_seconds(1.0 / 60.0))
|
||||
.insert_resource(MapBounds { min: Vec2::ONE * -600.0, max: Vec2::ONE * 600.0 })
|
||||
.insert_resource(DensityGrid::new())
|
||||
.add_systems(Startup, spawn_camera)
|
||||
|
|
@ -35,6 +35,8 @@ fn main() {
|
|||
.add_systems(FixedUpdate, (density_grid_update, density_grid_force).chain())
|
||||
.add_systems(FixedUpdate, kirby_actions)
|
||||
.add_systems(FixedUpdate, bubble_move)
|
||||
.add_systems(FixedUpdate, bubble_receive_damage)
|
||||
.add_systems(FixedPostUpdate, despawn_if_dead)
|
||||
.add_systems(FixedPostUpdate, integrate)
|
||||
.run();
|
||||
}
|
||||
|
|
|
|||
12
src/map.rs
12
src/map.rs
|
|
@ -1,17 +1,7 @@
|
|||
use bevy::{prelude::*, render::renderer};
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct MapRoot;
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct MapBounds {
|
||||
pub min: Vec2,
|
||||
pub max: Vec2,
|
||||
}
|
||||
|
||||
pub fn setup_map_bounds(mut commands: Commands) {
|
||||
commands.insert_resource(MapBounds {
|
||||
min: Vec2::new(-320.0, -240.0),
|
||||
max: Vec2::new(320.0, 240.0),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::map::MapBounds;
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::map::MapBounds;
|
||||
|
||||
#[derive(Component)]
|
||||
#[require(Transform)]
|
||||
pub struct PhysicsBody {
|
||||
|
|
@ -12,20 +13,11 @@ pub struct PhysicsBody {
|
|||
|
||||
impl Default for PhysicsBody {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mass: 1.0,
|
||||
force: Vec2::ZERO,
|
||||
velocity: Vec2::ZERO,
|
||||
drag: 0.0,
|
||||
}
|
||||
Self { mass: 1.0, force: Vec2::ZERO, velocity: Vec2::ZERO, drag: 0.0 }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn integrate(
|
||||
bounds: Res<MapBounds>,
|
||||
mut query: Query<(&mut Transform, &mut PhysicsBody)>,
|
||||
time: Res<Time<Fixed>>,
|
||||
) {
|
||||
pub fn integrate(bounds: Res<MapBounds>, mut query: Query<(&mut Transform, &mut PhysicsBody)>, time: Res<Time<Fixed>>) {
|
||||
for (mut transform, mut physicsbody) in &mut query {
|
||||
let force = physicsbody.force;
|
||||
let mass = physicsbody.mass;
|
||||
|
|
|
|||
|
|
@ -7,10 +7,13 @@ pub struct SphereCollider {
|
|||
}
|
||||
|
||||
impl SphereCollider {
|
||||
fn point_inside(&self, self_pos: Vec3, point: Vec3) -> bool {
|
||||
pub fn new(radius: f32) -> Self {
|
||||
SphereCollider { radius: radius }
|
||||
}
|
||||
pub fn point_inside(&self, self_pos: Vec3, point: Vec3) -> bool {
|
||||
(self_pos - point).length_squared() < self.radius * self.radius
|
||||
}
|
||||
fn collides(&self, self_pos: Vec3, other: &SphereCollider, other_pos: Vec3) -> bool {
|
||||
pub fn collides(&self, self_pos: Vec3, other: &SphereCollider, other_pos: Vec3) -> bool {
|
||||
let radius_sum = self.radius + other.radius;
|
||||
(self_pos - other_pos).length_squared() < radius_sum * radius_sum
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue