goud_engine/sdk/game/
instance.rs1use crate::core::error::GoudResult;
4use crate::ecs::{Component, Entity, World};
5
6#[cfg(feature = "native")]
7use crate::ecs::InputManager;
8#[cfg(feature = "native")]
9use crate::libs::graphics::backend::opengl::OpenGLBackend;
10#[cfg(feature = "native")]
11use crate::libs::graphics::renderer3d::Renderer3D;
12#[cfg(feature = "native")]
13use crate::libs::platform::PlatformBackend;
14#[cfg(feature = "native")]
15use crate::rendering::sprite_batch::SpriteBatch;
16
17use crate::sdk::entity_builder::EntityBuilder;
18use crate::sdk::game_config::{GameConfig, GameContext};
19
20pub struct GoudGame {
35 pub(crate) world: World,
37
38 pub(crate) config: GameConfig,
40
41 pub(crate) context: GameContext,
43
44 pub(crate) initialized: bool,
46
47 #[cfg(feature = "native")]
52 pub(crate) platform: Option<Box<dyn PlatformBackend>>,
53
54 #[cfg(feature = "native")]
56 pub(crate) render_backend: Option<OpenGLBackend>,
57
58 #[cfg(feature = "native")]
60 pub(crate) input_manager: InputManager,
61
62 #[cfg(feature = "native")]
64 pub(crate) sprite_batch: Option<SpriteBatch<OpenGLBackend>>,
65
66 #[cfg(feature = "native")]
68 pub(crate) asset_server: Option<crate::assets::AssetServer>,
69
70 #[cfg(feature = "native")]
72 pub(crate) renderer_3d: Option<Renderer3D>,
73
74 #[cfg(feature = "native")]
76 pub(crate) immediate_state: Option<crate::sdk::rendering::ImmediateRenderState>,
77}
78
79impl GoudGame {
80 pub fn new(config: GameConfig) -> GoudResult<Self> {
86 let window_size = (config.width, config.height);
87 Ok(Self {
88 world: World::new(),
89 config,
90 context: GameContext::new(window_size),
91 initialized: false,
92 #[cfg(feature = "native")]
93 platform: None,
94 #[cfg(feature = "native")]
95 render_backend: None,
96 #[cfg(feature = "native")]
97 input_manager: InputManager::default(),
98 #[cfg(feature = "native")]
99 sprite_batch: None,
100 #[cfg(feature = "native")]
101 asset_server: None,
102 #[cfg(feature = "native")]
103 renderer_3d: None,
104 #[cfg(feature = "native")]
105 immediate_state: None,
106 })
107 }
108
109 pub fn default_game() -> GoudResult<Self> {
111 Self::new(GameConfig::default())
112 }
113
114 #[cfg(feature = "native")]
123 pub fn with_platform(config: GameConfig) -> GoudResult<Self> {
124 use crate::libs::platform::glfw_platform::GlfwPlatform;
125 use crate::libs::platform::WindowConfig;
126
127 let window_config = WindowConfig {
128 width: config.width,
129 height: config.height,
130 title: config.title.clone(),
131 vsync: config.vsync,
132 resizable: config.resizable,
133 };
134
135 let platform = GlfwPlatform::new(&window_config)?;
136 let window_size = (config.width, config.height);
137
138 Ok(Self {
139 world: World::new(),
140 config,
141 context: GameContext::new(window_size),
142 initialized: false,
143 platform: Some(Box::new(platform)),
144 render_backend: None,
145 input_manager: InputManager::default(),
146 sprite_batch: None,
147 asset_server: None,
148 renderer_3d: None,
149 immediate_state: None,
150 })
151 }
152
153 #[inline]
155 pub fn world(&self) -> &World {
156 &self.world
157 }
158
159 #[inline]
161 pub fn world_mut(&mut self) -> &mut World {
162 &mut self.world
163 }
164
165 #[inline]
167 pub fn spawn(&mut self) -> EntityBuilder<'_> {
168 EntityBuilder::new(&mut self.world)
169 }
170
171 #[inline]
173 pub fn spawn_empty(&mut self) -> Entity {
174 self.world.spawn_empty()
175 }
176
177 #[inline]
179 pub fn spawn_batch(&mut self, count: usize) -> Vec<Entity> {
180 self.world.spawn_batch(count)
181 }
182
183 #[inline]
185 pub fn despawn(&mut self, entity: Entity) -> bool {
186 self.world.despawn(entity)
187 }
188
189 #[inline]
191 pub fn get<T: Component>(&self, entity: Entity) -> Option<&T> {
192 self.world.get::<T>(entity)
193 }
194
195 #[inline]
197 pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<&mut T> {
198 self.world.get_mut::<T>(entity)
199 }
200
201 #[inline]
203 pub fn insert<T: Component>(&mut self, entity: Entity, component: T) {
204 self.world.insert(entity, component);
205 }
206
207 #[inline]
209 pub fn remove<T: Component>(&mut self, entity: Entity) -> Option<T> {
210 self.world.remove::<T>(entity)
211 }
212
213 #[inline]
215 pub fn has<T: Component>(&self, entity: Entity) -> bool {
216 self.world.has::<T>(entity)
217 }
218
219 #[inline]
221 pub fn entity_count(&self) -> usize {
222 self.world.entity_count()
223 }
224
225 #[inline]
227 pub fn is_alive(&self, entity: Entity) -> bool {
228 self.world.is_alive(entity)
229 }
230
231 #[inline]
233 pub fn config(&self) -> &GameConfig {
234 &self.config
235 }
236
237 #[inline]
239 pub fn title(&self) -> &str {
240 &self.config.title
241 }
242
243 #[inline]
245 pub fn window_size(&self) -> (u32, u32) {
246 (self.config.width, self.config.height)
247 }
248
249 pub fn run<F>(&mut self, mut update: F)
251 where
252 F: FnMut(&mut GameContext, &mut World),
253 {
254 self.initialized = true;
255
256 let frame_time = if self.config.target_fps > 0 {
258 1.0 / self.config.target_fps as f32
259 } else {
260 1.0 / 60.0 };
262
263 while self.context.is_running() {
266 self.context.update(frame_time);
267 update(&mut self.context, &mut self.world);
268
269 if self.context.frame_count() > 10000 {
271 break;
272 }
273 }
274 }
275
276 pub fn update_frame<F>(&mut self, delta_time: f32, mut update: F)
278 where
279 F: FnMut(&mut GameContext, &mut World),
280 {
281 self.context.update(delta_time);
282 update(&mut self.context, &mut self.world);
283 }
284
285 #[inline]
287 pub fn frame_count(&self) -> u64 {
288 self.context.frame_count()
289 }
290
291 #[inline]
293 pub fn total_time(&self) -> f32 {
294 self.context.total_time()
295 }
296
297 #[inline]
299 pub fn fps(&self) -> f32 {
300 self.context.fps()
301 }
302
303 #[inline]
305 pub fn is_initialized(&self) -> bool {
306 self.initialized
307 }
308}
309
310impl Default for GoudGame {
311 fn default() -> Self {
312 Self::new(GameConfig::default()).expect("Failed to create default GoudGame")
313 }
314}
315
316impl std::fmt::Debug for GoudGame {
317 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318 f.debug_struct("GoudGame")
319 .field("config", &self.config)
320 .field("entity_count", &self.world.entity_count())
321 .field("initialized", &self.initialized)
322 .finish()
323 }
324}