mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
migrate to winit 0.29.1-beta + wasm fixes
- use `EventLoop::spawn` as recommended in `EventLoop::run` docs
This commit is contained in:
+2
-1
@@ -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(),
|
||||
|
||||
@@ -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
@@ -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
@@ -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(),
|
||||
|
||||
@@ -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
@@ -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(),
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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(),
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user