Skip to content

Commit

Permalink
feat: Rigidbodies improvement (#74)
Browse files Browse the repository at this point in the history
* Removes RotSprite

RotSprite was too slow for realtime stuff.
This also adds some new debug visualization.

* Better rapier2d colliders

* Better collider loading/deloading
  • Loading branch information
Zac8668 authored Jan 28, 2024
1 parent 226e52d commit 4f95599
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 208 deletions.
Binary file added assets/player/jetpack.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ impl Chunk {
&self.get_values(materials),
CHUNK_LENGHT as u32,
CHUNK_LENGHT as u32,
(0., 0.),
)
}

Expand Down
143 changes: 95 additions & 48 deletions src/chunk_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,80 +218,117 @@ pub fn manager_setup(
pub fn add_colliders(
mut commands: Commands,
chunk_manager: Res<ChunkManager>,
chunks: Query<(Entity, &ChunkComponent)>,
rigidbodies: Query<(&Transform, &Rigidbody)>,
chunks: Query<(Entity, &ChunkComponent), Without<Collider>>,
materials: (Res<Assets<Materials>>, Res<MaterialsHandle>),
has_collider: Res<HasCollider>,
) {
let materials = materials.0.get(materials.1 .0.clone()).unwrap();
puffin::profile_function!();

let Some(materials) = materials.0.get(materials.1 .0.clone()) else {
return;
};

let mut rects = vec![];
if has_collider.0.is_empty() {
return;
}

for (ent, pos) in &chunks {
for rect in has_collider.0.iter() {
if rect.contains(pos.0) {
if let Some(chunk) = chunk_manager.chunks.get(&pos.0) {
let collider = chunk.get_collider(materials);

if let Some(collider) = collider {
commands
.entity(ent)
.insert(collider)
.insert(bevy_rapier2d::prelude::RigidBody::Fixed);
}
}

break;
}
}
}
}

pub fn remove_colliders(
mut commands: Commands,
chunks: Query<(Entity, &ChunkComponent), With<Collider>>,
has_collider: Res<HasCollider>,
) {
if has_collider.0.is_empty() {
return;
}

for (ent, pos) in &chunks {
let mut contains = false;
for rect in has_collider.0.iter() {
if rect.contains(pos.0) {
contains = true;
break;
}
}

if !contains {
//If none contains, remove collider and go to next chunk
//Remove collider
if let Some(mut entity) = commands.get_entity(ent) {
entity.remove::<Collider>();
entity.remove::<bevy_rapier2d::prelude::RigidBody>();
}
}
}
}

pub fn update_has_collider(
mut has_collider: ResMut<HasCollider>,
rigidbodies: Query<(&Transform, &Rigidbody)>,
) {
has_collider.0 = vec![];
for (transform, rigidbody) in &rigidbodies {
let l = std::f32::consts::SQRT_2 * (rigidbody.width as f32).max(rigidbody.height as f32);
let angle = -transform.rotation.to_euler(EulerRot::XYZ).2;
let mut center = transform.translation.xy();
center.y *= -1.;
center += vec2(rigidbody.width as f32, rigidbody.height as f32)
.rotate(Vec2::from_angle(angle))
/ 2.;

let l = std::f32::consts::SQRT_2 * (rigidbody.width as f32).max(rigidbody.height as f32);
let angle = std::f32::consts::FRAC_PI_4 + std::f32::consts::PI;
let mut top_left = transform.translation.xy();
top_left.y *= -1.;
let mut top_left = center;
top_left += vec2(angle.cos(), angle.sin()) * l / 2.;

let angle = std::f32::consts::FRAC_PI_4;
let mut down_right = transform.translation.xy();
down_right.y *= -1.;
let mut down_right = center;
down_right += vec2(angle.cos(), angle.sin()) * l / 2.;

/*{
//Some debug visualization
let mut top_left = top_left;
top_left.y *= -1.;
gizmos.circle_2d(top_left, 10., Color::RED);
gizmos.circle_2d(top_left, 5., Color::RED);
let mut down_right = down_right;
down_right.y *= -1.;
gizmos.circle_2d(down_right, 10., Color::BLACK);
gizmos.circle_2d(down_right, 5., Color::BLACK);
}*/

const LOADING_OFF: f32 = 1.5;
top_left -= vec2(CHUNK_LENGHT as f32, CHUNK_LENGHT as f32) * LOADING_OFF;
down_right += vec2(CHUNK_LENGHT as f32, CHUNK_LENGHT as f32) * LOADING_OFF;

top_left /= CHUNK_LENGHT as f32;
down_right /= CHUNK_LENGHT as f32;

const LOADING_OFF: i32 = 2;
let bounds_rect = IRect::new(
top_left.x as i32 - LOADING_OFF,
top_left.y as i32 - LOADING_OFF,
down_right.x as i32 + LOADING_OFF,
down_right.y as i32 + LOADING_OFF,
top_left.x as i32,
top_left.y as i32,
down_right.x as i32,
down_right.y as i32,
);

rects.push(bounds_rect);
}

if !rects.is_empty() {
'chunks: for (ent, pos) in &chunks {
for (i, rect) in rects.iter().enumerate() {
//If on bounds continue by breaking this loop
if rect.contains(pos.0) {
break;
} else if i == rects.len() - 1 {
//If none contains, remove collider and go to next chunk
//Remove collider
if let Some(mut entity) = commands.get_entity(ent) {
entity.remove::<Collider>();
entity.remove::<bevy_rapier2d::prelude::RigidBody>();
}
continue 'chunks;
}
}

if let Some(chunk) = chunk_manager.chunks.get(&pos.0) {
let collider = chunk.get_collider(materials);

if let Some(collider) = collider {
commands
.entity(ent)
.insert(collider)
.insert(bevy_rapier2d::prelude::RigidBody::Fixed);
}
}
}
has_collider.0.push(bounds_rect);
}
}

Expand Down Expand Up @@ -670,6 +707,9 @@ fn clear_render_rect(mut dirty_rects: ResMut<DirtyRects>) {
dirty_rects.render = HashMap::new();
}

#[derive(Resource, Default)]
pub struct HasCollider(pub Vec<IRect>);

pub struct ChunkManagerPlugin;
impl Plugin for ChunkManagerPlugin {
fn build(&self, app: &mut App) {
Expand All @@ -680,12 +720,19 @@ impl Plugin for ChunkManagerPlugin {
)
.add_systems(
Update,
(update_manager_pos, add_colliders).run_if(in_state(GameState::Game)),
(
update_manager_pos,
add_colliders.after(update_has_collider),
remove_colliders.after(update_has_collider),
update_has_collider,
)
.run_if(in_state(GameState::Game)),
)
.add_systems(
PreUpdate,
clear_render_rect.run_if(in_state(GameState::Game)),
)
.init_resource::<HasCollider>()
.init_resource::<ChunkManager>()
.init_resource::<DirtyRects>();

Expand Down
1 change: 1 addition & 0 deletions src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub const _CAMERA_SPEED: f32 = 10.;
pub const PLAYER_LAYER: f32 = 1.;
pub const PARTICLE_LAYER: f32 = 10.;
pub const AUTOMATA_LAYER: f32 = 100.;
pub const RIGIDBODY_LAYER: f32 = 1000.;

//Buttons
pub const NORMAL_BUTTON: Color = Color::rgb(0.15, 0.15, 0.15);
Expand Down
92 changes: 67 additions & 25 deletions src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,32 @@ fn prev_mpos(

//Debug Render systems

pub fn render_dirty_rects(mut commands: Commands, dirty_rects: Res<DirtyRects>) {
pub fn render_dirty_rects_gizmos(mut gizmos: Gizmos, dirty_rects: Res<DirtyRects>) {
let (dirty_update, render_update) = (&dirty_rects.new, &dirty_rects.render);

let mut i = false;
for rect in [dirty_update, render_update] {
for (chunk_pos, rect) in rect {
let size =
UVec2::new(rect.max.x - rect.min.x + 1, rect.max.y - rect.min.y + 1).as_vec2();
let position = ivec2(
chunk_pos.x * CHUNK_LENGHT as i32 + rect.min.x as i32,
-(chunk_pos.y * CHUNK_LENGHT as i32) - rect.min.y as i32,
)
.as_vec2()
+ vec2(size.x, -(size.y)) / 2.;

if !i {
gizmos.rect_2d_gradient(position, 0., size);
} else {
gizmos.rect_2d(position, 0., size, Color::rgba(1., 0.25, 1., 1.));
}
}
i = true;
}
}

pub fn _render_dirty_rects_image(mut commands: Commands, dirty_rects: Res<DirtyRects>) {
let (dirty_update, render_update) = (&dirty_rects.new, &dirty_rects.render);

let mut i = 0.;
Expand Down Expand Up @@ -128,28 +153,7 @@ pub fn render_dirty_rects(mut commands: Commands, dirty_rects: Res<DirtyRects>)
}
}

fn render_actors(mut commands: Commands, actors: Query<&Actor>) {
for actor in actors.iter() {
commands
.spawn(SpriteBundle {
sprite: Sprite {
color: Color::rgba(0.75, 0.25, 0.25, 0.2),
custom_size: Some(Vec2::new(actor.width as f32, actor.height as f32)),
anchor: Anchor::TopLeft,
..default()
},
transform: Transform::from_translation(Vec3::new(
actor.pos.x as f32,
-actor.pos.y as f32,
1.,
)),
..default()
})
.insert(DeleteImage);
}
}

pub fn delete_image(mut commands: Commands, debug_images: Query<Entity, With<DeleteImage>>) {
pub fn _delete_image(mut commands: Commands, debug_images: Query<Entity, With<DeleteImage>>) {
for image in debug_images.iter() {
commands.entity(image).despawn();
}
Expand All @@ -158,6 +162,18 @@ pub fn delete_image(mut commands: Commands, debug_images: Query<Entity, With<Del
#[derive(Component)]
pub struct DeleteImage;

fn render_actors(mut gizmos: Gizmos, actors: Query<&Actor>) {
for actor in actors.iter() {
gizmos.rect_2d(
vec2(actor.pos.x as f32, -actor.pos.y as f32)
+ vec2(actor.width as f32, -(actor.height as f32)) / 2.,
0.,
Vec2::new(actor.width as f32, actor.height as f32),
Color::rgba(0.75, 0.25, 0.25, 0.2),
);
}
}

fn _camera(keys: Res<Input<KeyCode>>, mut camera_q: Query<&mut Transform, With<Camera>>) {
let x = -(keys.pressed(KeyCode::A) as u8 as f32) + keys.pressed(KeyCode::D) as u8 as f32;
let y = -(keys.pressed(KeyCode::S) as u8 as f32) + keys.pressed(KeyCode::W) as u8 as f32;
Expand All @@ -166,21 +182,47 @@ fn _camera(keys: Res<Input<KeyCode>>, mut camera_q: Query<&mut Transform, With<C
camera_q.single_mut().translation += v * _CAMERA_SPEED;
}

pub trait Rect2dGradient {
fn rect_2d_gradient(&mut self, position: Vec2, rotation: f32, size: Vec2);
}

impl<'a> Rect2dGradient for Gizmos<'a> {
fn rect_2d_gradient(&mut self, position: Vec2, rotation: f32, size: Vec2) {
let rotation = Mat2::from_angle(rotation);
let [tl, tr, br, bl] = rect_inner(size).map(|vec2| position + rotation * vec2);
self.linestrip_gradient_2d([
(tl, Color::RED),
(tr, Color::GREEN),
(br, Color::BLUE),
(bl, Color::PURPLE),
(tl, Color::GREEN),
]);
}
}

fn rect_inner(size: Vec2) -> [Vec2; 4] {
let half_size = size / 2.;
let tl = Vec2::new(-half_size.x, half_size.y);
let tr = Vec2::new(half_size.x, half_size.y);
let bl = Vec2::new(-half_size.x, -half_size.y);
let br = Vec2::new(half_size.x, -half_size.y);
[tl, tr, br, bl]
}

pub struct DebugPlugin;
impl Plugin for DebugPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Update,
(
render_dirty_rects.after(update_actors),
render_dirty_rects_gizmos.after(update_actors),
brush.after(chunk_manager_update),
render_actors.after(update_actors),
prev_mpos.after(brush),
//_camera
)
.run_if(in_state(GameState::Game)),
)
.add_systems(PreUpdate, delete_image.run_if(in_state(GameState::Game)))
.add_plugins(WorldInspectorPlugin::new())
.add_plugins(RapierDebugRenderPlugin::default())
//Frame on console
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fn main() {
ParticlesPlugin,
MaterialsPlugin,
CameraPlugin,
//RigidbodyPlugin,
RigidbodyPlugin,
))
.add_plugins((
RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(6.),
Expand Down
Loading

0 comments on commit 4f95599

Please sign in to comment.