feat(app): added the prefab interface to the app struct

This commit is contained in:
lisk77 2025-07-14 01:58:49 +02:00
parent e1597e6fa4
commit 7cf9f5bd29
4 changed files with 256 additions and 227 deletions

0
crates/comet_app/Cargo.toml Normal file → Executable file
View file

483
crates/comet_app/src/app.rs Normal file → Executable file
View file

@ -1,285 +1,314 @@
use comet_colors::{Color as ColorTrait, LinearRgba};
use comet_ecs::{
Camera2D, Color, Component, Entity, Render2D, Scene, Text, Transform2D, Transform3D,
};
use comet_input::keyboard::Key;
use comet_log::*;
use comet_renderer::renderer::Renderer;
use std::any::{type_name, Any, TypeId}; use std::any::{type_name, Any, TypeId};
use std::sync::Arc; use std::sync::Arc;
use comet_ecs::{Camera2D, Color, Component, Entity, Render2D, Scene, Text, Transform2D, Transform3D};
use winit::{
event::*,
event_loop::EventLoop,
window::{Icon, Window},
};
use comet_colors::{Color as ColorTrait, LinearRgba};
use comet_log::*;
use winit::dpi::LogicalSize; use winit::dpi::LogicalSize;
use winit::{
event::*,
event_loop::EventLoop,
window::{Icon, Window},
};
use winit_input_helper::WinitInputHelper as InputManager; use winit_input_helper::WinitInputHelper as InputManager;
use comet_input::keyboard::Key;
use comet_renderer::renderer::Renderer;
use comet_structs::ComponentSet;
pub enum ApplicationType { pub enum ApplicationType {
App2D, App2D,
App3D App3D,
} }
pub struct App { pub struct App {
title: String, title: String,
icon: Option<Icon>, icon: Option<Icon>,
size: Option<LogicalSize<u32>>, size: Option<LogicalSize<u32>>,
clear_color: Option<LinearRgba>, clear_color: Option<LinearRgba>,
input_manager: InputManager, input_manager: InputManager,
delta_time: f32, delta_time: f32,
update_timer: f32, update_timer: f32,
game_state: Option<Box<dyn Any>>, game_state: Option<Box<dyn Any>>,
scene: Scene, scene: Scene,
fullscreen: bool, fullscreen: bool,
should_quit: bool should_quit: bool,
} }
impl App { impl App {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
title: "Untitled".to_string(), title: "Untitled".to_string(),
icon: None, icon: None,
size: None, size: None,
clear_color: None, clear_color: None,
input_manager: InputManager::new(), input_manager: InputManager::new(),
delta_time: 0.0, delta_time: 0.0,
update_timer: 0.0166667, update_timer: 0.0166667,
game_state: None, game_state: None,
scene: Scene::new(), scene: Scene::new(),
fullscreen: false, fullscreen: false,
should_quit: false should_quit: false,
} }
} }
pub fn with_title(mut self, title: impl Into<String>) -> Self { pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = title.into(); self.title = title.into();
self self
} }
pub fn with_icon(mut self, path: impl AsRef<std::path::Path>) -> Self { pub fn with_icon(mut self, path: impl AsRef<std::path::Path>) -> Self {
self.icon = Self::load_icon(path.as_ref()); self.icon = Self::load_icon(path.as_ref());
self self
} }
pub fn with_size(mut self, width: u32, height: u32) -> Self { pub fn with_size(mut self, width: u32, height: u32) -> Self {
self.size = Some(LogicalSize::new(width, height)); self.size = Some(LogicalSize::new(width, height));
self self
} }
pub fn with_clear_color(mut self, clear_color: impl ColorTrait) -> Self { pub fn with_clear_color(mut self, clear_color: impl ColorTrait) -> Self {
self.clear_color = Some(clear_color.to_linear()); self.clear_color = Some(clear_color.to_linear());
self self
} }
pub fn with_game_state(mut self, game_state: impl Any + 'static) -> Self { pub fn with_game_state(mut self, game_state: impl Any + 'static) -> Self {
self.game_state = Some(Box::new(game_state)); self.game_state = Some(Box::new(game_state));
self self
} }
pub fn with_preset(mut self, preset: ApplicationType) -> Self { pub fn with_preset(mut self, preset: ApplicationType) -> Self {
match preset { match preset {
ApplicationType::App2D => { ApplicationType::App2D => {
info!("Creating 2D app!"); info!("Creating 2D app!");
self.scene.register_component::<Transform2D>(); self.scene.register_component::<Transform2D>();
self.scene.register_component::<Render2D>(); self.scene.register_component::<Render2D>();
self.scene.register_component::<Camera2D>(); self.scene.register_component::<Camera2D>();
self.scene.register_component::<Text>(); self.scene.register_component::<Text>();
}, }
ApplicationType::App3D => { ApplicationType::App3D => {
info!("Creating 3D app!"); info!("Creating 3D app!");
self.scene.register_component::<Transform3D>() self.scene.register_component::<Transform3D>()
} }
}; };
self self
} }
fn load_icon(path: &std::path::Path) -> Option<Icon> { fn load_icon(path: &std::path::Path) -> Option<Icon> {
let image = match image::open(path) { let image = match image::open(path) {
Ok(image) => image, Ok(image) => image,
Err(_) => { Err(_) => {
error!("Failed loading icon {}", path.display()); error!("Failed loading icon {}", path.display());
return None; return None;
} }
}; };
let rgba_image = image.to_rgba8(); let rgba_image = image.to_rgba8();
let (width, height) = rgba_image.dimensions(); let (width, height) = rgba_image.dimensions();
Some(Icon::from_rgba(rgba_image.into_raw(), width, height).unwrap()) Some(Icon::from_rgba(rgba_image.into_raw(), width, height).unwrap())
} }
pub fn game_state<T: 'static>(&self) -> Option<&T> { pub fn game_state<T: 'static>(&self) -> Option<&T> {
self.game_state.as_ref()?.downcast_ref::<T>() self.game_state.as_ref()?.downcast_ref::<T>()
} }
pub fn game_state_mut<T: 'static>(&mut self) -> Option<&mut T> { pub fn game_state_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.game_state.as_mut()?.downcast_mut::<T>() self.game_state.as_mut()?.downcast_mut::<T>()
} }
pub fn scene(&self) -> &Scene { pub fn scene(&self) -> &Scene {
&self.scene &self.scene
} }
pub fn input_manager(&self) -> &InputManager { pub fn input_manager(&self) -> &InputManager {
&self.input_manager &self.input_manager
} }
pub fn key_pressed(&self, key: Key) -> bool { pub fn key_pressed(&self, key: Key) -> bool {
self.input_manager.key_pressed(key) self.input_manager.key_pressed(key)
} }
pub fn key_held(&self, key: Key) -> bool { pub fn key_held(&self, key: Key) -> bool {
self.input_manager.key_held(key) self.input_manager.key_held(key)
} }
pub fn key_released(&self, key: Key) -> bool { pub fn key_released(&self, key: Key) -> bool {
self.input_manager.key_released(key) self.input_manager.key_released(key)
} }
pub fn new_entity(&mut self) -> usize{ pub fn new_entity(&mut self) -> usize {
self.scene.new_entity() as usize self.scene.new_entity() as usize
} }
pub fn delete_entity(&mut self, entity_id: usize) { pub fn delete_entity(&mut self, entity_id: usize) {
self.scene.delete_entity(entity_id) self.scene.delete_entity(entity_id)
} }
pub fn get_entity(&self, entity_id: usize) -> Option<&Entity> { pub fn get_entity(&self, entity_id: usize) -> Option<&Entity> {
self.scene.get_entity(entity_id) self.scene.get_entity(entity_id)
} }
pub fn get_entity_mut(&mut self, entity_id: usize) -> Option<&mut Entity> { pub fn get_entity_mut(&mut self, entity_id: usize) -> Option<&mut Entity> {
self.scene.get_entity_mut(entity_id) self.scene.get_entity_mut(entity_id)
} }
pub fn register_component<C: Component>(&mut self) { pub fn register_component<C: Component>(&mut self) {
self.scene.register_component::<C>() self.scene.register_component::<C>()
} }
pub fn deregister_component<C: Component>(&mut self) { pub fn deregister_component<C: Component>(&mut self) {
self.scene.deregister_component::<C>() self.scene.deregister_component::<C>()
} }
pub fn add_component<C: Component>(&mut self, entity_id: usize, component: C) { pub fn add_component<C: Component>(&mut self, entity_id: usize, component: C) {
self.scene.add_component(entity_id, component) self.scene.add_component(entity_id, component)
} }
pub fn remove_component<C: Component>(&mut self, entity_id: usize) { pub fn remove_component<C: Component>(&mut self, entity_id: usize) {
self.scene.remove_component::<C>(entity_id) self.scene.remove_component::<C>(entity_id)
} }
pub fn get_component<C: Component>(&self, entity_id: usize) -> Option<&C> { pub fn get_component<C: Component>(&self, entity_id: usize) -> Option<&C> {
self.scene.get_component::<C>(entity_id) self.scene.get_component::<C>(entity_id)
} }
pub fn get_component_mut<C: Component>(&mut self, entity_id: usize) -> Option<&mut C> { pub fn get_component_mut<C: Component>(&mut self, entity_id: usize) -> Option<&mut C> {
self.scene.get_component_mut::<C>(entity_id) self.scene.get_component_mut::<C>(entity_id)
} }
pub fn get_entities_with(&self, components: Vec<TypeId>) -> Vec<usize> { pub fn get_entities_with(&self, components: Vec<TypeId>) -> Vec<usize> {
self.scene.get_entities_with(components) self.scene.get_entities_with(components)
} }
pub fn delete_entities_with(&mut self, components: Vec<TypeId>) { pub fn delete_entities_with(&mut self, components: Vec<TypeId>) {
self.scene.delete_entities_with(components) self.scene.delete_entities_with(components)
} }
pub fn foreach<C: Component, K: Component>(&mut self, func: fn(&mut C,&mut K)) { pub fn foreach<C: Component, K: Component>(&mut self, func: fn(&mut C, &mut K)) {
self.scene.foreach::<C,K>(func) self.scene.foreach::<C, K>(func)
} }
pub fn has<C: Component>(&self, entity_id: usize) -> bool { pub fn has<C: Component>(&self, entity_id: usize) -> bool {
self.scene.has::<C>(entity_id) self.scene.has::<C>(entity_id)
} }
pub fn quit(&mut self) { pub fn register_prefab(&mut self, name: &str, factory: comet_ecs::PrefabFactory) {
self.should_quit = true; self.scene.register_prefab(name, factory)
} }
pub fn dt(&self) -> f32 { pub fn spawn_prefab(&mut self, name: &str) -> Option<usize> {
self.update_timer self.scene.spawn_prefab(name)
} }
/// Sets the amount of times the game is updated per second pub fn has_prefab(&self, name: &str) -> bool {
pub fn set_update_rate(&mut self, update_rate: u32) { self.scene.has_prefab(name)
if update_rate == 0 { }
self.update_timer = f32::INFINITY;
return;
}
self.update_timer = 1.0/update_rate as f32;
}
fn create_window(app_title: String, app_icon: &Option<Icon>, window_size: &Option<LogicalSize<u32>>, event_loop: &EventLoop<()>) -> Window { pub fn quit(&mut self) {
let winit_window = winit::window::WindowBuilder::new() self.should_quit = true;
.with_title(app_title); }
let winit_window = if let Some(icon) = app_icon.clone() { pub fn dt(&self) -> f32 {
winit_window.with_window_icon(Some(icon)) self.update_timer
} else { }
winit_window
};
let winit_window = if let Some(size) = window_size.clone() { /// Sets the amount of times the game is updated per second
winit_window.with_inner_size(size) pub fn set_update_rate(&mut self, update_rate: u32) {
} else { if update_rate == 0 {
winit_window self.update_timer = f32::INFINITY;
}; return;
}
self.update_timer = 1.0 / update_rate as f32;
}
winit_window.build(event_loop).unwrap() fn create_window(
} app_title: String,
app_icon: &Option<Icon>,
window_size: &Option<LogicalSize<u32>>,
event_loop: &EventLoop<()>,
) -> Window {
let winit_window = winit::window::WindowBuilder::new().with_title(app_title);
pub fn run<R: Renderer>(mut self, setup: fn(&mut App, &mut R), update: fn(&mut App, &mut R, f32)) { let winit_window = if let Some(icon) = app_icon.clone() {
info!("Starting up {}!", self.title); winit_window.with_window_icon(Some(icon))
} else {
winit_window
};
pollster::block_on(async { let winit_window = if let Some(size) = window_size.clone() {
let event_loop = EventLoop::new().unwrap(); winit_window.with_inner_size(size)
let window = Arc::new(Self::create_window(self.title.clone(), &self.icon, &self.size ,&event_loop)); } else {
let mut renderer = R::new(window.clone(), self.clear_color.clone()); winit_window
info!("Renderer created! ({})", type_name::<R>()); };
info!("Setting up!"); winit_window.build(event_loop).unwrap()
setup(&mut self, &mut renderer); }
let mut time_stack = 0.0; pub fn run<R: Renderer>(
mut self,
setup: fn(&mut App, &mut R),
update: fn(&mut App, &mut R, f32),
) {
info!("Starting up {}!", self.title);
info!("Starting event loop!"); pollster::block_on(async {
event_loop.run(|event, elwt| { let event_loop = EventLoop::new().unwrap();
self.delta_time = renderer.update(); let window = Arc::new(Self::create_window(
self.title.clone(),
&self.icon,
&self.size,
&event_loop,
));
let mut renderer = R::new(window.clone(), self.clear_color.clone());
info!("Renderer created! ({})", type_name::<R>());
if self.should_quit { info!("Setting up!");
elwt.exit() setup(&mut self, &mut renderer);
}
self.input_manager.update(&event); let mut time_stack = 0.0;
if self.dt() != f32::INFINITY { info!("Starting event loop!");
time_stack += self.delta_time; event_loop
while time_stack > self.update_timer { .run(|event, elwt| {
let time = self.dt(); self.delta_time = renderer.update();
update(&mut self, &mut renderer, time);
time_stack -= self.update_timer;
}
}
match event { if self.should_quit {
Event::WindowEvent { ref event, window_id} => { elwt.exit()
match event { }
WindowEvent::CloseRequested {} => elwt.exit(),
WindowEvent::Resized(physical_size) => {
renderer.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
window.request_redraw();
match renderer.render() {
Ok(_) => {},
Err(e) => error!("Error rendering: {}", e)
}
}
_ => {}
}
}
_ => {}
}
}).unwrap()
});
info!("Shutting down {}!", self.title); self.input_manager.update(&event);
}
if self.dt() != f32::INFINITY {
time_stack += self.delta_time;
while time_stack > self.update_timer {
let time = self.dt();
update(&mut self, &mut renderer, time);
time_stack -= self.update_timer;
}
}
match event {
Event::WindowEvent {
ref event,
window_id,
} => match event {
WindowEvent::CloseRequested {} => elwt.exit(),
WindowEvent::Resized(physical_size) => {
renderer.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
window.request_redraw();
match renderer.render() {
Ok(_) => {}
Err(e) => error!("Error rendering: {}", e),
}
}
_ => {}
},
_ => {}
}
})
.unwrap()
});
info!("Shutting down {}!", self.title);
}
} }

0
crates/comet_app/src/game_state.rs Normal file → Executable file
View file

0
crates/comet_app/src/lib.rs Normal file → Executable file
View file