mirror of
https://github.com/lisk77/comet.git
synced 2025-10-23 21:38:50 +00:00
feat(app): added the prefab interface to the app struct
This commit is contained in:
parent
e1597e6fa4
commit
7cf9f5bd29
4 changed files with 256 additions and 227 deletions
0
crates/comet_app/Cargo.toml
Normal file → Executable file
0
crates/comet_app/Cargo.toml
Normal file → Executable file
483
crates/comet_app/src/app.rs
Normal file → Executable file
483
crates/comet_app/src/app.rs
Normal file → Executable 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
0
crates/comet_app/src/game_state.rs
Normal file → Executable file
0
crates/comet_app/src/lib.rs
Normal file → Executable file
0
crates/comet_app/src/lib.rs
Normal file → Executable file
Loading…
Add table
Add a link
Reference in a new issue