mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
222 lines
8.4 KiB
Rust
222 lines
8.4 KiB
Rust
//! Example 05. Simple scene.
|
|
//!
|
|
//! Difficulty: Easy.
|
|
//!
|
|
//! This example shows how to load simple scene made in [Fyroxed](https://github.com/FyroxEngine/Fyrox)
|
|
//!
|
|
//! It is almost the same as Example 01, even easier.
|
|
|
|
pub mod shared;
|
|
|
|
use crate::shared::create_camera;
|
|
use fyrox::{
|
|
core::{
|
|
algebra::{UnitQuaternion, Vector3},
|
|
color::Color,
|
|
pool::Handle,
|
|
},
|
|
engine::{resource_manager::ResourceManager, Engine},
|
|
event::{ElementState, Event, VirtualKeyCode, WindowEvent},
|
|
event_loop::{ControlFlow, EventLoop},
|
|
gui::{
|
|
message::MessageDirection,
|
|
text::{TextBuilder, TextMessage},
|
|
widget::WidgetBuilder,
|
|
BuildContext, UiNode,
|
|
},
|
|
scene::{base::BaseBuilder, node::Node, Scene},
|
|
utils::{
|
|
log::{Log, MessageKind},
|
|
translate_event,
|
|
},
|
|
};
|
|
use std::time::Instant;
|
|
|
|
fn create_ui(ctx: &mut BuildContext) -> Handle<UiNode> {
|
|
TextBuilder::new(WidgetBuilder::new()).build(ctx)
|
|
}
|
|
|
|
struct GameScene {
|
|
scene: Scene,
|
|
pivot: Handle<Node>,
|
|
}
|
|
|
|
async fn create_scene(resource_manager: ResourceManager) -> GameScene {
|
|
let mut scene = Scene::new();
|
|
|
|
// Set ambient light.
|
|
scene.ambient_lighting_color = Color::opaque(80, 80, 80);
|
|
|
|
// Camera is our eyes in the world - you won't see anything without it.
|
|
let pivot = BaseBuilder::new()
|
|
.with_children(&[create_camera(
|
|
resource_manager.clone(),
|
|
Vector3::new(0.0, 4.0, -8.0),
|
|
&mut scene.graph,
|
|
)
|
|
.await])
|
|
.build(&mut scene.graph);
|
|
|
|
// There is no difference between scene created in Fyroxed and any other
|
|
// model file, so any scene can be used directly as resource.
|
|
resource_manager
|
|
.request_model("examples/data/test_scene.rgs")
|
|
.await
|
|
.unwrap()
|
|
.instantiate(&mut scene);
|
|
|
|
GameScene { scene, pivot }
|
|
}
|
|
|
|
struct InputController {
|
|
rotate_left: bool,
|
|
rotate_right: bool,
|
|
}
|
|
|
|
fn main() {
|
|
let event_loop = EventLoop::new();
|
|
|
|
let window_builder = fyrox::window::WindowBuilder::new()
|
|
.with_title("Example - Scene")
|
|
.with_resizable(true);
|
|
|
|
let mut engine = Engine::new(window_builder, &event_loop, true).unwrap();
|
|
|
|
// Create simple user interface that will show some useful info.
|
|
let debug_text = create_ui(&mut engine.user_interface.build_ctx());
|
|
|
|
// Create test scene.
|
|
let GameScene { scene, pivot } =
|
|
fyrox::core::futures::executor::block_on(create_scene(engine.resource_manager.clone()));
|
|
|
|
// 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.
|
|
let scene_handle = engine.scenes.add(scene);
|
|
|
|
let clock = Instant::now();
|
|
let fixed_timestep = 1.0 / 60.0;
|
|
let mut elapsed_time = 0.0;
|
|
|
|
// We will rotate model using keyboard input.
|
|
let mut model_angle = 0.0f32.to_radians();
|
|
|
|
// Create input controller - it will hold information about needed actions.
|
|
let mut input_controller = InputController {
|
|
rotate_left: false,
|
|
rotate_right: false,
|
|
};
|
|
|
|
// 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, _, 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 mut dt = clock.elapsed().as_secs_f32() - elapsed_time;
|
|
while dt >= fixed_timestep {
|
|
dt -= fixed_timestep;
|
|
elapsed_time += 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];
|
|
|
|
// 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),
|
|
);
|
|
|
|
let fps = engine.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);
|
|
}
|
|
|
|
// 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.
|
|
engine.get_window().request_redraw();
|
|
}
|
|
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 { input, .. } => {
|
|
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
|
|
if let Some(key_code) = input.virtual_keycode {
|
|
match key_code {
|
|
VirtualKeyCode::A => {
|
|
input_controller.rotate_left =
|
|
input.state == ElementState::Pressed
|
|
}
|
|
VirtualKeyCode::D => {
|
|
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);
|
|
}
|
|
}
|
|
Event::DeviceEvent { .. } => {
|
|
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
|
|
}
|
|
_ => *control_flow = ControlFlow::Poll,
|
|
}
|
|
});
|
|
}
|