1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
use std::{
hint,
sync::Arc,
thread,
time::{Duration, Instant},
};
use winit::{
application::ApplicationHandler,
dpi::LogicalSize,
event::{ElementState, WindowEvent},
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
window::{Window, WindowId},
};
struct App {
window: Option<Arc<Window>>,
}
impl App {
fn new() -> Self {
Self { window: None }
}
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
// ウィンドウメッセージをpollで捌く
// キューが空になる度に`about_to_wait`メソッドが呼ばれる
event_loop.set_control_flow(ControlFlow::Poll);
let attributes = Window::default_attributes()
.with_inner_size(LogicalSize::new(1280, 720))
.with_fullscreen(None);
let window = match event_loop.create_window(attributes) {
Ok(v) => Arc::new(v),
Err(e) => panic!("error: {e}"),
};
self.window = Some(window);
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::MouseInput { state, button, .. } => match state {
ElementState::Pressed => {
println!("pressed {:?}", button);
}
ElementState::Released => {
println!("released {:?}", button);
}
},
WindowEvent::CursorMoved { position, .. } => {
let Some(window) = self.window.as_ref() else {
return;
};
println!(
"moved {:?}",
position.to_logical::<f32>(window.scale_factor())
);
}
WindowEvent::CursorLeft { .. } => {
println!("left");
}
WindowEvent::Focused(false) | WindowEvent::Occluded(true) => {
println!("focused or occluded");
}
_ => {}
}
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
let start = Instant::now();
let Some(_) = &self.window else {
// NOTE: `window`が`Some`になる`resumed`メソッドで`ControlFlow::Poll`を指定しているため、
// ここで`ControlFlow::WaitUntil`を指定しても問題ない。
event_loop.set_control_flow(ControlFlow::WaitUntil(start + Duration::from_millis(16)));
return;
};
sync_60fps(start);
}
}
/// 60fpsになるようにスレッドを停止する関数
///
/// * start - フレーム開始時刻
///
/// 15.66...msをsleepし、不足時間をスピンウェイトする。
/// 正直アニメーションの強いゲームではないので厳密さは要らないのだけれど、
/// 一応アニメーションのあるゲームだからね多少はね。
fn sync_60fps(start: Instant) {
const TARGET_FRAME_TIME: Duration = Duration::from_nanos(1_000_000_000 / 60); // 60FPS
const SPIN_THRESHOLD: Duration = Duration::from_millis(1);
let elapsed = start.elapsed();
if elapsed < TARGET_FRAME_TIME {
let remaining = TARGET_FRAME_TIME - elapsed;
if remaining > SPIN_THRESHOLD {
thread::sleep(remaining - SPIN_THRESHOLD);
}
while start.elapsed() < TARGET_FRAME_TIME {
hint::spin_loop();
}
}
}
fn main() {
let event_loop = match EventLoop::new() {
Ok(v) => v,
Err(e) => panic!("error: {e}"),
};
match event_loop.run_app(&mut App::new()) {
Ok(()) => {}
Err(e) => panic!("error: {e}"),
}
}
|