mirror of
https://github.com/bevyengine/bevy
synced 2025-02-16 14:08:32 +00:00
headless apps
This commit is contained in:
parent
c7ee4bc133
commit
7c121563db
8 changed files with 173 additions and 81 deletions
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::*;
|
||||
|
||||
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>>()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
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 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();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue