migrate to winit 0.29.1-beta + wasm fixes

- use `EventLoop::spawn` as recommended in `EventLoop::run` docs
This commit is contained in:
Dmitry
2023-09-19 22:35:12 +03:00
parent 0904880765
commit 32df8a1046
34 changed files with 1655 additions and 1560 deletions
+3 -3
View File
@@ -60,7 +60,7 @@ strum = "0.25.0"
strum_macros = "0.25.0"
clap = { version = "4", features = ["derive"] }
spade = "2.1.0"
winit = { version = "0.29.0-beta.0", features = ["serde"] }
winit = { version = "0.29.1-beta", features = ["serde"] }
half = "2.2.1"
fast_image_resize = "2.7.0"
@@ -69,11 +69,11 @@ enable_profiler = ["fyrox-core/enable_profiler"]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
glutin = "0.30.10"
glutin-winit = "0.4.0-beta.0"
glutin-winit = "0.4.1-beta"
raw-window-handle = "0.5.0"
[target.'cfg(target_os = "android")'.dependencies]
winit = { version = "0.29.0-beta.0", features = ["android-native-activity"] }
winit = { version = "0.29.1-beta", features = ["android-native-activity"] }
[profile.github-ci]
inherits = "dev"
+1 -1
View File
@@ -2,7 +2,7 @@ use fyrox::event_loop::EventLoop;
use fyroxed_base::Editor;
fn main() {
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let editor = Editor::new(&event_loop, None);
editor.run(event_loop)
}
+129 -124
View File
@@ -2420,151 +2420,156 @@ impl Editor {
self.focused || !self.settings.general.suspend_unfocused_editor
}
pub fn run(mut self, event_loop: EventLoop<()>) -> ! {
pub fn run(mut self, event_loop: EventLoop<()>) {
for_each_plugin!(self.plugins => on_start(&mut self));
event_loop.run(move |event, _, control_flow| match event {
Event::MainEventsCleared => {
if self.is_active() {
update(&mut self, control_flow);
}
if self.exit {
*control_flow = ControlFlow::Exit;
// Kill any active child process on exit.
match self.mode {
Mode::Edit => {}
Mode::Build { ref mut process }
| Mode::Play {
ref mut process, ..
} => {
let _ = process.kill();
}
event_loop
.run(move |event, _, control_flow| match event {
Event::AboutToWait => {
if self.is_active() {
update(&mut self, control_flow);
}
}
}
Event::RedrawRequested(_) => {
if self.is_active() {
// Temporarily disable cameras in currently edited scene. This is needed to prevent any
// scene camera to interfere with the editor camera.
let mut camera_state = Vec::new();
if let Some(editor_scene) = self.scenes.current_editor_scene_ref() {
let scene = &mut self.engine.scenes[editor_scene.scene];
let has_preview_camera =
scene.graph.is_valid_handle(editor_scene.preview_camera);
for (handle, camera) in scene.graph.pair_iter_mut().filter_map(|(h, n)| {
if has_preview_camera && h != editor_scene.preview_camera
|| !has_preview_camera && h != editor_scene.camera_controller.camera
{
n.cast_mut::<Camera>().map(|c| (h, c))
} else {
None
if self.exit {
*control_flow = ControlFlow::Exit;
// Kill any active child process on exit.
match self.mode {
Mode::Edit => {}
Mode::Build { ref mut process }
| Mode::Play {
ref mut process, ..
} => {
let _ = process.kill();
}
}) {
camera_state.push((handle, camera.is_enabled()));
camera.set_enabled(false);
}
}
self.engine.render().unwrap();
// Revert state of the cameras.
if let Some(scene) = self.scenes.current_editor_scene_ref() {
for (handle, enabled) in camera_state {
self.engine.scenes[scene.scene].graph[handle]
.as_camera_mut()
.set_enabled(enabled);
}
}
}
}
Event::WindowEvent { ref event, .. } => {
match event {
WindowEvent::CloseRequested => {
self.message_sender.send(Message::Exit { force: false });
Event::RedrawRequested(_) => {
if self.is_active() {
// Temporarily disable cameras in currently edited scene. This is needed to prevent any
// scene camera to interfere with the editor camera.
let mut camera_state = Vec::new();
if let Some(editor_scene) = self.scenes.current_editor_scene_ref() {
let scene = &mut self.engine.scenes[editor_scene.scene];
let has_preview_camera =
scene.graph.is_valid_handle(editor_scene.preview_camera);
for (handle, camera) in
scene.graph.pair_iter_mut().filter_map(|(h, n)| {
if has_preview_camera && h != editor_scene.preview_camera
|| !has_preview_camera
&& h != editor_scene.camera_controller.camera
{
n.cast_mut::<Camera>().map(|c| (h, c))
} else {
None
}
})
{
camera_state.push((handle, camera.is_enabled()));
camera.set_enabled(false);
}
}
self.engine.render().unwrap();
// Revert state of the cameras.
if let Some(scene) = self.scenes.current_editor_scene_ref() {
for (handle, enabled) in camera_state {
self.engine.scenes[scene.scene].graph[handle]
.as_camera_mut()
.set_enabled(enabled);
}
}
}
WindowEvent::Resized(size) => {
if let Err(e) = self.engine.set_frame_size((*size).into()) {
fyrox::core::log::Log::writeln(
MessageKind::Error,
format!("Failed to set renderer size! Reason: {:?}", e),
}
Event::WindowEvent { ref event, .. } => {
match event {
WindowEvent::CloseRequested => {
self.message_sender.send(Message::Exit { force: false });
}
WindowEvent::Resized(size) => {
if let Err(e) = self.engine.set_frame_size((*size).into()) {
fyrox::core::log::Log::writeln(
MessageKind::Error,
format!("Failed to set renderer size! Reason: {:?}", e),
);
}
let logical_size = size.to_logical(
self.engine
.graphics_context
.as_initialized_ref()
.window
.scale_factor(),
);
}
let logical_size = size.to_logical(
self.engine
.graphics_context
.as_initialized_ref()
.window
.scale_factor(),
);
self.engine
.user_interface
.send_message(WidgetMessage::width(
self.root_grid,
MessageDirection::ToWidget,
logical_size.width,
));
self.engine
.user_interface
.send_message(WidgetMessage::height(
self.root_grid,
MessageDirection::ToWidget,
logical_size.height,
));
.user_interface
.send_message(WidgetMessage::width(
self.root_grid,
MessageDirection::ToWidget,
logical_size.width,
));
self.engine
.user_interface
.send_message(WidgetMessage::height(
self.root_grid,
MessageDirection::ToWidget,
logical_size.height,
));
if size.width > 0 && size.height > 0 {
self.settings.windows.window_size.x = size.width as f32;
self.settings.windows.window_size.y = size.height as f32;
Log::verify(self.settings.save());
if size.width > 0 && size.height > 0 {
self.settings.windows.window_size.x = size.width as f32;
self.settings.windows.window_size.y = size.height as f32;
Log::verify(self.settings.save());
}
}
}
WindowEvent::Focused(focused) => {
self.focused = *focused;
}
WindowEvent::Moved(new_position) => {
if new_position.x > 0 && new_position.y > 0 {
self.settings.windows.window_position.x = new_position.x as f32;
self.settings.windows.window_position.y = new_position.y as f32;
Log::verify(self.settings.save());
WindowEvent::Focused(focused) => {
self.focused = *focused;
}
WindowEvent::Moved(new_position) => {
if new_position.x > 0 && new_position.y > 0 {
self.settings.windows.window_position.x = new_position.x as f32;
self.settings.windows.window_position.y = new_position.y as f32;
Log::verify(self.settings.save());
}
}
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
set_ui_scaling(&self.engine.user_interface, *scale_factor as f32);
}
_ => (),
}
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
set_ui_scaling(&self.engine.user_interface, *scale_factor as f32);
self.update_loop_state.request_update_in_current_frame();
if let Some(os_event) = translate_event(event) {
self.engine.user_interface.process_os_event(&os_event);
}
_ => (),
}
Event::LoopExiting => {
Log::verify(self.settings.save());
self.update_loop_state.request_update_in_current_frame();
if let Some(os_event) = translate_event(event) {
self.engine.user_interface.process_os_event(&os_event);
for_each_plugin!(self.plugins => on_exit(&mut self));
}
}
Event::LoopDestroyed => {
Log::verify(self.settings.save());
_ => {
if !self.update_loop_state.is_suspended() && self.is_active() {
if self.is_suspended {
for_each_plugin!(self.plugins => on_resumed(&mut self));
self.is_suspended = false;
}
for_each_plugin!(self.plugins => on_exit(&mut self));
}
_ => {
if !self.update_loop_state.is_suspended() && self.is_active() {
if self.is_suspended {
for_each_plugin!(self.plugins => on_resumed(&mut self));
self.is_suspended = false;
*control_flow = ControlFlow::Poll;
} else {
if !self.is_suspended {
for_each_plugin!(self.plugins => on_suspended(&mut self));
self.is_suspended = true;
}
*control_flow = ControlFlow::Wait;
}
*control_flow = ControlFlow::Poll;
} else {
if !self.is_suspended {
for_each_plugin!(self.plugins => on_suspended(&mut self));
self.is_suspended = true;
}
*control_flow = ControlFlow::Wait;
}
}
});
})
.unwrap();
}
}
+2 -1
View File
@@ -4,6 +4,7 @@
//!
//! This example shows simple 2D scene with light sources.
use fyrox::event_loop::EventLoop;
use fyrox::resource::texture::Texture;
use fyrox::{
asset::manager::ResourceManager,
@@ -233,7 +234,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - 2D".to_string(),
+2 -1
View File
@@ -4,6 +4,7 @@
//!
//! This example shows simple 2D scene with light sources.
use fyrox::event_loop::EventLoop;
use fyrox::resource::model::{Model, ModelResourceExtension};
use fyrox::{
asset::manager::ResourceManager,
@@ -171,7 +172,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - 2D Scene".to_string(),
+182 -173
View File
@@ -59,205 +59,214 @@ fn main() {
// Finally run our event loop which will respond to OS and window events and update
// engine state accordingly.
event_loop.run(move |event, window_target, control_flow| {
match event {
Event::MainEventsCleared => {
// This is main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
event_loop
.run(move |event, window_target, control_flow| {
match event {
Event::AboutToWait => {
// This is main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
// Check each frame if our scene is created - here we just trying to lock context
// without blocking, it is important for main thread to be functional while other
// thread still loading data.
if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock() {
if let Some(load_result) = load_context.scene_data.take() {
// Add scene to engine - engine will take ownership over scene and will return
// you a handle to scene which can be used later on to borrow it and do some
// actions you need.
game.game_scene = Some(GameScene {
scene: game.engine.scenes.add(load_result.scene),
player: load_result.player,
});
// Check each frame if our scene is created - here we just trying to lock context
// without blocking, it is important for main thread to be functional while other
// thread still loading data.
if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock()
{
if let Some(load_result) = load_context.scene_data.take() {
// Add scene to engine - engine will take ownership over scene and will return
// you a handle to scene which can be used later on to borrow it and do some
// actions you need.
game.game_scene = Some(GameScene {
scene: game.engine.scenes.add(load_result.scene),
player: load_result.player,
});
// Once scene is loaded, we should hide progress bar and text.
// Once scene is loaded, we should hide progress bar and text.
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_bar,
MessageDirection::ToWidget,
false,
));
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_text,
MessageDirection::ToWidget,
false,
));
}
// Report progress in UI.
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
.send_message(ProgressBarMessage::progress(
interface.progress_bar,
MessageDirection::ToWidget,
false,
));
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_text,
MessageDirection::ToWidget,
false,
load_context.progress,
));
game.engine.user_interface.send_message(TextMessage::text(
interface.progress_text,
MessageDirection::ToWidget,
format!(
"Loading scene: {}%\n{}",
load_context.progress * 100.0,
load_context.message
),
));
}
// Report progress in UI.
game.engine
.user_interface
.send_message(ProgressBarMessage::progress(
interface.progress_bar,
MessageDirection::ToWidget,
load_context.progress,
));
game.engine.user_interface.send_message(TextMessage::text(
interface.progress_text,
MessageDirection::ToWidget,
format!(
"Loading scene: {}%\n{}",
load_context.progress * 100.0,
load_context.message
),
));
}
// Update scene only if it is loaded.
if let Some(game_scene) = game.game_scene.as_mut() {
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut game.engine.scenes[game_scene.scene];
game_scene.player.update(scene, fixed_timestep);
}
// Update scene only if it is loaded.
if let Some(game_scene) = game.game_scene.as_mut() {
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut game.engine.scenes[game_scene.scene];
game_scene.player.update(scene, fixed_timestep);
}
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context {
let debug_text = format!(
"Example 03 - 3rd Person\n\
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context
{
let debug_text = format!(
"Example 03 - 3rd Person\n\
[W][S][A][D] - walk, [SPACE] - jump.\n\
Use [1][2][3][4] to select graphics quality.\n\
{}",
ctx.renderer.get_statistics()
);
game.engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
debug_text,
));
}
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = game.engine.user_interface.poll_message() {
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
}
game.engine
.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
game.engine
.initialize_graphics_context(window_target)
.unwrap();
}
Event::Suspended => {
game.engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
game.engine.render().unwrap();
}
Event::LoopDestroyed => {
println!("{:?}", fyrox::core::profiler::print());
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = game.engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
ctx.renderer.get_statistics()
);
game.engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
debug_text,
));
}
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context
{
let size = size.to_logical(ctx.window.scale_factor());
game.engine
.user_interface
.send_message(WidgetMessage::width(
interface.root,
MessageDirection::ToWidget,
size.width,
));
game.engine
.user_interface
.send_message(WidgetMessage::height(
interface.root,
MessageDirection::ToWidget,
size.height,
));
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = game.engine.user_interface.poll_message() {
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
}
game.engine.update(
fixed_timestep,
control_flow,
&mut lag,
Default::default(),
);
lag -= fixed_timestep;
}
WindowEvent::KeyboardInput { event: input, .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene.player.handle_key_event(input, fixed_timestep);
}
let settings = match input.physical_key {
KeyCode::Digit1 => Some(QualitySettings::ultra()),
KeyCode::Digit2 => Some(QualitySettings::high()),
KeyCode::Digit3 => Some(QualitySettings::medium()),
KeyCode::Digit4 => Some(QualitySettings::low()),
_ => None,
};
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
game.engine
.initialize_graphics_context(window_target)
.unwrap();
}
Event::Suspended => {
game.engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
game.engine.render().unwrap();
}
Event::LoopExiting => {
println!("{:?}", fyrox::core::profiler::print());
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = game.engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
if let Some(settings) = settings {
if let GraphicsContext::Initialized(ref mut ctx) =
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
if let GraphicsContext::Initialized(ref ctx) =
game.engine.graphics_context
{
ctx.renderer
.set_quality_settings(&fix_shadows_distance(settings))
.unwrap();
let size = size.to_logical(ctx.window.scale_factor());
game.engine
.user_interface
.send_message(WidgetMessage::width(
interface.root,
MessageDirection::ToWidget,
size.width,
));
game.engine
.user_interface
.send_message(WidgetMessage::height(
interface.root,
MessageDirection::ToWidget,
size.height,
));
}
}
}
_ => (),
}
WindowEvent::KeyboardInput { event: input, .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene.player.handle_key_event(input, fixed_timestep);
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
game.engine.user_interface.process_os_event(&os_event);
let settings = match input.physical_key {
KeyCode::Digit1 => Some(QualitySettings::ultra()),
KeyCode::Digit2 => Some(QualitySettings::high()),
KeyCode::Digit3 => Some(QualitySettings::medium()),
KeyCode::Digit4 => Some(QualitySettings::low()),
_ => None,
};
if let Some(settings) = settings {
if let GraphicsContext::Initialized(ref mut ctx) =
game.engine.graphics_context
{
ctx.renderer
.set_quality_settings(&fix_shadows_distance(settings))
.unwrap();
}
}
}
_ => (),
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
game.engine.user_interface.process_os_event(&os_event);
}
}
}
Event::DeviceEvent { event, .. } => {
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene
.player
.handle_device_event(&event, fixed_timestep);
Event::DeviceEvent { event, .. } => {
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene
.player
.handle_device_event(&event, fixed_timestep);
}
}
_ => *control_flow = ControlFlow::Poll,
}
_ => *control_flow = ControlFlow::Poll,
}
});
})
.unwrap();
}
+3 -2
View File
@@ -7,6 +7,8 @@
pub mod shared;
use fyrox::event_loop::EventLoop;
use fyrox::keyboard::KeyCode;
use fyrox::resource::model::{Model, ModelResourceExtension};
use fyrox::{
asset::manager::ResourceManager,
@@ -32,7 +34,6 @@ use fyrox::{
window::WindowAttributes,
};
use std::sync::{Arc, Mutex};
use winit::keyboard::KeyCode;
use crate::shared::create_camera;
@@ -374,7 +375,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Asynchronous Scene Loading".to_string(),
+3 -2
View File
@@ -1,6 +1,8 @@
pub mod shared;
use crate::shared::create_camera;
use fyrox::event_loop::EventLoop;
use fyrox::keyboard::KeyCode;
use fyrox::resource::model::{Model, ModelResourceExtension};
use fyrox::{
asset::manager::ResourceManager,
@@ -34,7 +36,6 @@ use fyrox::{
window::WindowAttributes,
};
use std::collections::BTreeSet;
use winit::keyboard::KeyCode;
struct GameSceneLoader {
scene: Scene,
@@ -272,7 +273,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Blend Shapes".to_string(),
+2 -1
View File
@@ -1,3 +1,4 @@
use fyrox::event_loop::EventLoop;
use fyrox::{
core::{algebra::Vector2, pool::Handle, rand::Rng},
engine::{executor::Executor, GraphicsContext, GraphicsContextParams},
@@ -76,7 +77,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Button".to_string(),
+2 -1
View File
@@ -14,6 +14,7 @@ use fyrox::{
utils::into_gui_texture,
window::WindowAttributes,
};
use winit::event_loop::EventLoop;
struct Game;
@@ -82,7 +83,7 @@ impl Plugin for Game {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Compressed Textures".to_string(),
+43 -41
View File
@@ -197,7 +197,7 @@ impl GameSceneLoader {
}
fn main() {
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let graphics_context_params = GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Custom Resource Loader".to_string(),
@@ -241,47 +241,49 @@ fn main() {
let fixed_timestep = 1.0 / 60.0;
let mut lag = 0.0;
event_loop.run(move |event, window_target, control_flow| match event {
Event::MainEventsCleared => {
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
if let Err(e) = engine.set_frame_size(size.into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
event_loop
.run(move |event, window_target, control_flow| match event {
Event::AboutToWait => {
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
_ => (),
}
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
ctx.window.request_redraw();
}
}
}
_ => *control_flow = ControlFlow::Poll,
});
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
if let Err(e) = engine.set_frame_size(size.into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
_ => (),
}
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
}
}
_ => *control_flow = ControlFlow::Poll,
})
.unwrap();
}
+78 -76
View File
@@ -21,7 +21,7 @@ use std::sync::Arc;
use winit::window::WindowAttributes;
fn main() {
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
// Create window builder first.
let graphics_context_params = GraphicsContextParams {
@@ -48,85 +48,87 @@ fn main() {
// Finally run our event loop which will respond to OS and window events and update
// engine state accordingly. Engine lets you to decide which event should be handled,
// this is minimal working example if how it should be.
event_loop.run(move |event, window_target, control_flow| {
match event {
Event::MainEventsCleared => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// ************************
// Put your game logic here.
// ************************
// ************************
event_loop
.run(move |event, window_target, control_flow| {
match event {
Event::AboutToWait => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// ************************
// Put your game logic here.
// ************************
// ************************
// It is very important to update the engine every frame!
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
// It is very important to update the engine every frame!
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = engine.user_interface.poll_message() {
// ************************
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
// ************************
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref mut graphics_context) =
engine.graphics_context
{
graphics_context.window.request_redraw();
}
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size(size.into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
lag -= fixed_timestep;
}
// Handle rest of events here if necessary.
_ => (),
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = engine.user_interface.poll_message() {
// ************************
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
// ************************
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref mut graphics_context) =
engine.graphics_context
{
graphics_context.window.request_redraw();
}
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size(size.into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
// Handle rest of events here if necessary.
_ => (),
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
}
}
// Continue polling messages from OS.
_ => *control_flow = ControlFlow::Poll,
}
// Continue polling messages from OS.
_ => *control_flow = ControlFlow::Poll,
}
});
})
.unwrap();
}
+2 -1
View File
@@ -1,3 +1,4 @@
use fyrox::event_loop::EventLoop;
use fyrox::{
asset::{
event::ResourceEventBroadcaster,
@@ -146,7 +147,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Custom Resource".to_string(),
+2 -1
View File
@@ -1,3 +1,4 @@
use fyrox::event_loop::EventLoop;
use fyrox::{
core::pool::Handle,
engine::{executor::Executor, GraphicsContextParams},
@@ -57,7 +58,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Grid".to_string(),
+111 -106
View File
@@ -107,7 +107,7 @@ async fn create_scene(resource_manager: ResourceManager) -> GameScene {
}
fn main() {
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let graphics_context_params = GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - User Interface".to_string(),
@@ -164,125 +164,130 @@ fn main() {
// Finally run our event loop which will respond to OS and window events and update
// engine state accordingly. Engine lets you to decide which event should be handled,
// this is minimal working example if how it should be.
event_loop.run(move |event, window_target, control_flow| {
match event {
Event::MainEventsCleared => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
event_loop
.run(move |event, window_target, control_flow| {
match event {
Event::AboutToWait => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
let fps = ctx.renderer.get_statistics().frames_per_second;
engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
format!("Example 04 - User Interface\nFPS: {}", fps),
));
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
let fps = ctx.renderer.get_statistics().frames_per_second;
engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
format!("Example 04 - User Interface\nFPS: {}", fps),
));
}
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
// It is very important to "pump" messages from UI. This our main point where we communicate
// with user interface. As you saw earlier, there is no callbacks on UI elements, instead we
// use messages to get information from UI elements. This provides perfect decoupling of logic
// from UI elements and works well with borrow checker.
let scene = &mut engine.scenes[scene_handle];
while let Some(ui_message) = engine.user_interface.poll_message() {
if ui_message.destination() == interface.inspector
&& ui_message.direction() == MessageDirection::FromWidget
{
if let Some(InspectorMessage::PropertyChanged(args)) =
ui_message.data::<InspectorMessage>()
// It is very important to "pump" messages from UI. This our main point where we communicate
// with user interface. As you saw earlier, there is no callbacks on UI elements, instead we
// use messages to get information from UI elements. This provides perfect decoupling of logic
// from UI elements and works well with borrow checker.
let scene = &mut engine.scenes[scene_handle];
while let Some(ui_message) = engine.user_interface.poll_message() {
if ui_message.destination() == interface.inspector
&& ui_message.direction() == MessageDirection::FromWidget
{
if let FieldKind::Object(ref value) = args.value {
match args.name.as_str() {
"local_scale" => {
value.cast_clone::<Vector3<f32>>(&mut |result| {
if let Some(InspectorMessage::PropertyChanged(args)) =
ui_message.data::<InspectorMessage>()
{
if let FieldKind::Object(ref value) = args.value {
match args.name.as_str() {
"local_scale" => {
value.cast_clone::<Vector3<f32>>(&mut |result| {
scene.graph[model_handle]
.local_transform_mut()
.set_scale(result.unwrap());
});
}
"visibility" => value.cast_clone::<bool>(&mut |result| {
scene.graph[model_handle]
.local_transform_mut()
.set_scale(result.unwrap());
});
.set_visibility(result.unwrap());
}),
"local_rotation" => {
value.cast_clone::<UnitQuaternion<f32>>(
&mut |result| {
scene.graph[model_handle]
.local_transform_mut()
.set_rotation(result.unwrap());
},
);
}
"name" => value.cast_clone::<String>(&mut |result| {
scene.graph[model_handle].set_name(result.unwrap())
}),
// TODO: Add rest of properties.
_ => (),
}
"visibility" => value.cast_clone::<bool>(&mut |result| {
scene.graph[model_handle].set_visibility(result.unwrap());
}),
"local_rotation" => {
value.cast_clone::<UnitQuaternion<f32>>(&mut |result| {
scene.graph[model_handle]
.local_transform_mut()
.set_rotation(result.unwrap());
});
}
"name" => value.cast_clone::<String>(&mut |result| {
scene.graph[model_handle].set_name(result.unwrap())
}),
// TODO: Add rest of properties.
_ => (),
}
}
}
}
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
ctx.window.request_redraw();
}
WindowEvent::KeyboardInput { event: input, .. } => {
if input.state == ElementState::Pressed
&& input.physical_key == KeyCode::Escape
{
*control_flow = ControlFlow::Exit;
}
}
_ => (),
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
WindowEvent::KeyboardInput { event: input, .. } => {
if input.state == ElementState::Pressed
&& input.physical_key == KeyCode::Escape
{
*control_flow = ControlFlow::Exit;
}
}
_ => (),
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
}
}
Event::DeviceEvent { .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
}
_ => *control_flow = ControlFlow::Poll,
}
Event::DeviceEvent { .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
}
_ => *control_flow = ControlFlow::Poll,
}
});
})
.unwrap();
}
+3 -2
View File
@@ -8,6 +8,8 @@
pub mod shared;
use crate::shared::create_camera;
use fyrox::event_loop::EventLoop;
use fyrox::keyboard::KeyCode;
use fyrox::resource::model::{Model, ModelResourceExtension};
use fyrox::resource::texture::Texture;
use fyrox::{
@@ -45,7 +47,6 @@ use fyrox::{
},
window::WindowAttributes,
};
use winit::keyboard::KeyCode;
struct SceneLoader {
scene: Scene,
@@ -284,7 +285,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Instancing".to_string(),
+224 -220
View File
@@ -300,7 +300,7 @@ struct InputController {
}
fn main() {
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let graphics_context_params = GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Lightmap".to_string(),
@@ -366,122 +366,33 @@ fn main() {
// Finally run our event loop which will respond to OS and window events and update
// engine state accordingly. Engine lets you to decide which event should be handled,
// this is minimal working example if how it should be.
event_loop.run(move |event, window_target, control_flow| {
match event {
Event::MainEventsCleared => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
event_loop
.run(move |event, window_target, control_flow| {
match event {
Event::AboutToWait => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
// Check each frame if our scene is created - here we just trying to lock context
// without blocking, it is important for main thread to be functional while other
// thread still loading data.
if let Ok(mut load_context) = game_scene.try_lock() {
if let Some(game_scene) = load_context.data.take() {
// Add scene to engine - engine will take ownership over scene and will return
// you a handle to scene which can be used later on to borrow it and do some
// actions you need.
scene_handle = engine.scenes.add(game_scene.scene);
model_handle = game_scene.root;
// Check each frame if our scene is created - here we just trying to lock context
// without blocking, it is important for main thread to be functional while other
// thread still loading data.
if let Ok(mut load_context) = game_scene.try_lock() {
if let Some(game_scene) = load_context.data.take() {
// Add scene to engine - engine will take ownership over scene and will return
// you a handle to scene which can be used later on to borrow it and do some
// actions you need.
scene_handle = engine.scenes.add(game_scene.scene);
model_handle = game_scene.root;
// Once scene is loaded, we should hide progress bar and text.
engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_grid,
MessageDirection::ToWidget,
false,
));
engine.user_interface.send_message(WindowMessage::close(
interface.choice_window,
MessageDirection::ToWidget,
));
}
let stage = match load_context.progress_indicator.stage() {
ProgressStage::LightsCaching => "Caching Lights",
ProgressStage::UvGeneration => "Generating UVs",
ProgressStage::GeometryCaching => "Caching Geometry",
ProgressStage::CalculatingLight => "Calculating Light",
};
let message = if load_context.generate_lightmap {
format!(
"Please wait until lightmap is fully generated.\n\
Stage {} of 4: {}\n\
Elapsed time: {:.2} s",
load_context.progress_indicator.stage() as u32 + 1,
stage,
load_context.start_time.elapsed().as_secs_f32(),
)
} else {
format!(
"Please wait until existing lightmap is fully loaded.\n\
Elapsed time: {:.2} s",
load_context.start_time.elapsed().as_secs_f32(),
)
};
// Report progress in UI.
engine
.user_interface
.send_message(ProgressBarMessage::progress(
interface.progress_bar,
MessageDirection::ToWidget,
load_context.progress_indicator.progress_percent() as f32 / 100.0,
));
engine.user_interface.send_message(TextMessage::text(
interface.progress_text,
MessageDirection::ToWidget,
message,
));
}
// Update scene only if it is loaded.
if scene_handle.is_some() {
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut engine.scenes[scene_handle];
// Rotate model according to input controller state.
if input_controller.rotate_left {
model_angle -= 5.0f32.to_radians();
} else if input_controller.rotate_right {
model_angle += 5.0f32.to_radians();
}
scene.graph[model_handle]
.local_transform_mut()
.set_rotation(UnitQuaternion::from_axis_angle(
&Vector3::y_axis(),
model_angle,
));
}
// While scene is loading, we will update progress bar.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
let debug_text = format!(
"Example 09 - Lightmap\nUse [A][D] keys to rotate model.\n{}",
ctx.renderer.get_statistics()
);
engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
debug_text,
));
}
while let Some(ui_event) = engine.user_interface.poll_message() {
if let Some(ButtonMessage::Click) = ui_event.data::<ButtonMessage>() {
if ui_event.destination() == interface.cancel {
game_scene.lock().unwrap().cancellation_token.cancel();
// Once scene is loaded, we should hide progress bar and text.
engine
.user_interface
.send_message(WidgetMessage::visibility(
@@ -489,124 +400,217 @@ fn main() {
MessageDirection::ToWidget,
false,
));
engine
.user_interface
.send_message(WindowMessage::open_modal(
engine.user_interface.send_message(WindowMessage::close(
interface.choice_window,
MessageDirection::ToWidget,
));
}
let stage = match load_context.progress_indicator.stage() {
ProgressStage::LightsCaching => "Caching Lights",
ProgressStage::UvGeneration => "Generating UVs",
ProgressStage::GeometryCaching => "Caching Geometry",
ProgressStage::CalculatingLight => "Calculating Light",
};
let message = if load_context.generate_lightmap {
format!(
"Please wait until lightmap is fully generated.\n\
Stage {} of 4: {}\n\
Elapsed time: {:.2} s",
load_context.progress_indicator.stage() as u32 + 1,
stage,
load_context.start_time.elapsed().as_secs_f32(),
)
} else {
format!(
"Please wait until existing lightmap is fully loaded.\n\
Elapsed time: {:.2} s",
load_context.start_time.elapsed().as_secs_f32(),
)
};
// Report progress in UI.
engine
.user_interface
.send_message(ProgressBarMessage::progress(
interface.progress_bar,
MessageDirection::ToWidget,
load_context.progress_indicator.progress_percent() as f32
/ 100.0,
));
engine.user_interface.send_message(TextMessage::text(
interface.progress_text,
MessageDirection::ToWidget,
message,
));
}
// Update scene only if it is loaded.
if scene_handle.is_some() {
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut engine.scenes[scene_handle];
// Rotate model according to input controller state.
if input_controller.rotate_left {
model_angle -= 5.0f32.to_radians();
} else if input_controller.rotate_right {
model_angle += 5.0f32.to_radians();
}
scene.graph[model_handle]
.local_transform_mut()
.set_rotation(UnitQuaternion::from_axis_angle(
&Vector3::y_axis(),
model_angle,
));
}
// While scene is loading, we will update progress bar.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
let debug_text = format!(
"Example 09 - Lightmap\nUse [A][D] keys to rotate model.\n{}",
ctx.renderer.get_statistics()
);
engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
debug_text,
));
}
while let Some(ui_event) = engine.user_interface.poll_message() {
if let Some(ButtonMessage::Click) = ui_event.data::<ButtonMessage>() {
if ui_event.destination() == interface.cancel {
game_scene.lock().unwrap().cancellation_token.cancel();
engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_grid,
MessageDirection::ToWidget,
false,
));
engine
.user_interface
.send_message(WindowMessage::open_modal(
interface.choice_window,
MessageDirection::ToWidget,
true,
));
} else if ui_event.destination() == interface.generate_new {
game_scene = create_scene_async(
engine.serialization_context.clone(),
engine.resource_manager.clone(),
true,
);
engine.user_interface.send_message(WindowMessage::close(
interface.choice_window,
MessageDirection::ToWidget,
true,
));
} else if ui_event.destination() == interface.generate_new {
game_scene = create_scene_async(
engine.serialization_context.clone(),
engine.resource_manager.clone(),
true,
);
engine.user_interface.send_message(WindowMessage::close(
interface.choice_window,
MessageDirection::ToWidget,
));
engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_grid,
engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_grid,
MessageDirection::ToWidget,
true,
));
} else if ui_event.destination() == interface.load_existing {
game_scene = create_scene_async(
engine.serialization_context.clone(),
engine.resource_manager.clone(),
false,
);
engine.user_interface.send_message(WindowMessage::close(
interface.choice_window,
MessageDirection::ToWidget,
true,
));
} else if ui_event.destination() == interface.load_existing {
game_scene = create_scene_async(
engine.serialization_context.clone(),
engine.resource_manager.clone(),
false,
);
engine.user_interface.send_message(WindowMessage::close(
interface.choice_window,
MessageDirection::ToWidget,
));
engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_grid,
MessageDirection::ToWidget,
true,
));
engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_grid,
MessageDirection::ToWidget,
true,
));
}
}
}
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
let size = size.to_logical(ctx.window.scale_factor());
engine.user_interface.send_message(WidgetMessage::width(
interface.root,
MessageDirection::ToWidget,
size.width,
));
engine.user_interface.send_message(WidgetMessage::height(
interface.root,
MessageDirection::ToWidget,
size.height,
));
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
ctx.window.request_redraw();
}
WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key {
KeyCode::KeyA => {
input_controller.rotate_left = input.state == ElementState::Pressed
}
KeyCode::KeyD => {
input_controller.rotate_right = input.state == ElementState::Pressed
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
let size = size.to_logical(ctx.window.scale_factor());
engine.user_interface.send_message(WidgetMessage::width(
interface.root,
MessageDirection::ToWidget,
size.width,
));
engine.user_interface.send_message(WidgetMessage::height(
interface.root,
MessageDirection::ToWidget,
size.height,
));
}
}
WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key
{
KeyCode::KeyA => {
input_controller.rotate_left = input.state == ElementState::Pressed
}
KeyCode::KeyD => {
input_controller.rotate_right = input.state == ElementState::Pressed
}
_ => (),
},
_ => (),
},
_ => (),
}
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
}
}
Event::DeviceEvent { .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
}
_ => *control_flow = ControlFlow::Poll,
}
Event::DeviceEvent { .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
}
_ => *control_flow = ControlFlow::Poll,
}
});
})
.unwrap();
}
+122 -119
View File
@@ -146,7 +146,7 @@ struct InputController {
}
fn main() {
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let graphics_context_params = GraphicsContextParams {
window_attributes: WindowAttributes {
@@ -198,136 +198,139 @@ fn main() {
// Finally run our event loop which will respond to OS and window events and update
// engine state accordingly. Engine lets you to decide which event should be handled,
// this is minimal working example if how it should be.
event_loop.run(move |event, window_target, control_flow| {
match event {
Event::MainEventsCleared => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
event_loop
.run(move |event, window_target, control_flow| {
match event {
Event::AboutToWait => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut engine.scenes[scene_handle];
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut engine.scenes[scene_handle];
// Rotate model according to input controller state.
if input_controller.rotate_left {
model_angle -= 5.0f32.to_radians();
} else if input_controller.rotate_right {
model_angle += 5.0f32.to_radians();
}
// Rotate model according to input controller state.
if input_controller.rotate_left {
model_angle -= 5.0f32.to_radians();
} else if input_controller.rotate_right {
model_angle += 5.0f32.to_radians();
}
if input_controller.forward {
distance = (distance - 0.1f32).max(0.0f32);
} else if input_controller.backward {
distance = (distance + 0.1f32).min(32.0f32)
}
if input_controller.forward {
distance = (distance - 0.1f32).max(0.0f32);
} else if input_controller.backward {
distance = (distance + 0.1f32).min(32.0f32)
}
scene.graph[model_handle]
.local_transform_mut()
.set_rotation(UnitQuaternion::from_axis_angle(
&Vector3::y_axis(),
model_angle,
));
scene.graph[model_handle]
.local_transform_mut()
.set_rotation(UnitQuaternion::from_axis_angle(
&Vector3::y_axis(),
model_angle,
));
scene.graph[camera]
.local_transform_mut()
.set_position(Vector3::new(0.0, 1.5, -distance));
scene.graph[camera]
.local_transform_mut()
.set_position(Vector3::new(0.0, 1.5, -distance));
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
let fps = ctx.renderer.get_statistics().frames_per_second;
let text = format!(
"Example 08 - Level of Detail\n\
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
let fps = ctx.renderer.get_statistics().frames_per_second;
let text = format!(
"Example 08 - Level of Detail\n\
Use [A][D] keys to rotate model, \
[W][S] to zoom in/out.\nFPS: {}\n\
Triangles rendered: {}",
fps,
ctx.renderer.get_statistics().geometry.triangles_rendered
);
engine.user_interface.send_message(TextMessage::text(
debug_text,
MessageDirection::ToWidget,
text,
));
}
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = engine.user_interface.poll_message() {
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
fps,
ctx.renderer.get_statistics().geometry.triangles_rendered
);
engine.user_interface.send_message(TextMessage::text(
debug_text,
MessageDirection::ToWidget,
text,
));
}
}
WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key {
KeyCode::KeyA => {
input_controller.rotate_left = input.state == ElementState::Pressed
}
KeyCode::KeyD => {
input_controller.rotate_right = input.state == ElementState::Pressed
}
KeyCode::KeyW => {
input_controller.forward = input.state == ElementState::Pressed
}
KeyCode::KeyS => {
input_controller.backward = input.state == ElementState::Pressed
}
_ => (),
},
_ => (),
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = engine.user_interface.poll_message() {
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key
{
KeyCode::KeyA => {
input_controller.rotate_left = input.state == ElementState::Pressed
}
KeyCode::KeyD => {
input_controller.rotate_right = input.state == ElementState::Pressed
}
KeyCode::KeyW => {
input_controller.forward = input.state == ElementState::Pressed
}
KeyCode::KeyS => {
input_controller.backward = input.state == ElementState::Pressed
}
_ => (),
},
_ => (),
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
}
}
_ => *control_flow = ControlFlow::Poll,
}
_ => *control_flow = ControlFlow::Poll,
}
});
})
.unwrap();
}
+2 -1
View File
@@ -40,6 +40,7 @@ use fyrox::{
},
window::WindowAttributes,
};
use winit::event_loop::EventLoop;
struct Game {
debug_text: Handle<UiNode>,
@@ -147,7 +148,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Materials and Shaders".to_string(),
+2 -1
View File
@@ -7,6 +7,7 @@
pub mod shared;
use crate::shared::create_camera;
use fyrox::event_loop::EventLoop;
use fyrox::scene::node::NodeTrait;
use fyrox::{
asset::manager::ResourceManager,
@@ -295,7 +296,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example 12 - Navigation Mesh".to_string(),
+2 -1
View File
@@ -1,4 +1,5 @@
use fyrox::engine::GraphicsContext;
use fyrox::event_loop::EventLoop;
use fyrox::{
core::{
color::{Color, Hsv},
@@ -44,7 +45,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Plugins".to_string(),
+2 -1
View File
@@ -25,6 +25,7 @@ use fyrox::{
Scene,
},
};
use winit::event_loop::EventLoop;
struct Game {
from: Vector2<f32>,
@@ -175,7 +176,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - 2D Raycasting".to_string(),
+2 -1
View File
@@ -1,3 +1,4 @@
use fyrox::event_loop::EventLoop;
use fyrox::resource::texture::TextureResourceExtension;
use fyrox::{
core::{algebra::Vector2, futures::executor::block_on, pool::Handle},
@@ -153,7 +154,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Render Target".to_string(),
+2 -1
View File
@@ -18,6 +18,7 @@ use fyrox::{
scene::Scene,
window::WindowAttributes,
};
use winit::event_loop::EventLoop;
struct Game {
grid: Handle<UiNode>,
@@ -117,7 +118,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Right Anchored Button".to_string(),
+193 -184
View File
@@ -161,215 +161,224 @@ fn main() {
// Finally run our event loop which will respond to OS and window events and update
// engine state accordingly.
event_loop.run(move |event, window_target, control_flow| {
match event {
Event::MainEventsCleared => {
// This is main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
event_loop
.run(move |event, window_target, control_flow| {
match event {
Event::AboutToWait => {
// This is main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
// Check each frame if our scene is created - here we just trying to lock context
// without blocking, it is important for main thread to be functional while other
// thread still loading data.
if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock() {
if let Some(load_result) = load_context.scene_data.take() {
// Add scene to engine - engine will take ownership over scene and will return
// you a handle to scene which can be used later on to borrow it and do some
// actions you need.
game.game_scene = Some(GameScene {
scene: game.engine.scenes.add(load_result.scene),
player: load_result.player,
});
// Check each frame if our scene is created - here we just trying to lock context
// without blocking, it is important for main thread to be functional while other
// thread still loading data.
if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock()
{
if let Some(load_result) = load_context.scene_data.take() {
// Add scene to engine - engine will take ownership over scene and will return
// you a handle to scene which can be used later on to borrow it and do some
// actions you need.
game.game_scene = Some(GameScene {
scene: game.engine.scenes.add(load_result.scene),
player: load_result.player,
});
// Once scene is loaded, we should hide progress bar and text.
// Once scene is loaded, we should hide progress bar and text.
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_bar,
MessageDirection::ToWidget,
false,
));
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_text,
MessageDirection::ToWidget,
false,
));
}
// Report progress in UI.
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
.send_message(ProgressBarMessage::progress(
interface.progress_bar,
MessageDirection::ToWidget,
false,
load_context.progress,
));
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_text,
MessageDirection::ToWidget,
false,
));
}
// Report progress in UI.
game.engine
.user_interface
.send_message(ProgressBarMessage::progress(
interface.progress_bar,
game.engine.user_interface.send_message(TextMessage::text(
interface.progress_text,
MessageDirection::ToWidget,
load_context.progress,
format!(
"Loading scene: {}%\n{}",
load_context.progress * 100.0,
load_context.message
),
));
game.engine.user_interface.send_message(TextMessage::text(
interface.progress_text,
MessageDirection::ToWidget,
format!(
"Loading scene: {}%\n{}",
load_context.progress * 100.0,
load_context.message
),
));
}
// Update scene only if it is loaded.
if let Some(game_scene) = game.game_scene.as_mut() {
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut game.engine.scenes[game_scene.scene];
game_scene.player.update(scene, fixed_timestep);
}
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context {
let fps = ctx.renderer.get_statistics().frames_per_second;
let debug_text = format!(
"Example 06 - Save/load\n[W][S][A][D] - walk, \
[SPACE] - jump.\nFPS: {}\nUse [1][2][3][4] to select \
graphics quality.\nUse F5 to save game, F9 to load.",
fps
);
game.engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
debug_text,
));
}
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = game.engine.user_interface.poll_message() {
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
}
game.engine
.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
game.engine
.initialize_graphics_context(window_target)
.unwrap();
}
Event::Suspended => {
game.engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
game.engine.render().unwrap();
}
Event::LoopDestroyed => {
println!("{:?}", fyrox::core::profiler::print());
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = game.engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
// Update scene only if it is loaded.
if let Some(game_scene) = game.game_scene.as_mut() {
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut game.engine.scenes[game_scene.scene];
game_scene.player.update(scene, fixed_timestep);
}
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context
{
let size = size.to_logical(ctx.window.scale_factor());
game.engine
.user_interface
.send_message(WidgetMessage::width(
interface.root,
MessageDirection::ToWidget,
size.width,
));
game.engine
.user_interface
.send_message(WidgetMessage::height(
interface.root,
MessageDirection::ToWidget,
size.height,
));
let fps = ctx.renderer.get_statistics().frames_per_second;
let debug_text = format!(
"Example 06 - Save/load\n[W][S][A][D] - walk, \
[SPACE] - jump.\nFPS: {}\nUse [1][2][3][4] to select \
graphics quality.\nUse F5 to save game, F9 to load.",
fps
);
game.engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
debug_text,
));
}
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = game.engine.user_interface.poll_message() {
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
}
game.engine.update(
fixed_timestep,
control_flow,
&mut lag,
Default::default(),
);
lag -= fixed_timestep;
}
WindowEvent::KeyboardInput { event: input, .. } => {
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene.player.handle_key_event(input, fixed_timestep);
}
let settings = match input.physical_key {
KeyCode::Digit1 => Some(QualitySettings::ultra()),
KeyCode::Digit2 => Some(QualitySettings::high()),
KeyCode::Digit3 => Some(QualitySettings::medium()),
KeyCode::Digit4 => Some(QualitySettings::low()),
_ => None,
};
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
game.engine
.initialize_graphics_context(window_target)
.unwrap();
}
Event::Suspended => {
game.engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
game.engine.render().unwrap();
}
Event::LoopExiting => {
println!("{:?}", fyrox::core::profiler::print());
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = game.engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
if let Some(settings) = settings {
if let GraphicsContext::Initialized(ref mut ctx) =
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
if let GraphicsContext::Initialized(ref ctx) =
game.engine.graphics_context
{
ctx.renderer
.set_quality_settings(&fix_shadows_distance(settings))
.unwrap();
let size = size.to_logical(ctx.window.scale_factor());
game.engine
.user_interface
.send_message(WidgetMessage::width(
interface.root,
MessageDirection::ToWidget,
size.width,
));
game.engine
.user_interface
.send_message(WidgetMessage::height(
interface.root,
MessageDirection::ToWidget,
size.height,
));
}
}
WindowEvent::KeyboardInput { event: input, .. } => {
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene.player.handle_key_event(input, fixed_timestep);
}
// Prevent saving/loading while example is starting.
if game.game_scene.is_some() {
// Save/load bound to classic F5 and F9 keys.
match input.physical_key {
KeyCode::F5 => save(&mut game),
KeyCode::F9 => block_on(load(&mut game)),
_ => (),
let settings = match input.physical_key {
KeyCode::Digit1 => Some(QualitySettings::ultra()),
KeyCode::Digit2 => Some(QualitySettings::high()),
KeyCode::Digit3 => Some(QualitySettings::medium()),
KeyCode::Digit4 => Some(QualitySettings::low()),
_ => None,
};
}
}
_ => (),
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
game.engine.user_interface.process_os_event(&os_event);
if let Some(settings) = settings {
if let GraphicsContext::Initialized(ref mut ctx) =
game.engine.graphics_context
{
ctx.renderer
.set_quality_settings(&fix_shadows_distance(settings))
.unwrap();
}
}
// Prevent saving/loading while example is starting.
if game.game_scene.is_some() {
// Save/load bound to classic F5 and F9 keys.
match input.physical_key {
KeyCode::F5 => save(&mut game),
KeyCode::F9 => block_on(load(&mut game)),
_ => (),
};
}
}
_ => (),
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
game.engine.user_interface.process_os_event(&os_event);
}
}
}
Event::DeviceEvent { event, .. } => {
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene
.player
.handle_device_event(&event, fixed_timestep);
Event::DeviceEvent { event, .. } => {
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene
.player
.handle_device_event(&event, fixed_timestep);
}
}
_ => *control_flow = ControlFlow::Poll,
}
_ => *control_flow = ControlFlow::Poll,
}
});
})
.unwrap();
}
+103 -99
View File
@@ -79,7 +79,7 @@ struct InputController {
}
fn main() {
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let graphics_context_params = GraphicsContextParams {
window_attributes: WindowAttributes {
@@ -126,119 +126,123 @@ fn main() {
// Finally run our event loop which will respond to OS and window events and update
// engine state accordingly. Engine lets you to decide which event should be handled,
// this is minimal working example if how it should be.
event_loop.run(move |event, window_target, control_flow| {
match event {
Event::MainEventsCleared => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
event_loop
.run(move |event, window_target, control_flow| {
match event {
Event::AboutToWait => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut engine.scenes[scene_handle];
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut engine.scenes[scene_handle];
// Rotate model according to input controller state.
if input_controller.rotate_left {
model_angle -= 5.0f32.to_radians();
} else if input_controller.rotate_right {
model_angle += 5.0f32.to_radians();
// Rotate model according to input controller state.
if input_controller.rotate_left {
model_angle -= 5.0f32.to_radians();
} else if input_controller.rotate_right {
model_angle += 5.0f32.to_radians();
}
scene.graph[pivot].local_transform_mut().set_rotation(
UnitQuaternion::from_axis_angle(&Vector3::y_axis(), model_angle),
);
if let GraphicsContext::Initialized(ref graphics_context) =
engine.graphics_context
{
let fps = graphics_context.renderer.get_statistics().frames_per_second;
let text = format!(
"Example 05 - Scene\nUse [A][D] keys to rotate camera.\nFPS: {}",
fps
);
engine.user_interface.send_message(TextMessage::text(
debug_text,
MessageDirection::ToWidget,
text,
));
}
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
scene.graph[pivot].local_transform_mut().set_rotation(
UnitQuaternion::from_axis_angle(&Vector3::y_axis(), model_angle),
);
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = engine.user_interface.poll_message() {
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref graphics_context) =
engine.graphics_context
{
let fps = graphics_context.renderer.get_statistics().frames_per_second;
let text = format!(
"Example 05 - Scene\nUse [A][D] keys to rotate camera.\nFPS: {}",
fps
);
engine.user_interface.send_message(TextMessage::text(
debug_text,
MessageDirection::ToWidget,
text,
));
graphics_context.window.request_redraw();
}
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = engine.user_interface.poll_message() {
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref graphics_context) = engine.graphics_context
{
graphics_context.window.request_redraw();
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key {
KeyCode::KeyA => {
input_controller.rotate_left = input.state == ElementState::Pressed
}
KeyCode::KeyD => {
input_controller.rotate_right = input.state == ElementState::Pressed
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key
{
KeyCode::KeyA => {
input_controller.rotate_left = input.state == ElementState::Pressed
}
KeyCode::KeyD => {
input_controller.rotate_right = input.state == ElementState::Pressed
}
_ => (),
},
_ => (),
},
_ => (),
}
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
}
}
Event::DeviceEvent { .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
}
_ => *control_flow = ControlFlow::Poll,
}
Event::DeviceEvent { .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
}
_ => *control_flow = ControlFlow::Poll,
}
});
})
.unwrap();
}
+1 -1
View File
@@ -127,7 +127,7 @@ pub struct Game {
impl Game {
pub fn new(title: &str) -> (Self, EventLoop<()>) {
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let graphics_context_params = GraphicsContextParams {
window_attributes: WindowAttributes {
+2 -1
View File
@@ -7,6 +7,7 @@
pub mod shared;
use crate::shared::create_camera;
use fyrox::event_loop::EventLoop;
use fyrox::resource::model::{Model, ModelResourceExtension};
use fyrox::resource::texture::Texture;
use fyrox::{
@@ -241,7 +242,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Simple".to_string(),
+261 -251
View File
@@ -74,291 +74,301 @@ fn main() {
// Finally run our event loop which will respond to OS and window events and update
// engine state accordingly.
event_loop.run(move |event, window_target, control_flow| {
match event {
Event::MainEventsCleared => {
// This is main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
event_loop
.run(move |event, window_target, control_flow| {
match event {
Event::AboutToWait => {
// This is main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
// Check each frame if our scene is created - here we just trying to lock context
// without blocking, it is important for main thread to be functional while other
// thread still loading data.
if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock() {
if let Some(mut load_result) = load_context.scene_data.take() {
let animation_player = load_result
.scene
.graph
.find(load_result.player.animation_player, &mut |n| {
n.query_component_ref::<AnimationPlayer>().is_some()
// Check each frame if our scene is created - here we just trying to lock context
// without blocking, it is important for main thread to be functional while other
// thread still loading data.
if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock()
{
if let Some(mut load_result) = load_context.scene_data.take() {
let animation_player = load_result
.scene
.graph
.find(load_result.player.animation_player, &mut |n| {
n.query_component_ref::<AnimationPlayer>().is_some()
})
.unwrap()
.0;
// Once scene is fully loaded, add some signals to walking animation.
(**load_result.scene.graph[animation_player]
.query_component_mut::<AnimationPlayer>()
.unwrap()
.animations_mut())
.get_mut(load_result.player.locomotion_machine.walk_animation)
// Add signals to the walk animation timeline, we'll use signals to emit foot step
// sounds.
.add_signal(AnimationSignal {
id: FOOTSTEP_SIGNAL,
time: 0.2,
name: "Footstep".to_string(),
enabled: true,
})
.unwrap()
.0;
.add_signal(AnimationSignal {
id: FOOTSTEP_SIGNAL,
time: 0.95,
name: "Footstep".to_string(),
enabled: true,
});
// Once scene is fully loaded, add some signals to walking animation.
(**load_result.scene.graph[animation_player]
.query_component_mut::<AnimationPlayer>()
.unwrap()
.animations_mut())
.get_mut(load_result.player.locomotion_machine.walk_animation)
// Add signals to the walk animation timeline, we'll use signals to emit foot step
// sounds.
.add_signal(AnimationSignal {
id: FOOTSTEP_SIGNAL,
time: 0.2,
name: "Footstep".to_string(),
enabled: true,
})
.add_signal(AnimationSignal {
id: FOOTSTEP_SIGNAL,
time: 0.95,
name: "Footstep".to_string(),
enabled: true,
});
// Add scene to engine - engine will take ownership over scene and will return
// you a handle to scene which can be used later on to borrow it and do some
// actions you need.
game.game_scene = Some(GameScene {
scene: game.engine.scenes.add(load_result.scene),
player: load_result.player,
});
// Add scene to engine - engine will take ownership over scene and will return
// you a handle to scene which can be used later on to borrow it and do some
// actions you need.
game.game_scene = Some(GameScene {
scene: game.engine.scenes.add(load_result.scene),
player: load_result.player,
});
// Once scene is loaded, we should hide progress bar and text.
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_bar,
MessageDirection::ToWidget,
false,
));
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_text,
MessageDirection::ToWidget,
false,
));
}
// Once scene is loaded, we should hide progress bar and text.
// Report progress in UI.
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
.send_message(ProgressBarMessage::progress(
interface.progress_bar,
MessageDirection::ToWidget,
false,
load_context.progress,
));
game.engine
.user_interface
.send_message(WidgetMessage::visibility(
interface.progress_text,
MessageDirection::ToWidget,
false,
));
}
// Report progress in UI.
game.engine
.user_interface
.send_message(ProgressBarMessage::progress(
interface.progress_bar,
game.engine.user_interface.send_message(TextMessage::text(
interface.progress_text,
MessageDirection::ToWidget,
load_context.progress,
format!(
"Loading scene: {}%\n{}",
load_context.progress * 100.0,
load_context.message
),
));
game.engine.user_interface.send_message(TextMessage::text(
interface.progress_text,
MessageDirection::ToWidget,
format!(
"Loading scene: {}%\n{}",
load_context.progress * 100.0,
load_context.message
),
));
}
// Update scene only if it is loaded.
if let Some(game_scene) = game.game_scene.as_mut() {
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut game.engine.scenes[game_scene.scene];
game_scene.player.update(scene, fixed_timestep);
let active_state = scene.graph
[game_scene.player.locomotion_machine.machine]
.query_component_ref::<AnimationBlendingStateMachine>()
.unwrap()
.machine()
.layers()[0]
.active_state();
let animation_player = scene.graph[game_scene.player.animation_player]
.query_component_mut::<AnimationPlayer>()
.unwrap();
let mut events = Vec::new();
while let Some(event) = (**animation_player.animations_mut())
.get_mut(game_scene.player.locomotion_machine.walk_animation)
.pop_event()
{
events.push(event);
}
while let Some(event) = events.pop() {
// We must play sound only if it was foot step signal and player was in walking state.
if event.signal_id != FOOTSTEP_SIGNAL
|| active_state != game_scene.player.locomotion_machine.walk_state
// Update scene only if it is loaded.
if let Some(game_scene) = game.game_scene.as_mut() {
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut game.engine.scenes[game_scene.scene];
game_scene.player.update(scene, fixed_timestep);
let active_state = scene.graph
[game_scene.player.locomotion_machine.machine]
.query_component_ref::<AnimationBlendingStateMachine>()
.unwrap()
.machine()
.layers()[0]
.active_state();
let animation_player = scene.graph[game_scene.player.animation_player]
.query_component_mut::<AnimationPlayer>()
.unwrap();
let mut events = Vec::new();
while let Some(event) = (**animation_player.animations_mut())
.get_mut(game_scene.player.locomotion_machine.walk_animation)
.pop_event()
{
continue;
events.push(event);
}
// We'll emit sounds on player's feet.
let mut position =
scene.graph[game_scene.player.pivot].global_position();
position.y -= 0.5;
while let Some(event) = events.pop() {
// We must play sound only if it was foot step signal and player was in walking state.
if event.signal_id != FOOTSTEP_SIGNAL
|| active_state
!= game_scene.player.locomotion_machine.walk_state
{
continue;
}
let foot_step = footstep_buffers
[fyrox::rand::thread_rng().gen_range(0..footstep_buffers.len())]
.clone();
// We'll emit sounds on player's feet.
let mut position =
scene.graph[game_scene.player.pivot].global_position();
position.y -= 0.5;
// Create new temporary foot step sound source.
SoundBuilder::new(
BaseBuilder::new().with_local_transform(
TransformBuilder::new()
.with_local_position(position)
.build(),
),
)
// Specify the name of the effect to which the sound will attach to.
.with_audio_bus(AudioBusGraph::PRIMARY_BUS.to_string())
// Fyrox provides built-in way to create temporary sounds that will die immediately
// after first play. This is very useful for foot step sounds.
.with_play_once(true)
.with_buffer(Some(foot_step))
// Every sound source must be explicitly set to Playing status, otherwise it will be stopped.
.with_status(Status::Playing)
.build(&mut scene.graph);
}
}
let foot_step = footstep_buffers[fyrox::rand::thread_rng()
.gen_range(0..footstep_buffers.len())]
.clone();
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context {
let fps = ctx.renderer.get_statistics().frames_per_second;
let debug_text = format!(
"Example 07 - Sound\n[W][S][A][D] - walk, [SPACE] - jump.\n\
FPS: {}\nUse [1][2][3][4] to select graphics quality.",
fps
);
game.engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
debug_text,
));
}
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = game.engine.user_interface.poll_message() {
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
}
game.engine
.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
game.engine
.initialize_graphics_context(window_target)
.unwrap();
}
Event::Suspended => {
game.engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
game.engine.render().unwrap();
}
Event::LoopDestroyed => {
println!("{:?}", fyrox::core::profiler::print());
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = game.engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
// Create new temporary foot step sound source.
SoundBuilder::new(
BaseBuilder::new().with_local_transform(
TransformBuilder::new()
.with_local_position(position)
.build(),
),
)
// Specify the name of the effect to which the sound will attach to.
.with_audio_bus(AudioBusGraph::PRIMARY_BUS.to_string())
// Fyrox provides built-in way to create temporary sounds that will die immediately
// after first play. This is very useful for foot step sounds.
.with_play_once(true)
.with_buffer(Some(foot_step))
// Every sound source must be explicitly set to Playing status, otherwise it will be stopped.
.with_status(Status::Playing)
.build(&mut scene.graph);
}
}
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context
{
let size = size.to_logical(ctx.window.scale_factor());
game.engine
.user_interface
.send_message(WidgetMessage::width(
interface.root,
MessageDirection::ToWidget,
size.width,
));
game.engine
.user_interface
.send_message(WidgetMessage::height(
interface.root,
MessageDirection::ToWidget,
size.height,
));
let fps = ctx.renderer.get_statistics().frames_per_second;
let debug_text = format!(
"Example 07 - Sound\n[W][S][A][D] - walk, [SPACE] - jump.\n\
FPS: {}\nUse [1][2][3][4] to select graphics quality.",
fps
);
game.engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
debug_text,
));
}
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = game.engine.user_interface.poll_message() {
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
}
game.engine.update(
fixed_timestep,
control_flow,
&mut lag,
Default::default(),
);
lag -= fixed_timestep;
}
WindowEvent::KeyboardInput { event: input, .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene.player.handle_key_event(input, fixed_timestep);
}
let settings = match input.physical_key {
KeyCode::Digit1 => Some(QualitySettings::ultra()),
KeyCode::Digit2 => Some(QualitySettings::high()),
KeyCode::Digit3 => Some(QualitySettings::medium()),
KeyCode::Digit4 => Some(QualitySettings::low()),
_ => None,
};
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
game.engine
.initialize_graphics_context(window_target)
.unwrap();
}
Event::Suspended => {
game.engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
game.engine.render().unwrap();
}
Event::LoopExiting => {
println!("{:?}", fyrox::core::profiler::print());
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = game.engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
if let Some(settings) = settings {
if let GraphicsContext::Initialized(ref mut ctx) =
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
if let GraphicsContext::Initialized(ref ctx) =
game.engine.graphics_context
{
ctx.renderer
.set_quality_settings(&fix_shadows_distance(settings))
.unwrap();
let size = size.to_logical(ctx.window.scale_factor());
game.engine
.user_interface
.send_message(WidgetMessage::width(
interface.root,
MessageDirection::ToWidget,
size.width,
));
game.engine
.user_interface
.send_message(WidgetMessage::height(
interface.root,
MessageDirection::ToWidget,
size.height,
));
}
}
}
_ => (),
}
WindowEvent::KeyboardInput { event: input, .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene.player.handle_key_event(input, fixed_timestep);
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
game.engine.user_interface.process_os_event(&os_event);
let settings = match input.physical_key {
KeyCode::Digit1 => Some(QualitySettings::ultra()),
KeyCode::Digit2 => Some(QualitySettings::high()),
KeyCode::Digit3 => Some(QualitySettings::medium()),
KeyCode::Digit4 => Some(QualitySettings::low()),
_ => None,
};
if let Some(settings) = settings {
if let GraphicsContext::Initialized(ref mut ctx) =
game.engine.graphics_context
{
ctx.renderer
.set_quality_settings(&fix_shadows_distance(settings))
.unwrap();
}
}
}
_ => (),
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
game.engine.user_interface.process_os_event(&os_event);
}
}
}
Event::DeviceEvent { event, .. } => {
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene
.player
.handle_device_event(&event, fixed_timestep);
Event::DeviceEvent { event, .. } => {
if let Some(game_scene) = game.game_scene.as_mut() {
game_scene
.player
.handle_device_event(&event, fixed_timestep);
}
}
_ => *control_flow = ControlFlow::Poll,
}
_ => *control_flow = ControlFlow::Poll,
}
});
})
.unwrap();
}
+2 -1
View File
@@ -3,6 +3,7 @@
pub mod shared;
use crate::shared::create_camera;
use fyrox::event_loop::EventLoop;
use fyrox::resource::texture::Texture;
use fyrox::{
asset::manager::ResourceManager,
@@ -277,7 +278,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Terrain".to_string(),
+2 -1
View File
@@ -1,3 +1,4 @@
use fyrox::event_loop::EventLoop;
use fyrox::{
core::{algebra::Vector2, color::Color, pool::Handle},
engine::{executor::Executor, GraphicsContextParams},
@@ -41,7 +42,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
Default::default(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Text Box".to_string(),
+1 -1
View File
@@ -90,7 +90,7 @@ impl PluginConstructor for GameConstructor {
fn main() {
let mut executor = Executor::from_params(
EventLoop::new(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
transparent: true,
+139 -133
View File
@@ -323,7 +323,7 @@ async fn create_scene(resource_manager: ResourceManager) -> GameScene {
}
fn main() {
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let graphics_context_params = GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - User Interface".to_string(),
@@ -365,154 +365,160 @@ fn main() {
// Finally run our event loop which will respond to OS and window events and update
// engine state accordingly. Engine lets you to decide which event should be handled,
// this is minimal working example if how it should be.
event_loop.run(move |event, window_target, control_flow| {
match event {
Event::MainEventsCleared => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
event_loop
.run(move |event, window_target, control_flow| {
match event {
Event::AboutToWait => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut engine.scenes[scene_handle];
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut engine.scenes[scene_handle];
scene.graph[model_handle]
.local_transform_mut()
.set_scale(Vector3::new(model_scale, model_scale, model_scale))
.set_rotation(UnitQuaternion::from_axis_angle(
&Vector3::y_axis(),
model_angle.to_radians(),
));
scene.graph[model_handle]
.local_transform_mut()
.set_scale(Vector3::new(model_scale, model_scale, model_scale))
.set_rotation(UnitQuaternion::from_axis_angle(
&Vector3::y_axis(),
model_angle.to_radians(),
));
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
let fps = ctx.renderer.get_statistics().frames_per_second;
engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
format!("Example 04 - User Interface\nFPS: {}", fps),
));
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
let fps = ctx.renderer.get_statistics().frames_per_second;
engine.user_interface.send_message(TextMessage::text(
interface.debug_text,
MessageDirection::ToWidget,
format!("Example 04 - User Interface\nFPS: {}", fps),
));
}
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
// It is very important to "pump" messages from UI. This our main point where we communicate
// with user interface. As you saw earlier, there is no callbacks on UI elements, instead we
// use messages to get information from UI elements. This provides perfect decoupling of logic
// from UI elements and works well with borrow checker.
while let Some(ui_message) = engine.user_interface.poll_message() {
if let Some(ScrollBarMessage::Value(value)) =
ui_message.data::<ScrollBarMessage>()
{
if ui_message.direction() == MessageDirection::FromWidget {
// Some of our scroll bars has changed its value. Check which one.
// Each message has source - a handle of UI element that created this message.
// It is used to understand from which UI element message has come.
if ui_message.destination() == interface.scale {
model_scale = *value;
} else if ui_message.destination() == interface.yaw {
model_angle = *value;
// It is very important to "pump" messages from UI. This our main point where we communicate
// with user interface. As you saw earlier, there is no callbacks on UI elements, instead we
// use messages to get information from UI elements. This provides perfect decoupling of logic
// from UI elements and works well with borrow checker.
while let Some(ui_message) = engine.user_interface.poll_message() {
if let Some(ScrollBarMessage::Value(value)) =
ui_message.data::<ScrollBarMessage>()
{
if ui_message.direction() == MessageDirection::FromWidget {
// Some of our scroll bars has changed its value. Check which one.
// Each message has source - a handle of UI element that created this message.
// It is used to understand from which UI element message has come.
if ui_message.destination() == interface.scale {
model_scale = *value;
} else if ui_message.destination() == interface.yaw {
model_angle = *value;
}
}
}
} else if let Some(ButtonMessage::Click) = ui_message.data::<ButtonMessage>() {
// Once we received Click event from Reset button, we have to reset angle and scale
// of model. To do that we borrow each UI element in engine and set its value directly.
// This is not ideal because there is tight coupling between UI code and model values,
// but still good enough for example.
if ui_message.destination() == interface.reset {
engine.user_interface.send_message(ScrollBarMessage::value(
interface.scale,
MessageDirection::ToWidget,
DEFAULT_MODEL_SCALE,
));
engine.user_interface.send_message(ScrollBarMessage::value(
interface.yaw,
MessageDirection::ToWidget,
DEFAULT_MODEL_ROTATION,
));
}
} else if let Some(DropdownListMessage::SelectionChanged(Some(idx))) =
ui_message.data::<DropdownListMessage>()
{
// Video mode has changed and we must change video mode to what user wants.
if ui_message.destination() == interface.resolutions {
let video_mode = interface.video_modes.get(*idx).unwrap();
} else if let Some(ButtonMessage::Click) =
ui_message.data::<ButtonMessage>()
{
// Once we received Click event from Reset button, we have to reset angle and scale
// of model. To do that we borrow each UI element in engine and set its value directly.
// This is not ideal because there is tight coupling between UI code and model values,
// but still good enough for example.
if ui_message.destination() == interface.reset {
engine.user_interface.send_message(ScrollBarMessage::value(
interface.scale,
MessageDirection::ToWidget,
DEFAULT_MODEL_SCALE,
));
engine.user_interface.send_message(ScrollBarMessage::value(
interface.yaw,
MessageDirection::ToWidget,
DEFAULT_MODEL_ROTATION,
));
}
} else if let Some(DropdownListMessage::SelectionChanged(Some(idx))) =
ui_message.data::<DropdownListMessage>()
{
// Video mode has changed and we must change video mode to what user wants.
if ui_message.destination() == interface.resolutions {
let video_mode = interface.video_modes.get(*idx).unwrap();
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
ctx.window.set_fullscreen(Some(Fullscreen::Exclusive(
video_mode.clone(),
)));
if let GraphicsContext::Initialized(ref ctx) =
engine.graphics_context
{
ctx.window.set_fullscreen(Some(Fullscreen::Exclusive(
video_mode.clone(),
)));
// Due to some weird bug in winit it does not send Resized event.
if let Err(e) = engine.set_frame_size((
video_mode.size().width,
video_mode.size().height,
)) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
// Due to some weird bug in winit it does not send Resized event.
if let Err(e) = engine.set_frame_size((
video_mode.size().width,
video_mode.size().height,
)) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
}
}
}
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
ctx.window.request_redraw();
}
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
ctx.window.request_redraw();
}
WindowEvent::KeyboardInput { event: input, .. } => {
if input.state == ElementState::Pressed
&& input.physical_key == KeyCode::Escape
{
*control_flow = ControlFlow::Exit;
}
}
_ => (),
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match &event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size((*size).into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
WindowEvent::KeyboardInput { event: input, .. } => {
if input.state == ElementState::Pressed
&& input.physical_key == KeyCode::Escape
{
*control_flow = ControlFlow::Exit;
}
}
_ => (),
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
}
}
_ => *control_flow = ControlFlow::Poll,
}
_ => *control_flow = ControlFlow::Poll,
}
});
})
.unwrap();
}
+25 -7
View File
@@ -10,10 +10,11 @@ use crate::{
Engine, EngineInitParams, GraphicsContext, GraphicsContextParams, SerializationContext,
},
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
plugin::PluginConstructor,
scene::loader::AsyncSceneLoader,
utils::translate_event,
window::WindowAttributes,
};
use clap::Parser;
use std::{
@@ -21,7 +22,6 @@ use std::{
sync::Arc,
time::Duration,
};
use winit::window::WindowAttributes;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
@@ -90,7 +90,7 @@ impl Executor {
/// way to create an executor see [`Executor::from_params`].
pub fn new() -> Self {
Self::from_params(
EventLoop::new(),
EventLoop::new().unwrap(),
GraphicsContextParams {
window_attributes: WindowAttributes {
resizable: true,
@@ -132,8 +132,8 @@ impl Executor {
self.engine.add_plugin_constructor(plugin)
}
/// Runs the executor - starts your game. This function is never returns.
pub fn run(mut self) -> ! {
/// Runs the executor - starts your game.
pub fn run(mut self) {
let mut engine = self.engine;
let event_loop = self.event_loop;
let headless = self.headless;
@@ -156,7 +156,9 @@ impl Executor {
let fixed_time_step = 1.0 / self.desired_update_rate;
let mut lag = 0.0;
event_loop.run(move |event, window_target, control_flow| {
run_executor(event_loop, move |event, window_target, control_flow| {
control_flow.set_wait_timeout(Duration::from_millis(16));
engine.handle_os_event_by_plugins(&event, fixed_time_step, control_flow, &mut lag);
let scenes = engine
@@ -196,7 +198,7 @@ impl Executor {
&mut lag,
);
}
Event::MainEventsCleared => {
Event::AboutToWait => {
prepare_control_flow(control_flow, fixed_time_step);
if let Some(loader) = self.loader.as_ref() {
@@ -273,3 +275,19 @@ fn prepare_control_flow(control_flow: &mut ControlFlow, #[allow(unused_variables
control_flow.set_poll();
}
}
fn run_executor<T, F>(event_loop: EventLoop<T>, callback: F)
where
F: FnMut(Event<T>, &EventLoopWindowTarget<T>, &mut ControlFlow) + 'static,
{
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::EventLoopExtWebSys;
event_loop.spawn(callback);
}
#[cfg(not(target_arch = "wasm32"))]
{
event_loop.run(callback).unwrap();
}
}