Vsync instead of fps + print display modes
[kaka/rust-sdl-test.git] / src / game / app.rs
... / ...
CommitLineData
1use rand::Rng;
2use sdl2::event::Event;
3use sdl2::{EventPump, VideoSubsystem};
4use sdl2::keyboard::Keycode;
5use sdl2::pixels::Color;
6use sdl2::render::BlendMode;
7use sdl2::render::Canvas;
8use sdl2::video::{SwapInterval, Window};
9
10use ::{SCREEN_HEIGHT, SCREEN_WIDTH};
11use boll::*;
12use common::{Point2D, Rect};
13use sprites::SpriteManager;
14use NS_PER_FRAME;
15use point; // defined in common, but loaded from main...
16
17pub type Nanoseconds = u64;
18
19#[derive(Default)]
20pub struct AppBuilder {
21 resolution: Rect<u16>,
22 state: Option<Box<dyn AppState>>,
23 title: Option<String>,
24}
25
26impl AppBuilder {
27 pub fn with_resolution(mut self, width: u16, height: u16) -> Self {
28 self.resolution = Rect { width, height };
29 self
30 }
31
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
42 pub fn build(self) -> Result<App, String> {
43 let context = sdl2::init().unwrap();
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
50 .window(&self.title.unwrap(), self.resolution.width.into(), self.resolution.height.into())
51 .position_centered()
52 // .fullscreen()
53 // .fullscreen_desktop()
54 .opengl()
55 .build().unwrap();
56 context.mouse().show_cursor(false);
57
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();
63
64 video.gl_set_swap_interval(SwapInterval::VSync)?;
65
66 let event_pump = context.event_pump()?;
67 let sprites = SpriteManager::new(canvas.texture_creator());
68
69 Ok(App {
70 canvas,
71 event_pump,
72 sprites,
73 state: self.state.unwrap_or(Box::new(ActiveState::new())),
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());
91 }
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 }
105
106 pub fn load_sprites(&mut self, sprites: &[(&str, &str)]) {
107 for (name, file) in sprites {
108 self.sprites.load(name, file);
109 }
110 }
111}
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 {
128 pub fn new() -> ActiveState {
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 {
141 for _i in 0..(-delta) {
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) {
158 for b in &mut self.bolls {
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>) {
170 for b in &self.bolls {
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}