|
| 1 | +# Plugin: Collider |
| 2 | + |
| 3 | +The Collider plugin facilitates the management of colliders within the ECS framework, enabling efficient collision detection and response in a 3D environment. |
| 4 | + |
| 5 | +## Dependencies |
| 6 | +- `bevy`: The primary game engine used for rendering and managing game state. |
| 7 | +- `bevy_rapier3d`: Provides physics capabilities including collision detection and rigid body dynamics. |
| 8 | + |
| 9 | +## Mermaid Diagram |
| 10 | +```mermaid |
| 11 | +graph TD |
| 12 | + subgraph Components |
| 13 | + BlockCollider[BlockCollider] |
| 14 | + end |
| 15 | +
|
| 16 | + subgraph Systems |
| 17 | + SetupCollidersSystem[setup_coliders_system] |
| 18 | + HandleColliderUpdateEventsSystem[handle_collider_update_events_system] |
| 19 | + end |
| 20 | +
|
| 21 | + subgraph Events |
| 22 | + ColliderUpdateEvent[ColliderUpdateEvent] |
| 23 | + end |
| 24 | +
|
| 25 | + subgraph Resources |
| 26 | + ChunkManager[ChunkManager] |
| 27 | + end |
| 28 | +
|
| 29 | + BlockCollider -->|has| SetupCollidersSystem |
| 30 | + ColliderUpdateEvent -->|triggers| HandleColliderUpdateEventsSystem |
| 31 | + HandleColliderUpdateEventsSystem -->|reads from| ChunkManager |
| 32 | +``` |
| 33 | + |
| 34 | +## Components |
| 35 | +- `BlockCollider`: Represents a collider with a defined relative position, used to detect collisions within the game world. |
| 36 | + |
| 37 | +## Resources |
| 38 | +- `ChunkManager`: Manages chunks of the game world, facilitating retrieval and updates of block states for collision calculations. |
| 39 | + |
| 40 | +## Systems |
| 41 | +- **Setup**: |
| 42 | + - `setup_coliders_system`: Initializes the collider grid by spawning BlockCollider entities based on a predefined size. |
| 43 | +- **Update**: |
| 44 | + - `handle_collider_update_events_system`: Responds to ColliderUpdateEvents to update collider positions based on world changes. |
| 45 | + |
| 46 | +## Context |
| 47 | +- Includes files from the project's plugin directory. |
| 48 | +- Incorporates [`prelude.rs`](https://github.com/CuddlyBunion341/hello-bevy/blob/main/src/client/prelude.rs) and networking systems specific to the plugin. |
| 49 | + |
| 50 | +## Collected Source Files |
| 51 | +- [events.rs](https://github.com/CuddlyBunion341/hello-bevy/blob/main/src/client/collider/events.rs) |
| 52 | +- [systems.rs](https://github.com/CuddlyBunion341/hello-bevy/blob/main/src/client/collider/systems.rs) |
| 53 | +- [mod.rs](https://github.com/CuddlyBunion341/hello-bevy/blob/main/src/client/collider/mod.rs) |
| 54 | +- [components.rs](https://github.com/CuddlyBunion341/hello-bevy/blob/main/src/client/collider/components.rs) |
| 55 | + |
| 56 | +## Source Code Content |
| 57 | + |
| 58 | +```rs |
| 59 | +// ---- File: src/client/collider/events.rs ---- |
| 60 | +use crate::prelude::*; |
| 61 | + |
| 62 | +#[derive(Event)] |
| 63 | +pub struct ColliderUpdateEvent { |
| 64 | + pub grid_center_position: [f32; 3], |
| 65 | +} |
| 66 | + |
| 67 | +// ---- File: src/client/collider/systems.rs ---- |
| 68 | +use crate::prelude::*; |
| 69 | + |
| 70 | +static COLLIDER_GRID_SIZE: u32 = 3; |
| 71 | +static COLLIDER_RESTING_POSITION: Vec3 = Vec3::ZERO; |
| 72 | + |
| 73 | +pub fn setup_coliders_system(mut commands: Commands) { |
| 74 | + let collider_range = 0..COLLIDER_GRID_SIZE; |
| 75 | + |
| 76 | + for x in collider_range.clone() { |
| 77 | + for y in collider_range.clone() { |
| 78 | + for z in collider_range.clone() { |
| 79 | + commands |
| 80 | + .spawn(Collider::cuboid(0.5, 0.5, 0.5)) |
| 81 | + .insert(TransformBundle::from(Transform::from_xyz( |
| 82 | + x as f32, y as f32, z as f32, |
| 83 | + ))) |
| 84 | + .insert(collider_components::BlockCollider { |
| 85 | + relative_position: Vec3 { |
| 86 | + x: x as f32, |
| 87 | + y: y as f32, |
| 88 | + z: z as f32, |
| 89 | + }, |
| 90 | + }); |
| 91 | + } |
| 92 | + } |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +pub fn handle_collider_update_events_system( |
| 97 | + mut collider_grid_events: EventReader<collider_events::ColliderUpdateEvent>, |
| 98 | + mut query: Query<(&mut Transform, &collider_components::BlockCollider)>, |
| 99 | + mut chunk_manager: ResMut<terrain_resources::ChunkManager>, |
| 100 | +) { |
| 101 | + for event in collider_grid_events.read() { |
| 102 | + let event_position = Vec3::new( |
| 103 | + event.grid_center_position[0], |
| 104 | + event.grid_center_position[1], |
| 105 | + event.grid_center_position[2], |
| 106 | + ) |
| 107 | + .floor(); |
| 108 | + for (mut transform, collider) in query.iter_mut() { |
| 109 | + let relative_position = collider.relative_position; |
| 110 | + let collider_position = (event_position + relative_position).floor(); |
| 111 | + let block = chunk_manager.get_block(collider_position); |
| 112 | + |
| 113 | + match block { |
| 114 | + Some(block) => { |
| 115 | + if block != BlockId::Air { |
| 116 | + transform.translation = collider_position + 0.5; |
| 117 | + } else { |
| 118 | + transform.translation = COLLIDER_RESTING_POSITION; |
| 119 | + } |
| 120 | + } |
| 121 | + None => { |
| 122 | + transform.translation = COLLIDER_RESTING_POSITION; |
| 123 | + } |
| 124 | + } |
| 125 | + } |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | +#[cfg(test)] |
| 130 | +mod tests { |
| 131 | + use collider_events::ColliderUpdateEvent; |
| 132 | + |
| 133 | + use super::*; |
| 134 | + |
| 135 | + fn setup_app() -> App { |
| 136 | + let mut app = App::new(); |
| 137 | + app.add_plugins(MinimalPlugins); |
| 138 | + app |
| 139 | + } |
| 140 | + |
| 141 | + #[test] |
| 142 | + fn test_setup_coliders_system() { |
| 143 | + let mut app = setup_app(); |
| 144 | + app.add_systems(Startup, setup_coliders_system); |
| 145 | + |
| 146 | + app.update(); |
| 147 | + |
| 148 | + let mut colliders_query = app.world.query::<&collider_components::BlockCollider>(); |
| 149 | + let colliders_count = colliders_query.iter(&app.world).count(); |
| 150 | + |
| 151 | + assert_eq!(colliders_count, 3 * 3 * 3); |
| 152 | + } |
| 153 | + |
| 154 | + #[test] |
| 155 | + fn test_handle_collider_update_events_system() { |
| 156 | + let mut app = App::new(); |
| 157 | + |
| 158 | + app.add_event::<collider_events::ColliderUpdateEvent>(); |
| 159 | + app.add_systems(Update, handle_collider_update_events_system); |
| 160 | + app.insert_resource(terrain_resources::ChunkManager::new()); |
| 161 | + |
| 162 | + app.world.spawn(( |
| 163 | + Transform { |
| 164 | + translation: Vec3 { x: 0.0, y: 0.0, z: 0.0 }, |
| 165 | + ..Default::default() |
| 166 | + }, |
| 167 | + collider_components::BlockCollider { |
| 168 | + relative_position: Vec3 { x: 1.0, y: 2.0, z: 3.0 }, |
| 169 | + }, |
| 170 | + )); |
| 171 | + |
| 172 | + let block = BlockId::Dirt; |
| 173 | + let mut resource = app.world.get_resource_mut::<terrain_resources::ChunkManager>().unwrap(); |
| 174 | + |
| 175 | + let chunks = terrain_resources::ChunkManager::instantiate_chunks(Vec3 { x: 0.0, y: 0.0, z: 0.0 }, 1); |
| 176 | + |
| 177 | + resource.insert_chunks(chunks); |
| 178 | + resource.set_block(Vec3 { x: 6.0, y: 7.0, z: 8.0 }, block); |
| 179 | + |
| 180 | + app.world.send_event(ColliderUpdateEvent { grid_center_position: [5.0, 5.0, 5.0] }); |
| 181 | + |
| 182 | + app.update(); |
| 183 | + |
| 184 | + let mut collider_query = app.world.query::<(&Transform, &collider_components::BlockCollider)>(); |
| 185 | + |
| 186 | + let (collider_transform, _) = collider_query.single(&app.world); |
| 187 | + |
| 188 | + assert_eq!(Vec3 { x: 6.5, y: 7.5, z: 8.5 }, collider_transform.translation); |
| 189 | + } |
| 190 | +} |
| 191 | + |
| 192 | +// ---- File: src/client/collider/mod.rs ---- |
| 193 | +pub mod components; |
| 194 | +pub mod events; |
| 195 | +pub mod systems; |
| 196 | + |
| 197 | +use crate::prelude::*; |
| 198 | + |
| 199 | +pub struct ColliderPlugin; |
| 200 | + |
| 201 | +impl Plugin for ColliderPlugin { |
| 202 | + fn build(&self, app: &mut App) { |
| 203 | + app.add_systems(Startup, collider_systems::setup_coliders_system); |
| 204 | + app.add_event::<collider_events::ColliderUpdateEvent>(); |
| 205 | + app.add_systems(Update, collider_systems::handle_collider_update_events_system); |
| 206 | + } |
| 207 | +} |
| 208 | + |
| 209 | +// ---- File: src/client/collider/components.rs ---- |
| 210 | +use crate::prelude::*; |
| 211 | + |
| 212 | +#[derive(Component)] |
| 213 | +pub struct BlockCollider { |
| 214 | + pub relative_position: Vec3, |
| 215 | +} |
| 216 | +``` |
0 commit comments