headless apps

This commit is contained in:
Carter Anderson 2020-03-30 11:52:33 -07:00
parent c7ee4bc133
commit 7c121563db
8 changed files with 173 additions and 81 deletions

31
examples/headless.rs Normal file
View 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");
})
}

View file

@ -1,10 +1,10 @@
use bevy::prelude::*;
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")
.write_resource::<AssetStorage<Mesh>>()
.write_resource::<AssetStorage<StandardMaterial>>()

View file

@ -5,7 +5,7 @@ pub struct App {
pub universe: Universe,
pub world: World,
pub resources: Resources,
pub run: Option<Box<dyn Fn(App)>>,
pub runner: Option<Box<dyn Fn(App)>>,
pub schedule: Schedule,
}
@ -21,7 +21,7 @@ impl App {
universe,
world,
schedule,
run,
runner: run,
resources,
}
}
@ -42,7 +42,7 @@ impl App {
}
pub fn run(mut self) {
if let Some(run) = self.run.take() {
if let Some(run) = self.runner.take() {
run(self)
}
}

View file

@ -3,12 +3,9 @@ use crate::{
plugin::{load_plugin, AppPlugin},
system_stage, App,
},
core::{winit::WinitPlugin, CorePlugin, Event},
core::{CorePlugin, Event},
legion::prelude::{Resources, Runnable, Schedulable, Schedule, Universe, World},
render::{
renderer::{renderers::wgpu_renderer::WgpuRendererPlugin},
RenderPlugin,
},
render::RenderPlugin,
ui::UiPlugin,
};
@ -119,13 +116,16 @@ impl AppBuilder {
pub fn add_system(self, system: Box<dyn Schedulable>) -> Self {
self.add_system_to_stage(system_stage::UPDATE, system)
}
pub fn add_setup_system(mut self, system: Box<dyn Schedulable>) -> Self {
self.setup_systems.push(system);
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);
self.add_system(system)
}
@ -173,7 +173,10 @@ impl AppBuilder {
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())
.add_system_to_stage(system_stage::EVENT_UPDATE, Event::<T>::update_system())
}
@ -186,6 +189,11 @@ impl AppBuilder {
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 {
self = self
.add_plugin(CorePlugin::default())
@ -194,12 +202,18 @@ impl AppBuilder {
#[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")]
{
self = self.add_plugin(WgpuRendererPlugin::default());
self = self.add_plugin(
crate::render::renderer::renderers::wgpu_renderer::WgpuRendererPlugin::default(),
);
}
self
}

View file

@ -2,6 +2,7 @@ mod app;
mod app_builder;
pub mod system_stage;
pub mod plugin;
pub mod schedule_runner;
pub use app::App;
pub use app_builder::AppBuilder;

View 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"
}
}

View file

@ -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 winit::{
@ -11,12 +14,8 @@ use winit::{
pub struct WinitPlugin;
impl AppPlugin for WinitPlugin {
fn build(&self, mut app: AppBuilder) -> AppBuilder {
{
app.run = Some(get_winit_run());
}
app
fn build(&self, app: AppBuilder) -> AppBuilder {
app.set_runner(winit_runner)
}
fn name(&self) -> &'static str {
@ -24,63 +23,61 @@ impl AppPlugin for WinitPlugin {
}
}
pub fn get_winit_run() -> Box<dyn Fn(App) + Send + Sync> {
Box::new(|mut app: App| {
env_logger::init();
let event_loop = EventLoop::new();
let winit_window = {
let window = app.resources.get::<Window>().unwrap();
let winit_window = winit::window::Window::new(&event_loop).unwrap();
winit_window.set_title(&window.title);
winit_window.set_inner_size(winit::dpi::PhysicalSize::new(window.width, window.height));
winit_window
pub fn winit_runner(mut app: App) {
env_logger::init();
let event_loop = EventLoop::new();
let winit_window = {
let window = app.resources.get::<Window>().unwrap();
let winit_window = winit::window::Window::new(&event_loop).unwrap();
winit_window.set_title(&window.title);
winit_window.set_inner_size(winit::dpi::PhysicalSize::new(window.width, window.height));
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);
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;
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();
}
_ => (),
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();
}
_ => (),
}
});
}

View file

@ -343,9 +343,12 @@ impl WgpuRenderer {
}
pub fn create_surface(&mut self, resources: &Resources) {
let window = resources.get::<winit::window::Window>().unwrap();
let surface = wgpu::Surface::create(window.deref());
self.surface = Some(surface);
#[cfg(feature = "winit")]
{
let window = resources.get::<winit::window::Window>().unwrap();
let surface = wgpu::Surface::create(window.deref());
self.surface = Some(surface);
}
}
}