Added a basic gamestate with a controlled mario
authorTomas Wenström <tomas.wenstrom@gmail.com>
Tue, 12 Jan 2021 17:40:31 +0000 (18:40 +0100)
committerTomas Wenström <tomas.wenstrom@gmail.com>
Tue, 12 Jan 2021 17:40:31 +0000 (18:40 +0100)
src/common.rs
src/core/app.rs
src/core/controller.rs
src/core/game.rs [new file with mode: 0644]
src/core/mod.rs
src/main.rs

index 442b11c..52b7324 100644 (file)
@@ -1,5 +1,7 @@
 use std::ops::{Add, AddAssign, Mul};
 
+pub type Nanoseconds = u64;
+
 #[macro_export]
 macro_rules! point {
     ( $x:expr, $y:expr ) => {
@@ -7,7 +9,7 @@ macro_rules! point {
     };
 }
 
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Debug, Default, Copy, Clone, PartialEq)]
 pub struct Point2D<T> {
     pub x: T,
     pub y: T,
@@ -37,6 +39,15 @@ impl<T: AddAssign> AddAssign for Point2D<T> {
     }
 }
 
+impl<T> From<(T, T)> for Point2D<T> {
+    fn from(item: (T, T)) -> Self {
+        Point2D {
+            x: item.0,
+            y: item.1,
+        }
+    }
+}
+
 #[macro_export]
 macro_rules! rect {
     ( $x:expr, $y:expr ) => {
index 8c07417..62d7507 100644 (file)
@@ -1,5 +1,5 @@
 use boll::*;
-use common::{Point2D, Rect};
+use common::{Nanoseconds, Point2D, Rect};
 use core::controller::ControllerManager;
 use point; // defined in common, but loaded from main...
 use rand::Rng;
@@ -15,8 +15,6 @@ use sprites::SpriteManager;
 use std::f32::consts::PI;
 use time::PreciseTime;
 
-pub type Nanoseconds = u64;
-
 const FPS: u32 = 60;
 const NS_PER_FRAME: u32 = 1_000_000_000 / FPS;
 
@@ -47,7 +45,7 @@ impl AppBuilder {
         let context = sdl2::init().unwrap();
         sdl2::image::init(sdl2::image::InitFlag::PNG)?;
         let video = context.video()?;
-        self.print_video_display_modes(&video);
+        //self.print_video_display_modes(&video);
 
         let window = video
             .window(
@@ -86,6 +84,7 @@ impl AppBuilder {
         })
     }
 
+    #[allow(dead_code)]
     fn print_video_display_modes(&self, video: &VideoSubsystem) {
         println!("video subsystem: {:?}", video);
         println!("current_video_driver: {:?}", video.current_video_driver());
@@ -131,7 +130,7 @@ pub struct App {
     event_pump: EventPump,
     sprites: SpriteManager,
     state: Box<dyn AppState>,
-    ctrl_man: ControllerManager,
+    pub ctrl_man: ControllerManager,
 }
 
 impl App {
@@ -147,10 +146,12 @@ impl App {
     }
 
     pub fn start(&mut self) {
-        let mut frame_count: u64 = 0;
-        let mut fps_time = PreciseTime::now();
+        // let mut frame_count: u64 = 0;
+        // let mut fps_time = PreciseTime::now();
         let mut last_time = PreciseTime::now();
 
+       self.state.enter(&mut self.ctrl_man);
+
         'running: loop {
            if let Err(_) = self.handle_events() {
                break 'running;
@@ -163,14 +164,14 @@ impl App {
 
            self.render();
 
-            frame_count += 1;
-            if frame_count == FPS as u64 {
-                let duration = fps_time.to(PreciseTime::now()).num_nanoseconds().unwrap() as f64
-                    / 1_000_000_000.0;
-                // println!("fps: {}", frame_count as f64 / duration);
-                frame_count = 0;
-                fps_time = PreciseTime::now();
-            }
+            // frame_count += 1;
+            // if frame_count == FPS as u64 {
+            //     let duration = fps_time.to(PreciseTime::now()).num_nanoseconds().unwrap() as f64
+            //         / 1_000_000_000.0;
+            //     // println!("fps: {}", frame_count as f64 / duration);
+            //     frame_count = 0;
+            //     fps_time = PreciseTime::now();
+            // }
         }
 
         self.state.leave();
@@ -180,10 +181,6 @@ impl App {
         for event in self.event_pump.poll_iter() {
            self.ctrl_man.handle_event(&event);
             match event {
-               Event::ControllerButtonDown { .. } => {
-                   let c = self.ctrl_man.controllers[0].clone();
-                   c.borrow().rumble(1.0, 300);
-               }
                 Event::Quit { .. }
                 | Event::KeyDown {
                     keycode: Some(Keycode::Escape),
@@ -246,7 +243,7 @@ impl App {
                 } => {
                     println!("window focus lost")
                 }
-                _ => self.state.on_event(event),
+                _ => self.state.handle_event(event),
             }
         }
        Ok(())
@@ -261,10 +258,11 @@ impl App {
 }
 
 pub trait AppState {
+    fn enter(&mut self, ctrl_man: &mut ControllerManager);
+    fn leave(&mut self);
     fn update(&mut self, dt: Nanoseconds);
     fn render(&mut self, canvas: &mut Canvas<Window>, sprites: &mut SpriteManager);
-    fn leave(&self);
-    fn on_event(&mut self, event: Event);
+    fn handle_event(&mut self, event: Event);
 }
 
 type Bollar = Vec<Box<dyn Boll>>;
@@ -313,6 +311,8 @@ impl ActiveState {
 }
 
 impl AppState for ActiveState {
+    fn enter(&mut self, ctrl_man: &mut ControllerManager) {}
+
     fn update(&mut self, dt: Nanoseconds) {
         for b in &mut self.bolls {
             b.update();
@@ -430,11 +430,11 @@ impl AppState for ActiveState {
         }
     }
 
-    fn leave(&self) {
+    fn leave(&mut self) {
         println!("number of bolls: {}", self.bolls.len());
     }
 
-    fn on_event(&mut self, event: Event) {
+    fn handle_event(&mut self, event: Event) {
         match event {
             Event::KeyDown {
                 keycode: Some(Keycode::KpPlus),
index f565070..fb56465 100644 (file)
@@ -1,31 +1,39 @@
+use std::collections::HashMap;
 use std::cell::RefCell;
 use sdl2::haptic::Haptic;
 use sdl2::HapticSubsystem;
-use sdl2::GameControllerSubsystem;
+pub use sdl2::GameControllerSubsystem;
 use sdl2::event::Event;
 use sdl2::controller::GameController;
 use std::rc::Rc;
 
 //#[derive(Debug)]
 pub struct ControllerManager {
-    ctrl: GameControllerSubsystem,
+    pub ctrl: GameControllerSubsystem,
     haptic: Rc<HapticSubsystem>,
-    pub controllers: Vec<Rc<RefCell<Controller>>>,
+    pub controllers: HashMap<u32, Rc<RefCell<Controller>>>,
 }
 
 //#[derive(Debug)]
 pub struct Controller {
-    id: u32,
     pub ctrl: GameController,
     haptic: Option<Rc<RefCell<Haptic>>>,
 }
 
 impl ControllerManager {
     pub fn new(ctrl: GameControllerSubsystem, haptic: HapticSubsystem) -> Self {
-       ControllerManager {
+       let mut c = ControllerManager {
            ctrl,
            haptic: Rc::new(haptic),
-           controllers: vec![],
+           controllers: HashMap::new(),
+       };
+       c.init();
+       c
+    }
+
+    fn init(&mut self) {
+       for i in 0..self.ctrl.num_joysticks().unwrap() {
+           self.add_device(i);
        }
     }
 
@@ -56,7 +64,11 @@ impl ControllerManager {
            Err(_) => None
        };
 
-       let detached = self.controllers.iter().find(|c| !c.borrow().ctrl.attached());
+       if self.controllers.contains_key(&id) {
+           return;
+       }
+
+       let detached = self.controllers.values().find(|c| !c.borrow().ctrl.attached());
        match detached {
            Some(c) => {
                let mut c = c.borrow_mut();
@@ -64,8 +76,8 @@ impl ControllerManager {
                c.haptic = haptic.map(|h| Rc::new(RefCell::new(h)));
            }
            None => {
-               let c = Rc::new(RefCell::new(Controller {id, ctrl, haptic: haptic.map(|h| Rc::new(RefCell::new(h)))}));
-               self.controllers.push(c);
+               let c = Rc::new(RefCell::new(Controller {ctrl, haptic: haptic.map(|h| Rc::new(RefCell::new(h)))}));
+               self.controllers.insert(id, c);
            }
        };
     }
diff --git a/src/core/game.rs b/src/core/game.rs
new file mode 100644 (file)
index 0000000..d9af79e
--- /dev/null
@@ -0,0 +1,164 @@
+use sdl2::controller::{Axis, Button};
+use core::controller::ControllerManager;
+use std::cell::RefCell;
+use std::rc::Rc;
+use core::controller::Controller;
+use common::Point2D;
+use sdl2::rect::Rect;
+use common::Nanoseconds;
+use sdl2::event::Event;
+use sprites::SpriteManager;
+use sdl2::render::Canvas;
+use sdl2::video::Window;
+use AppState;
+use point;
+
+////////// GAMESTATE ///////////////////////////////////////////////////////////
+
+#[derive(Default)]
+pub struct GameState {
+    world: World,
+}
+
+impl GameState {
+    pub fn new() -> Self {
+       GameState {
+           world: World::new(),
+       }
+    }
+}
+
+impl AppState for GameState {
+    fn enter(&mut self, ctrl_man: &mut ControllerManager) {
+       if let Some(ctrl) = ctrl_man.controllers.get(&0) {
+           self.world.add(Box::new(Character::new(ctrl.clone())));
+       }
+    }
+
+    fn leave(&mut self) {}
+
+    fn update(&mut self, dt: Nanoseconds) {
+       self.world.update(dt);
+    }
+
+    fn render(&mut self, canvas: &mut Canvas<Window>, sprites: &mut SpriteManager) {
+       self.world.render(canvas, sprites);
+    }
+
+    fn handle_event(&mut self, _event: Event) {}
+}
+
+////////// WORLD ///////////////////////////////////////////////////////////////
+
+#[derive(Default)]
+pub struct World {
+    level: Level,
+    objects: Vec<Box<dyn Object>>,
+}
+
+impl World {
+    pub fn new() -> Self {
+       World {
+           level: Level {
+               gravity: point!(0.0, 0.1),
+               ground: 600.0,
+           },
+           ..Default::default()
+       }
+    }
+
+    pub fn update(&mut self, dt: Nanoseconds) {
+       for o in &mut self.objects {
+           o.update(&self.level, dt);
+       }
+    }
+
+    pub fn render(&mut self, canvas: &mut Canvas<Window>, sprites: &mut SpriteManager) {
+       self.level.render(canvas, sprites);
+       for o in &mut self.objects {
+           o.render(canvas, sprites);
+       }
+    }
+
+    pub fn add(&mut self, object: Box<dyn Object>) {
+       self.objects.push(object);
+    }
+}
+
+////////// LEVEL ///////////////////////////////////////////////////////////////
+
+#[derive(Default)]
+pub struct Level {
+    gravity: Point2D<f64>,
+    ground: f64,               // just to have something
+}
+
+impl Level {
+    pub fn render(&mut self, canvas: &mut Canvas<Window>, _sprites: &mut SpriteManager) {
+       let w = canvas.viewport().width() as i32;
+       for i in 1..11 {
+           let y = (i * i - 1) as i32 + self.ground as i32;
+           canvas.set_draw_color((255 - i * 20, 255 - i * 20, 0));
+           canvas.draw_line((0, y), (w, y)).unwrap();
+       }
+    }
+}
+
+////////// OBJECT //////////////////////////////////////////////////////////////
+
+pub trait Object {
+    fn update(&mut self, lvl: &Level, dt: Nanoseconds);
+    fn render(&mut self, canvas: &mut Canvas<Window>, _sprites: &mut SpriteManager);
+}
+
+pub trait Physical {}
+pub trait Drawable {}
+
+////////// CHARACTER ///////////////////////////////////////////////////////////
+
+pub struct Character {
+    ctrl: Rc<RefCell<Controller>>,
+    pos: Point2D<f64>,
+    vel: Point2D<f64>,
+}
+
+impl Character {
+    pub fn new(ctrl: Rc<RefCell<Controller>>) -> Self {
+       Character {
+           ctrl,
+           pos: point!(100.0, 100.0),
+           vel: point!(0.0, 0.0),
+       }
+    }
+}
+
+impl Object for Character {
+    fn update(&mut self, lvl: &Level, _dt: Nanoseconds) {
+       self.vel += lvl.gravity;
+       self.pos = self.pos + self.vel;
+
+       let ctrl = &self.ctrl.borrow().ctrl;
+
+       if self.pos.y >= lvl.ground {
+           self.pos.y = lvl.ground;
+           self.vel.y = 0.0;
+           self.vel.x *= 0.9;
+
+           if ctrl.button(Button::A) {
+               self.vel.y = -5.0;
+           }
+       }
+
+       match ctrl.axis(Axis::LeftX) as f64 / 32768.0 {
+           v if v < -0.9 => { self.vel.x -= 0.5 }
+           v if v > 0.9 => { self.vel.x += 0.5 }
+           _ => {}
+       }
+    }
+
+    fn render(&mut self, canvas: &mut Canvas<Window>, sprites: &mut SpriteManager) {
+        let block = sprites.get("mario");
+       let size = 32;
+        canvas.copy(block, None, Rect::new(self.pos.x as i32, self.pos.y as i32 - size as i32, size, size)).unwrap();
+    }
+}
index 140bac4..9bcbe0c 100644 (file)
@@ -1,2 +1,3 @@
 pub mod app;
 pub mod controller;
+pub mod game;
index 26ceadb..9195583 100644 (file)
@@ -2,6 +2,7 @@ extern crate rand;
 extern crate sdl2;
 extern crate time;
 
+use core::game::GameState;
 use core::app::*;
 
 mod core;
@@ -17,7 +18,8 @@ fn main() {
     println!("starting...");
     let mut app = App::new()
         .with_resolution(SCREEN_WIDTH, SCREEN_HEIGHT)
-        .with_state(Box::new(ActiveState::new((SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32))))
+//        .with_state(Box::new(ActiveState::new((SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32))))
+        .with_state(Box::new(GameState::new()))
         .with_title("SDL test")
         .build()
         .unwrap();