This commit is contained in:
parent
57db0aff22
commit
6aeda8be14
7 changed files with 171 additions and 19 deletions
80
src/density_grid.rs
Normal file
80
src/density_grid.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
use crate::globals::MAX_MAP_WIDTH;
|
||||||
|
use crate::physics_body::PhysicsBody;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
const CELL_SIZE: usize = 32;
|
||||||
|
const MAX_MAP_SIZE: usize = MAX_MAP_WIDTH * 2;
|
||||||
|
const ARRAY_WIDTH: usize = MAX_MAP_SIZE / CELL_SIZE;
|
||||||
|
const GRID_ARRAY_SIZE: usize = ARRAY_WIDTH * ARRAY_WIDTH;
|
||||||
|
|
||||||
|
const DENSITY_FORCE_SCALE: f32 = 100.0;
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct DensityGrid {
|
||||||
|
grid: Box<[i32; GRID_ARRAY_SIZE]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DensityGrid {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
DensityGrid { grid: Box::new([0; GRID_ARRAY_SIZE]) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos_to_grid_cell(pos: Vec2) -> UVec2 {
|
||||||
|
let ix = ((pos.x + MAX_MAP_WIDTH as f32) / CELL_SIZE as f32) as u32;
|
||||||
|
let iy = ((pos.y + MAX_MAP_WIDTH as f32) / CELL_SIZE as f32) as u32;
|
||||||
|
UVec2::new(ix, iy)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_index_from_uvec2(grid_cell: UVec2) -> usize {
|
||||||
|
if grid_cell.x >= ARRAY_WIDTH as u32 || grid_cell.y >= ARRAY_WIDTH as u32 {
|
||||||
|
panic!("Index out of bounds: {:?}", grid_cell);
|
||||||
|
}
|
||||||
|
(grid_cell.x + grid_cell.y * ARRAY_WIDTH as u32) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
pub struct DensityObject {}
|
||||||
|
|
||||||
|
pub fn init_density_object(mut density_grid: ResMut<DensityGrid>, mut query: Query<&Transform>) {
|
||||||
|
for transform in &mut query {
|
||||||
|
let grid_cell = pos_to_grid_cell(transform.translation.xy());
|
||||||
|
// println!("grid cell {:?}", grid_cell);
|
||||||
|
let index = get_index_from_uvec2(grid_cell);
|
||||||
|
density_grid.grid[index] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn density_grid_update(mut density_grid: ResMut<DensityGrid>, query: Query<&Transform>) {
|
||||||
|
density_grid.grid.fill(0);
|
||||||
|
|
||||||
|
for transform in &query {
|
||||||
|
let cell = pos_to_grid_cell(transform.translation.xy());
|
||||||
|
let idx = get_index_from_uvec2(cell);
|
||||||
|
density_grid.grid[idx] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn density_grid_force(
|
||||||
|
density_grid: Res<DensityGrid>,
|
||||||
|
mut query: Query<(&mut PhysicsBody, &Transform), With<DensityObject>>,
|
||||||
|
) {
|
||||||
|
for (mut physics_body, transform) in &mut query {
|
||||||
|
let grid_cell = pos_to_grid_cell(transform.translation.xy());
|
||||||
|
let mut density_gradient = Vec2::ZERO;
|
||||||
|
|
||||||
|
for dx in [-1, 0, 1] {
|
||||||
|
for dy in [-1, 0, 1] {
|
||||||
|
let offset = IVec2 { x: dx, y: dy };
|
||||||
|
let neighbor_cell = (offset + grid_cell.as_ivec2()).as_uvec2();
|
||||||
|
let index = get_index_from_uvec2(neighbor_cell);
|
||||||
|
let density = density_grid.grid[index];
|
||||||
|
|
||||||
|
// println!("offset : {:?} density: {:?}", offset.as_vec2(), density);
|
||||||
|
density_gradient += offset.as_vec2() * density as f32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println!("density : {:?}", density_gradient);
|
||||||
|
physics_body.force -= density_gradient * DENSITY_FORCE_SCALE;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/globals.rs
Normal file
1
src/globals.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub static MAX_MAP_WIDTH: usize = 1024;
|
||||||
16
src/kirby.rs
16
src/kirby.rs
|
|
@ -10,14 +10,9 @@ pub struct Kirby {
|
||||||
|
|
||||||
pub fn kirby_spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
|
pub fn kirby_spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
let sprite = Sprite::from_image(asset_server.load("sprites/kirby.png"));
|
let sprite = Sprite::from_image(asset_server.load("sprites/kirby.png"));
|
||||||
let body = PhysicsBody {
|
let body = PhysicsBody { mass: 10.0, force: Vec2::ZERO, velocity: Vec2::ZERO, drag: 0.05 };
|
||||||
mass: 10.0,
|
|
||||||
force: Vec2::ZERO,
|
|
||||||
velocity: Vec2::ZERO,
|
|
||||||
drag: 0.05,
|
|
||||||
};
|
|
||||||
let transform = Transform::from_xyz(0.0, 0.0, 0.0).with_scale(Vec3::ONE * 0.25);
|
let transform = Transform::from_xyz(0.0, 0.0, 0.0).with_scale(Vec3::ONE * 0.25);
|
||||||
commands.spawn((Kirby { speed_force: 50.0 }, transform, sprite, body));
|
commands.spawn((Kirby { speed_force: 10000.0 }, transform, sprite, body));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dir(keys: Res<ButtonInput<KeyCode>>) -> Vec2 {
|
pub fn get_dir(keys: Res<ButtonInput<KeyCode>>) -> Vec2 {
|
||||||
|
|
@ -43,16 +38,13 @@ pub fn get_dir(keys: Res<ButtonInput<KeyCode>>) -> Vec2 {
|
||||||
dir.normalize_or_zero()
|
dir.normalize_or_zero()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kirby_player_move(
|
pub fn kirby_player_move(keys: Res<ButtonInput<KeyCode>>, mut query: Query<(&mut PhysicsBody, &Kirby), With<Kirby>>) {
|
||||||
keys: Res<ButtonInput<KeyCode>>,
|
|
||||||
mut query: Query<(&mut PhysicsBody, &Kirby), With<Kirby>>,
|
|
||||||
) {
|
|
||||||
if keys.pressed(KeyCode::Space) {
|
if keys.pressed(KeyCode::Space) {
|
||||||
println!("SUCKING");
|
println!("SUCKING");
|
||||||
}
|
}
|
||||||
let dir = get_dir(keys);
|
let dir = get_dir(keys);
|
||||||
for (mut body, kirby) in &mut query {
|
for (mut body, kirby) in &mut query {
|
||||||
body.velocity += dir * kirby.speed_force;
|
body.force += dir * kirby.speed_force;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
57
src/lana.rs
Normal file
57
src/lana.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
use crate::density_grid::DensityObject;
|
||||||
|
use crate::kirby::Kirby;
|
||||||
|
use crate::physics_body::PhysicsBody;
|
||||||
|
use crate::sphere_collider::SphereCollider;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use rand::Rng;
|
||||||
|
use std::f32::consts::TAU;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[require(Sprite, PhysicsBody, SphereCollider, DensityObject)]
|
||||||
|
pub struct Lana {
|
||||||
|
move_force: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lana_spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
let texture: Handle<Image> = asset_server.load("sprites/lana.png");
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
|
||||||
|
for _ in 0..1_000 {
|
||||||
|
let angle = rng.random_range(0.0..TAU);
|
||||||
|
let pos = Vec2::from_angle(angle) * 500.0;
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
Lana { move_force: 3000.0 },
|
||||||
|
Sprite { image: texture.clone(), ..default() },
|
||||||
|
Transform::from_translation(pos.extend(0.0)).with_scale(Vec3::splat(0.02)),
|
||||||
|
PhysicsBody { mass: 10.0, force: Vec2::ZERO, velocity: Vec2::ZERO, drag: 0.05 },
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lana_move(
|
||||||
|
mut lana_query: Query<(&mut PhysicsBody, &Transform, &Lana), With<Lana>>,
|
||||||
|
kirby_query: Query<(&Transform, &Kirby), With<Kirby>>,
|
||||||
|
) {
|
||||||
|
for (mut lana_body, lana_transform, lana) in &mut lana_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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[require(Sprite, Transform)]
|
||||||
|
pub struct KirbySuction {
|
||||||
|
box_size: Vec2,
|
||||||
|
}
|
||||||
22
src/main.rs
22
src/main.rs
|
|
@ -1,12 +1,21 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use camera::spawn_camera;
|
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_player_move;
|
use kirby::kirby_player_move;
|
||||||
use kirby::kirby_spawn;
|
use kirby::kirby_spawn;
|
||||||
|
use lana::lana_move;
|
||||||
|
use lana::lana_spawn;
|
||||||
use map::MapBounds;
|
use map::MapBounds;
|
||||||
use physics_body::integrate;
|
use physics_body::integrate;
|
||||||
|
|
||||||
mod camera;
|
mod camera;
|
||||||
|
mod density_grid;
|
||||||
|
mod globals;
|
||||||
mod kirby;
|
mod kirby;
|
||||||
|
mod lana;
|
||||||
mod map;
|
mod map;
|
||||||
mod physics_body;
|
mod physics_body;
|
||||||
mod sphere_collider;
|
mod sphere_collider;
|
||||||
|
|
@ -14,12 +23,15 @@ mod sphere_collider;
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.insert_resource(MapBounds {
|
.insert_resource(MapBounds { min: Vec2::ONE * -600.0, max: Vec2::ONE * 600.0 })
|
||||||
min: Vec2::ONE * -400.0,
|
.insert_resource(DensityGrid::new())
|
||||||
max: Vec2::ONE * 400.0,
|
|
||||||
})
|
|
||||||
.add_systems(Startup, spawn_camera)
|
.add_systems(Startup, spawn_camera)
|
||||||
.add_systems(Startup, kirby_spawn)
|
.add_systems(Startup, kirby_spawn)
|
||||||
.add_systems(FixedUpdate, (kirby_player_move, integrate).chain())
|
.add_systems(Startup, lana_spawn)
|
||||||
|
.add_systems(PostStartup, init_density_object)
|
||||||
|
.add_systems(FixedUpdate, (density_grid_update, density_grid_force).chain())
|
||||||
|
.add_systems(FixedUpdate, kirby_player_move)
|
||||||
|
.add_systems(FixedUpdate, lana_move)
|
||||||
|
.add_systems(FixedPostUpdate, integrate)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ pub fn integrate(
|
||||||
physicsbody.velocity += force * delta_secs / mass;
|
physicsbody.velocity += force * delta_secs / mass;
|
||||||
physicsbody.velocity *= 1.0 - drag;
|
physicsbody.velocity *= 1.0 - drag;
|
||||||
|
|
||||||
bounce(&mut physicsbody.velocity, &transform, &bounds);
|
map_bounce(&mut physicsbody.velocity, &transform, &bounds);
|
||||||
|
|
||||||
transform.translation.x += physicsbody.velocity.x * delta_secs;
|
transform.translation.x += physicsbody.velocity.x * delta_secs;
|
||||||
transform.translation.y += physicsbody.velocity.y * delta_secs;
|
transform.translation.y += physicsbody.velocity.y * delta_secs;
|
||||||
|
|
@ -44,7 +44,7 @@ pub fn integrate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bounce(velocity: &mut Vec2, transform: &Transform, bounds: &MapBounds) {
|
fn map_bounce(velocity: &mut Vec2, transform: &Transform, bounds: &MapBounds) {
|
||||||
if (transform.translation.x < bounds.min.x && velocity.x < 0.0)
|
if (transform.translation.x < bounds.min.x && velocity.x < 0.0)
|
||||||
|| (transform.translation.x > bounds.max.x && velocity.x > 0.0)
|
|| (transform.translation.x > bounds.max.x && velocity.x > 0.0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,13 @@ use bevy::{prelude::*, render::renderer};
|
||||||
pub struct SphereCollider {
|
pub struct SphereCollider {
|
||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SphereCollider {
|
||||||
|
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 {
|
||||||
|
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