Vsync instead of fps + print display modes
[kaka/rust-sdl-test.git] / src / game / app.rs
CommitLineData
95e3e10d
TW
1use rand::Rng;
2use sdl2::event::Event;
fea68d56 3use sdl2::{EventPump, VideoSubsystem};
95e3e10d 4use sdl2::keyboard::Keycode;
3bfea951
TW
5use sdl2::pixels::Color;
6use sdl2::render::BlendMode;
7use sdl2::render::Canvas;
fea68d56 8use sdl2::video::{SwapInterval, Window};
3bfea951 9
9c06350b 10use ::{SCREEN_HEIGHT, SCREEN_WIDTH};
95e3e10d 11use boll::*;
6edafdc0 12use common::{Point2D, Rect};
3bfea951 13use sprites::SpriteManager;
95e3e10d 14use NS_PER_FRAME;
787dbfb4 15use point; // defined in common, but loaded from main...
95e3e10d
TW
16
17pub type Nanoseconds = u64;
3bfea951 18
6edafdc0
TW
19#[derive(Default)]
20pub struct AppBuilder {
21 resolution: Rect<u16>,
6edafdc0
TW
22 state: Option<Box<dyn AppState>>,
23 title: Option<String>,
3bfea951
TW
24}
25
6edafdc0
TW
26impl AppBuilder {
27 pub fn with_resolution(mut self, width: u16, height: u16) -> Self {
28 self.resolution = Rect { width, height };
29 self
30 }
31
6edafdc0
TW
32 pub fn with_state(mut self, state: Box<dyn AppState>) -> Self {
33 self.state = Some(state);
34 self
35 }
36
37 pub fn with_title(mut self, title: &str) -> Self {
38 self.title = Some(title.to_string());
39 self
40 }
41
fea68d56 42 pub fn build(self) -> Result<App, String> {
3bfea951 43 let context = sdl2::init().unwrap();
fea68d56
TW
44 sdl2::image::init(sdl2::image::InitFlag::PNG)?;
45 let video = context.video()?;
46
47 self.print_video_display_modes(&video);
48
49 let window = video
6edafdc0 50 .window(&self.title.unwrap(), self.resolution.width.into(), self.resolution.height.into())
3bfea951 51 .position_centered()
fea68d56
TW
52 // .fullscreen()
53 // .fullscreen_desktop()
3bfea951 54 .opengl()
6edafdc0 55 .build().unwrap();
3bfea951 56 context.mouse().show_cursor(false);
6edafdc0 57
3bfea951
TW
58 let mut canvas = window.into_canvas().build().unwrap();
59 canvas.set_blend_mode(BlendMode::Add);
60 canvas.set_draw_color(Color::RGB(0, 0, 0));
61 canvas.clear();
62 canvas.present();
6edafdc0 63
fea68d56
TW
64 video.gl_set_swap_interval(SwapInterval::VSync)?;
65
66 let event_pump = context.event_pump()?;
3bfea951 67 let sprites = SpriteManager::new(canvas.texture_creator());
6edafdc0 68
fea68d56 69 Ok(App {
3bfea951
TW
70 canvas,
71 event_pump,
72 sprites,
6edafdc0 73 state: self.state.unwrap_or(Box::new(ActiveState::new())),
fea68d56
TW
74 })
75 }
76
77 fn print_video_display_modes(&self, video: &VideoSubsystem) {
78 println!("video subsystem: {:?}", video);
79 println!("current_video_driver: {:?}", video.current_video_driver());
80 for display in 0..video.num_video_displays().unwrap() {
81 println!("=== display {} - {} ===", display, video.display_name(display).unwrap());
82 println!(" display_bounds: {:?}", video.display_bounds(display).unwrap());
83 println!(" num_display_modes: {:?}", video.num_display_modes(display).unwrap());
84 println!(" desktop_display_mode: {:?}", video.desktop_display_mode(display).unwrap());
85 println!(" current_display_mode: {:?}", video.current_display_mode(display).unwrap());
86 for mode in 0..video.num_display_modes(display).unwrap() {
87 println!(" {:2}: {:?}", mode, video.display_mode(display, mode).unwrap());
88 }
89 }
90 println!("swap interval: {:?}", video.gl_get_swap_interval());
3bfea951 91 }
6edafdc0
TW
92}
93
94pub struct App {
95 pub canvas: Canvas<Window>,
96 pub event_pump: EventPump,
97 pub sprites: SpriteManager,
98 pub state: Box<dyn AppState>,
99}
100
101impl App {
102 pub fn new() -> AppBuilder {
103 Default::default()
104 }
3bfea951 105
1e322944 106 pub fn load_sprites(&mut self, sprites: &[(&str, &str)]) {
3bfea951
TW
107 for (name, file) in sprites {
108 self.sprites.load(name, file);
109 }
110 }
111}
95e3e10d
TW
112
113pub trait AppState {
114 fn update(&mut self, dt: Nanoseconds);
115 fn render(&self, canvas: &mut Canvas<Window>);
116 fn leave(&self);
117 fn on_event(&mut self, event: Event);
118}
119
120type Bollar = Vec<Box<dyn Boll>>;
121
122pub struct ActiveState {
123 bolls: Bollar,
124 boll_size: u32,
125}
126
127impl ActiveState {
6edafdc0 128 pub fn new() -> ActiveState {
95e3e10d
TW
129 ActiveState {
130 bolls: Bollar::new(),
131 boll_size: 1,
132 }
133 }
134
135 fn change_boll_count(&mut self, delta: i32) {
136 if delta > 0 {
137 for _i in 0..delta {
138 self.add_boll();
139 }
140 } else if delta < 0 {
5cbbebbe 141 for _i in 0..(-delta) {
95e3e10d
TW
142 self.bolls.pop();
143 }
144 }
145 }
146
147 fn add_boll(&mut self) {
148 let mut rng = rand::thread_rng();
149 self.bolls.push(Box::new(SquareBoll {
150 pos: point!(rng.gen_range(0, SCREEN_WIDTH) as f64, rng.gen_range(0, SCREEN_HEIGHT) as f64),
151 vel: point!(rng.gen_range(-2.0, 2.0), rng.gen_range(-2.0, 2.0)),
152 }));
153 }
154}
155
156impl AppState for ActiveState {
157 fn update(&mut self, dt: Nanoseconds) {
93679b27 158 for b in &mut self.bolls {
95e3e10d
TW
159 b.update();
160 }
161
162 match dt {
163 ns if ns < (NS_PER_FRAME - 90_0000) as u64 => { self.change_boll_count(100) }
164 ns if ns > (NS_PER_FRAME + 90_0000) as u64 => { self.change_boll_count(-100) }
165 _ => {}
166 }
167 }
168
169 fn render(&self, canvas: &mut Canvas<Window>) {
93679b27 170 for b in &self.bolls {
95e3e10d
TW
171 b.draw(canvas, self.boll_size);
172 }
173 }
174
175 fn leave(&self) {
176 println!("number of bolls: {}", self.bolls.len());
177 }
178
179 fn on_event(&mut self, event: Event) {
180 match event {
181 Event::KeyDown { keycode: Some(Keycode::KpPlus), .. } => { self.boll_size = std::cmp::min(self.boll_size + 1, 32) }
182 Event::KeyDown { keycode: Some(Keycode::KpMinus), .. } => { self.boll_size = std::cmp::max(self.boll_size - 1, 1) }
183 Event::MouseMotion { x, y, .. } => {
184 self.bolls.push(Box::new(CircleBoll::new(
185 point!(x as f64, y as f64),
186 point!(0.0, 0.0),
187 )))
188 }
189 _ => {}
190 }
191 }
192}