Skip to main content

euv_engine/scene/
impl.rs

1use super::*;
2
3/// Implements static scene creation for `SceneManager`.
4impl SceneManager {
5    /// Creates a new `SceneRc` wrapping the given scene in a reference-counted cell.
6    ///
7    /// # Arguments
8    ///
9    /// - `T: Scene + 'static` - The concrete scene type.
10    ///
11    /// # Returns
12    ///
13    /// - `SceneRc` - The wrapped scene.
14    pub fn create_scene<T>(scene: T) -> SceneRc
15    where
16        T: Scene + 'static,
17    {
18        // `EngineCell<T: ?Sized>` accepts `dyn Scene` directly,
19        // so we do not need an intermediate `Box`.
20        Rc::new(EngineCell::new(scene))
21    }
22}
23
24/// Implements scene registration and lifecycle management for `SceneManager`.
25impl SceneManager {
26    /// Registers a scene under the given name.
27    ///
28    /// # Arguments
29    ///
30    /// - `String` - The name to register the scene under.
31    /// - `SceneRc` - The scene to register.
32    pub fn register(&mut self, name: String, scene: SceneRc) {
33        self.get_mut_scenes().insert(name, scene);
34    }
35
36    /// Unregisters and removes the scene with the given name.
37    ///
38    /// # Arguments
39    ///
40    /// - `N: AsRef<str>` - The name of the scene to remove.
41    pub fn unregister<N>(&mut self, name: N)
42    where
43        N: AsRef<str>,
44    {
45        self.get_mut_scenes().remove(name.as_ref());
46    }
47
48    /// Transitions to the scene with the given name.
49    ///
50    /// Calls `on_exit` on the current scene and `on_enter` on the new scene.
51    /// Returns `false` if no scene with the given name is registered.
52    ///
53    /// # Arguments
54    ///
55    /// - `N: AsRef<str>` - The name of the scene to switch to.
56    ///
57    /// # Returns
58    ///
59    /// - `bool` - True if the transition was successful.
60    pub fn switch_to<N>(&mut self, name: N) -> bool
61    where
62        N: AsRef<str>,
63    {
64        let name_ref: &str = name.as_ref();
65        if !self.get_scenes().contains_key(name_ref) {
66            return false;
67        }
68        let current_name: Option<String> = self.get_mut_current_scene_name().clone();
69        if let Some(name) = current_name.as_ref()
70            && let Some(current_scene) = self.get_scenes().get(name)
71        {
72            current_scene.get_mut().on_exit();
73        }
74        self.set_current_scene_name(Some(name_ref.to_string()));
75        if let Some(new_scene) = self.get_scenes().get(name_ref) {
76            new_scene.get_mut().on_enter();
77        }
78        true
79    }
80
81    /// Requests a deferred scene transition to be applied on the next update.
82    ///
83    /// # Arguments
84    ///
85    /// - `String` - The name of the scene to switch to.
86    pub fn request_transition(&mut self, name: String) {
87        self.set_pending_scene_name(Some(name));
88    }
89
90    /// Processes a pending scene transition if one was requested.
91    pub fn process_pending_transition(&mut self) {
92        let Some(name) = self.get_mut_pending_scene_name().take() else {
93            return;
94        };
95        self.switch_to(&name);
96    }
97
98    /// Calls `on_update` on the current scene.
99    ///
100    /// # Arguments
101    ///
102    /// - `f64` - The delta time in seconds.
103    pub fn update(&mut self, delta_time: f64) {
104        self.process_pending_transition();
105        // OPT 38: borrow the active scene name (Option<&String>) instead of
106        // cloning the whole String, then clone the `SceneRc` handle. The
107        // old form `get_mut_current_scene_name().clone()` paid for a full
108        // String heap allocation per frame on every scene, even though the
109        // name is only used as a HashMap key.
110        let Some(current_name) = self.try_get_current_scene_name().as_ref() else {
111            return;
112        };
113        let Some(scene) = self.get_scenes().get(current_name).cloned() else {
114            return;
115        };
116        scene.get_mut().on_update(delta_time);
117    }
118
119    /// Calls `on_render` on the current scene, recording into the shared draw list.
120    ///
121    /// The manager's reusable `DrawList` is cleared, filled by the scene, and
122    /// left populated for the caller to replay (e.g. via
123    /// `CanvasRenderer::replay`). Reusing the list avoids per-frame allocation.
124    ///
125    /// # Arguments
126    ///
127    /// - `&CanvasRenderingContext2d` - The canvas rendering context used to replay
128    ///   the recorded commands.
129    pub fn render(&mut self, context: &CanvasRenderingContext2d) {
130        let Some(current_name) = self.try_get_current_scene_name().as_ref() else {
131            return;
132        };
133        // Clone the `Rc` so the immutable borrow of the scenes map ends before
134        // we mutably borrow the draw list.
135        let Some(scene) = self.get_scenes().get(current_name).cloned() else {
136            return;
137        };
138        self.get_mut_draw_list().clear();
139        {
140            let draw_list: &mut DrawList = self.get_mut_draw_list();
141            scene.get().on_render(draw_list);
142        }
143        CanvasRenderer::replay_context(context, self.get_draw_list());
144    }
145
146    /// Returns whether a scene with the given name is registered.
147    ///
148    /// # Arguments
149    ///
150    /// - `N: AsRef<str>` - The scene name to check.
151    ///
152    /// # Returns
153    ///
154    /// - `bool` - True if the scene is registered.
155    pub fn has_scene<N>(&self, name: N) -> bool
156    where
157        N: AsRef<str>,
158    {
159        self.get_scenes().contains_key(name.as_ref())
160    }
161
162    /// Returns the name of the currently active scene.
163    ///
164    /// # Returns
165    ///
166    /// - `Option<&str>` - The current scene name, or `None`.
167    pub fn current_name(&self) -> Option<&str> {
168        self.try_get_current_scene_name().as_deref()
169    }
170}
171
172/// Forwards `SceneManager::update` through the [`Updatable`] trait so that
173/// scene managers can be driven alongside entities, animators, and physics
174/// worlds in a single homogeneous update loop.
175///
176/// The inherent [`SceneManager::update`] method is the canonical implementation;
177/// this impl exists purely for trait dispatch. The inherent call resolves
178/// first when both are in scope, so there is no recursion.
179impl Updatable for SceneManager {
180    /// Advances the simulation by `delta_time` seconds.
181    ///
182    /// # Arguments
183    ///
184    /// - `f64` - Seconds elapsed since the previous update.
185    fn update(&mut self, delta_time: f64) {
186        SceneManager::update(self, delta_time);
187    }
188}
189
190/// Implements `Default` for `SceneManager` as a new empty manager.
191impl Default for SceneManager {
192    /// Constructs a default [`SceneManager`] value.
193    ///
194    /// # Returns
195    ///
196    /// - `SceneManager` - A default-constructed instance with the documented initial state.
197    fn default() -> SceneManager {
198        SceneManager::new()
199    }
200}