Skip to main content

goud_engine/ecs/app/
physics_plugins.rs

1//! Physics plugins for 2D and 3D ECS integration.
2//!
3//! These plugins register the necessary resources for physics synchronization.
4//! The physics step systems must be added separately because they require
5//! ownership of a [`PhysicsProvider`] instance, and [`Plugin::build`] takes
6//! `&self` (preventing move semantics).
7//!
8//! # Usage
9//!
10//! ```rust,ignore
11//! use goud_engine::ecs::app::{App, PhysicsPlugin2D};
12//! use goud_engine::ecs::schedule::CoreStage;
13//! use goud_engine::ecs::systems::PhysicsStepSystem2D;
14//! use goud_engine::core::providers::impls::NullPhysicsProvider;
15//!
16//! let mut app = App::new();
17//! app.add_plugin(PhysicsPlugin2D);
18//! app.add_system(
19//!     CoreStage::Update,
20//!     PhysicsStepSystem2D::new(Box::new(NullPhysicsProvider::new())),
21//! );
22//! ```
23
24use super::plugin::Plugin;
25use super::App;
26use crate::ecs::systems::physics_sync_2d::PhysicsHandleMap2D;
27use crate::ecs::systems::physics_sync_3d::PhysicsHandleMap3D;
28
29/// Plugin that registers 2D physics resources.
30///
31/// Inserts a default [`PhysicsHandleMap2D`] resource into the world.
32/// After adding this plugin, add a [`PhysicsStepSystem2D`] to the desired
33/// stage (typically [`CoreStage::Update`]).
34///
35/// [`PhysicsStepSystem2D`]: crate::ecs::systems::PhysicsStepSystem2D
36/// [`CoreStage::Update`]: crate::ecs::schedule::CoreStage::Update
37#[derive(Debug, Default, Clone)]
38pub struct PhysicsPlugin2D;
39
40impl Plugin for PhysicsPlugin2D {
41    fn build(&self, app: &mut App) {
42        app.insert_resource(PhysicsHandleMap2D::default());
43    }
44
45    fn name(&self) -> &'static str {
46        "PhysicsPlugin2D"
47    }
48}
49
50/// Plugin that registers 3D physics resources.
51///
52/// Inserts a default [`PhysicsHandleMap3D`] resource into the world.
53/// After adding this plugin, add a [`PhysicsStepSystem3D`] to the desired
54/// stage (typically [`CoreStage::Update`]).
55///
56/// [`PhysicsStepSystem3D`]: crate::ecs::systems::PhysicsStepSystem3D
57/// [`CoreStage::Update`]: crate::ecs::schedule::CoreStage::Update
58#[derive(Debug, Default, Clone)]
59pub struct PhysicsPlugin3D;
60
61impl Plugin for PhysicsPlugin3D {
62    fn build(&self, app: &mut App) {
63        app.insert_resource(PhysicsHandleMap3D::default());
64    }
65
66    fn name(&self) -> &'static str {
67        "PhysicsPlugin3D"
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_physics_plugin_2d_build() {
77        let mut app = App::new();
78        app.add_plugin(PhysicsPlugin2D);
79        assert!(app.world().get_resource::<PhysicsHandleMap2D>().is_some());
80    }
81
82    #[test]
83    fn test_physics_plugin_2d_name() {
84        let plugin = PhysicsPlugin2D;
85        assert_eq!(plugin.name(), "PhysicsPlugin2D");
86    }
87
88    #[test]
89    fn test_physics_plugin_3d_build() {
90        let mut app = App::new();
91        app.add_plugin(PhysicsPlugin3D);
92        assert!(app.world().get_resource::<PhysicsHandleMap3D>().is_some());
93    }
94
95    #[test]
96    fn test_physics_plugin_3d_name() {
97        let plugin = PhysicsPlugin3D;
98        assert_eq!(plugin.name(), "PhysicsPlugin3D");
99    }
100
101    #[test]
102    fn test_both_plugins_can_coexist() {
103        let mut app = App::new();
104        app.add_plugin(PhysicsPlugin2D);
105        app.add_plugin(PhysicsPlugin3D);
106        assert!(app.world().get_resource::<PhysicsHandleMap2D>().is_some());
107        assert!(app.world().get_resource::<PhysicsHandleMap3D>().is_some());
108    }
109
110    #[test]
111    fn test_duplicate_plugin_ignored() {
112        let mut app = App::new();
113        app.add_plugin(PhysicsPlugin2D);
114        // Second add should be silently ignored (no panic).
115        app.add_plugin(PhysicsPlugin2D);
116    }
117}