headless apps
This commit is contained in:
parent
c7ee4bc133
commit
7c121563db
31
examples/headless.rs
Normal file
31
examples/headless.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use bevy::{
|
||||||
|
app::schedule_runner::{RunMode, ScheduleRunner},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("This app runs once:");
|
||||||
|
App::build()
|
||||||
|
.add_plugin(ScheduleRunner {
|
||||||
|
run_mode: RunMode::Once,
|
||||||
|
})
|
||||||
|
.add_system(hello_world_system())
|
||||||
|
.run();
|
||||||
|
|
||||||
|
println!("This app loops forever at 60 fps:");
|
||||||
|
App::build()
|
||||||
|
.add_plugin(ScheduleRunner {
|
||||||
|
run_mode: RunMode::Loop {
|
||||||
|
wait: Some(Duration::from_secs_f64(1.0 / 60.0)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.add_system(hello_world_system())
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hello_world_system() -> Box<dyn Schedulable> {
|
||||||
|
SystemBuilder::new("hello_world").build(move |_, _, _, _| {
|
||||||
|
println!("hello world");
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -1,10 +1,10 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::build().add_defaults().add_setup_system(setup()).run();
|
App::build().add_defaults().add_setup_system(setup_system()).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup() -> Box<dyn Schedulable> {
|
pub fn setup_system() -> Box<dyn Schedulable> {
|
||||||
SystemBuilder::new("setup")
|
SystemBuilder::new("setup")
|
||||||
.write_resource::<AssetStorage<Mesh>>()
|
.write_resource::<AssetStorage<Mesh>>()
|
||||||
.write_resource::<AssetStorage<StandardMaterial>>()
|
.write_resource::<AssetStorage<StandardMaterial>>()
|
||||||
|
|||||||
@ -5,7 +5,7 @@ pub struct App {
|
|||||||
pub universe: Universe,
|
pub universe: Universe,
|
||||||
pub world: World,
|
pub world: World,
|
||||||
pub resources: Resources,
|
pub resources: Resources,
|
||||||
pub run: Option<Box<dyn Fn(App)>>,
|
pub runner: Option<Box<dyn Fn(App)>>,
|
||||||
pub schedule: Schedule,
|
pub schedule: Schedule,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ impl App {
|
|||||||
universe,
|
universe,
|
||||||
world,
|
world,
|
||||||
schedule,
|
schedule,
|
||||||
run,
|
runner: run,
|
||||||
resources,
|
resources,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(mut self) {
|
pub fn run(mut self) {
|
||||||
if let Some(run) = self.run.take() {
|
if let Some(run) = self.runner.take() {
|
||||||
run(self)
|
run(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,12 +3,9 @@ use crate::{
|
|||||||
plugin::{load_plugin, AppPlugin},
|
plugin::{load_plugin, AppPlugin},
|
||||||
system_stage, App,
|
system_stage, App,
|
||||||
},
|
},
|
||||||
core::{winit::WinitPlugin, CorePlugin, Event},
|
core::{CorePlugin, Event},
|
||||||
legion::prelude::{Resources, Runnable, Schedulable, Schedule, Universe, World},
|
legion::prelude::{Resources, Runnable, Schedulable, Schedule, Universe, World},
|
||||||
render::{
|
render::RenderPlugin,
|
||||||
renderer::{renderers::wgpu_renderer::WgpuRendererPlugin},
|
|
||||||
RenderPlugin,
|
|
||||||
},
|
|
||||||
ui::UiPlugin,
|
ui::UiPlugin,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -119,13 +116,16 @@ impl AppBuilder {
|
|||||||
pub fn add_system(self, system: Box<dyn Schedulable>) -> Self {
|
pub fn add_system(self, system: Box<dyn Schedulable>) -> Self {
|
||||||
self.add_system_to_stage(system_stage::UPDATE, system)
|
self.add_system_to_stage(system_stage::UPDATE, system)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_setup_system(mut self, system: Box<dyn Schedulable>) -> Self {
|
pub fn add_setup_system(mut self, system: Box<dyn Schedulable>) -> Self {
|
||||||
self.setup_systems.push(system);
|
self.setup_systems.push(system);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_system<F>(mut self, build: F) -> Self where F: Fn(&mut Resources) -> Box<dyn Schedulable>{
|
pub fn build_system<F>(mut self, build: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(&mut Resources) -> Box<dyn Schedulable>,
|
||||||
|
{
|
||||||
let system = build(&mut self.resources);
|
let system = build(&mut self.resources);
|
||||||
self.add_system(system)
|
self.add_system(system)
|
||||||
}
|
}
|
||||||
@ -173,7 +173,10 @@ impl AppBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_event<T>(self) -> Self where T: Send + Sync + 'static {
|
pub fn add_event<T>(self) -> Self
|
||||||
|
where
|
||||||
|
T: Send + Sync + 'static,
|
||||||
|
{
|
||||||
self.add_resource(Event::<T>::default())
|
self.add_resource(Event::<T>::default())
|
||||||
.add_system_to_stage(system_stage::EVENT_UPDATE, Event::<T>::update_system())
|
.add_system_to_stage(system_stage::EVENT_UPDATE, Event::<T>::update_system())
|
||||||
}
|
}
|
||||||
@ -186,6 +189,11 @@ impl AppBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_runner(mut self, run_fn: impl Fn(App) + 'static) -> Self {
|
||||||
|
self.run = Some(Box::new(run_fn));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_defaults(mut self) -> Self {
|
pub fn add_defaults(mut self) -> Self {
|
||||||
self = self
|
self = self
|
||||||
.add_plugin(CorePlugin::default())
|
.add_plugin(CorePlugin::default())
|
||||||
@ -194,12 +202,18 @@ impl AppBuilder {
|
|||||||
|
|
||||||
#[cfg(feature = "winit")]
|
#[cfg(feature = "winit")]
|
||||||
{
|
{
|
||||||
self = self.add_plugin(WinitPlugin::default())
|
self = self.add_plugin(crate::core::window::winit::WinitPlugin::default())
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "winit"))]
|
||||||
|
{
|
||||||
|
self = self.add_plugin(crate::app::schedule_run::ScheduleRunner::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu")]
|
||||||
{
|
{
|
||||||
self = self.add_plugin(WgpuRendererPlugin::default());
|
self = self.add_plugin(
|
||||||
|
crate::render::renderer::renderers::wgpu_renderer::WgpuRendererPlugin::default(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ mod app;
|
|||||||
mod app_builder;
|
mod app_builder;
|
||||||
pub mod system_stage;
|
pub mod system_stage;
|
||||||
pub mod plugin;
|
pub mod plugin;
|
||||||
|
pub mod schedule_runner;
|
||||||
|
|
||||||
pub use app::App;
|
pub use app::App;
|
||||||
pub use app_builder::AppBuilder;
|
pub use app_builder::AppBuilder;
|
||||||
|
|||||||
46
src/app/schedule_runner.rs
Normal file
46
src/app/schedule_runner.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
use crate::{
|
||||||
|
app::{App, AppBuilder},
|
||||||
|
prelude::AppPlugin,
|
||||||
|
};
|
||||||
|
use std::{thread, time::Duration};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum RunMode {
|
||||||
|
Loop {
|
||||||
|
wait: Option<Duration>,
|
||||||
|
},
|
||||||
|
Once,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RunMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
RunMode::Loop {
|
||||||
|
wait: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ScheduleRunner {
|
||||||
|
pub run_mode: RunMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppPlugin for ScheduleRunner {
|
||||||
|
fn build(&self, app: AppBuilder) -> AppBuilder {
|
||||||
|
let run_mode = self.run_mode;
|
||||||
|
app.set_runner(move |mut app: App| match run_mode {
|
||||||
|
RunMode::Once => {
|
||||||
|
app.schedule.execute(&mut app.world, &mut app.resources);
|
||||||
|
}
|
||||||
|
RunMode::Loop { wait }=> loop {
|
||||||
|
app.schedule.execute(&mut app.world, &mut app.resources);
|
||||||
|
if let Some(wait) = wait {
|
||||||
|
thread::sleep(wait);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"ScheduleRun"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,7 @@
|
|||||||
use crate::{core::Event, app::{plugin::AppPlugin, App, AppBuilder}};
|
use crate::{
|
||||||
|
app::{plugin::AppPlugin, App, AppBuilder},
|
||||||
|
core::Event,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{Window, WindowResize};
|
use super::{Window, WindowResize};
|
||||||
use winit::{
|
use winit::{
|
||||||
@ -11,12 +14,8 @@ use winit::{
|
|||||||
pub struct WinitPlugin;
|
pub struct WinitPlugin;
|
||||||
|
|
||||||
impl AppPlugin for WinitPlugin {
|
impl AppPlugin for WinitPlugin {
|
||||||
fn build(&self, mut app: AppBuilder) -> AppBuilder {
|
fn build(&self, app: AppBuilder) -> AppBuilder {
|
||||||
{
|
app.set_runner(winit_runner)
|
||||||
app.run = Some(get_winit_run());
|
|
||||||
}
|
|
||||||
|
|
||||||
app
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
@ -24,63 +23,61 @@ impl AppPlugin for WinitPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_winit_run() -> Box<dyn Fn(App) + Send + Sync> {
|
pub fn winit_runner(mut app: App) {
|
||||||
Box::new(|mut app: App| {
|
env_logger::init();
|
||||||
env_logger::init();
|
let event_loop = EventLoop::new();
|
||||||
let event_loop = EventLoop::new();
|
let winit_window = {
|
||||||
let winit_window = {
|
let window = app.resources.get::<Window>().unwrap();
|
||||||
let window = app.resources.get::<Window>().unwrap();
|
let winit_window = winit::window::Window::new(&event_loop).unwrap();
|
||||||
let winit_window = winit::window::Window::new(&event_loop).unwrap();
|
winit_window.set_title(&window.title);
|
||||||
winit_window.set_title(&window.title);
|
winit_window.set_inner_size(winit::dpi::PhysicalSize::new(window.width, window.height));
|
||||||
winit_window.set_inner_size(winit::dpi::PhysicalSize::new(window.width, window.height));
|
winit_window
|
||||||
winit_window
|
};
|
||||||
|
|
||||||
|
app.resources.insert(winit_window);
|
||||||
|
|
||||||
|
log::debug!("Entering render loop");
|
||||||
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
*control_flow = if cfg!(feature = "metal-auto-capture") {
|
||||||
|
ControlFlow::Exit
|
||||||
|
} else {
|
||||||
|
ControlFlow::Poll
|
||||||
};
|
};
|
||||||
|
match event {
|
||||||
|
event::Event::WindowEvent {
|
||||||
|
event: WindowEvent::Resized(size),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut window = app.resources.get_mut::<Window>().unwrap();
|
||||||
|
window.width = size.width;
|
||||||
|
window.height = size.height;
|
||||||
|
|
||||||
app.resources.insert(winit_window);
|
let mut resize_event = app.resources.get_mut::<Event<WindowResize>>().unwrap();
|
||||||
|
resize_event.raise(WindowResize {
|
||||||
log::debug!("Entering render loop");
|
id: window.id,
|
||||||
event_loop.run(move |event, _, control_flow| {
|
height: window.height,
|
||||||
*control_flow = if cfg!(feature = "metal-auto-capture") {
|
width: window.width,
|
||||||
ControlFlow::Exit
|
});
|
||||||
} else {
|
|
||||||
ControlFlow::Poll
|
|
||||||
};
|
|
||||||
match event {
|
|
||||||
event::Event::WindowEvent {
|
|
||||||
event: WindowEvent::Resized(size),
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let mut window = app.resources.get_mut::<Window>().unwrap();
|
|
||||||
window.width = size.width;
|
|
||||||
window.height = size.height;
|
|
||||||
|
|
||||||
let mut resize_event = app.resources.get_mut::<Event<WindowResize>>().unwrap();
|
|
||||||
resize_event.raise(WindowResize {
|
|
||||||
id: window.id,
|
|
||||||
height: window.height,
|
|
||||||
width: window.width,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
event::Event::WindowEvent { event, .. } => match event {
|
|
||||||
WindowEvent::KeyboardInput {
|
|
||||||
input:
|
|
||||||
event::KeyboardInput {
|
|
||||||
virtual_keycode: Some(event::VirtualKeyCode::Escape),
|
|
||||||
state: event::ElementState::Pressed,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
}
|
|
||||||
| WindowEvent::CloseRequested => {
|
|
||||||
*control_flow = ControlFlow::Exit;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
event::Event::MainEventsCleared => {
|
|
||||||
app.update();
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
});
|
event::Event::WindowEvent { event, .. } => match event {
|
||||||
})
|
WindowEvent::KeyboardInput {
|
||||||
|
input:
|
||||||
|
event::KeyboardInput {
|
||||||
|
virtual_keycode: Some(event::VirtualKeyCode::Escape),
|
||||||
|
state: event::ElementState::Pressed,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| WindowEvent::CloseRequested => {
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
event::Event::MainEventsCleared => {
|
||||||
|
app.update();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -343,9 +343,12 @@ impl WgpuRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_surface(&mut self, resources: &Resources) {
|
pub fn create_surface(&mut self, resources: &Resources) {
|
||||||
let window = resources.get::<winit::window::Window>().unwrap();
|
#[cfg(feature = "winit")]
|
||||||
let surface = wgpu::Surface::create(window.deref());
|
{
|
||||||
self.surface = Some(surface);
|
let window = resources.get::<winit::window::Window>().unwrap();
|
||||||
|
let surface = wgpu::Surface::create(window.deref());
|
||||||
|
self.surface = Some(surface);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user