diff --git a/game/.gitignore b/game/.gitignore index ea8c4bf..ccb5166 100644 --- a/game/.gitignore +++ b/game/.gitignore @@ -1 +1,2 @@ /target +.vscode \ No newline at end of file diff --git a/game/src/engine/game.rs b/game/src/engine/game.rs index d262996..01fcf6c 100644 --- a/game/src/engine/game.rs +++ b/game/src/engine/game.rs @@ -1,69 +1,58 @@ - - -use std::time::Instant; - -use sdl3::render::Canvas; -use sdl3::{EventPump, Sdl, VideoSubsystem}; -use sdl3::video::Window; use sdl3::pixels::Color; -use sdl3::event::Event; -use sdl3::keyboard::Keycode; + +use crate::engine::math::{V2, V3}; +use std::{marker::PhantomData, time::Duration}; use super::error::Error; -use super::object::Object; -pub struct Game { - sdl_context: Sdl, - video_subsystem: VideoSubsystem, - canvas: Canvas, - event_pump: EventPump, - io: Io, +pub trait Object { + fn update(&mut self, delta: Duration); + fn render(&mut self, r: &mut R); +} + +pub trait Io> { + fn run(&mut self, game: &mut Game); +} + +pub trait Renderer { + fn draw_rect(&mut self, pos: V2, size: V2, color: Color); + fn point(&mut self, pos: V2, color: Color); + fn draw_line(&mut self, from: V2, to: V2, color: Color); + fn draw_cube(&mut self, pos: V3, size: V3, outline_colors: Color, fill_color: Color); +} + +pub enum Event { + Exit, +} + +pub struct Game> { + _phantom_data: PhantomData, objects: Vec, } -impl Game { - pub fn new(io: Io) -> Result { - let sdl_context = sdl3::init().unwrap(); - let video_subsystem = sdl_context.video().unwrap(); - let window = video_subsystem.window("Game", 1280, 720).position_centered().build().unwrap(); - let mut canvas = window.into_canvas(); - - canvas.set_draw_color(Color::BLACK); - canvas.clear(); - canvas.present(); - - let event_pump = sdl_context.event_pump().unwrap(); - +impl> Game { + pub fn new() -> Result { Ok(Self { - sdl_context, - video_subsystem, - canvas, - event_pump, - io, - objects: Vec::new() + _phantom_data: PhantomData, + objects: Vec::new(), }) } - pub fn run(&mut self) { - let mut time_before = Instant::now(); - let time_per_frame = 1_000_000_000 / 60; - 'running: loop { - for event in self.event_pump.poll_iter() { - match event { - Event::Quit {..} | - Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { - break 'running - }, - _ => {} - } - } - let now = Instant::now(); - let delta = now - time_before; - time_before = now; - for object in self.objects.iter_mut() { - object.update(delta); - } + pub fn spawn(&mut self, object: O) { + self.objects.push(object); + } + pub fn update(&mut self, delta_time: Duration) { + for object in self.objects.iter_mut() { + object.update(delta_time); } } -} \ No newline at end of file + + pub fn render(&mut self, r: &mut R) { + for object in self.objects.iter_mut() { + object.render(r); + } + } + + pub fn event(&mut self, event: Event) {} +} diff --git a/game/src/engine/game_example.rs b/game/src/engine/game_example.rs deleted file mode 100644 index 3f2a774..0000000 --- a/game/src/engine/game_example.rs +++ /dev/null @@ -1,10 +0,0 @@ - -pub trait GameObject { - fn update(deltaTime: Duration); - fn render(); -} - -pub struct Game { - -} - diff --git a/game/src/engine/io.rs b/game/src/engine/io.rs deleted file mode 100644 index e69de29..0000000 diff --git a/game/src/engine/math.rs b/game/src/engine/math.rs new file mode 100644 index 0000000..ad19d52 --- /dev/null +++ b/game/src/engine/math.rs @@ -0,0 +1,79 @@ +use std::ops::{Add, AddAssign, Mul, MulAssign}; + +pub struct V2(pub f64, pub f64); + +#[derive(Copy, Clone)] +pub struct V3(pub f64, pub f64, pub f64); + +impl Add for V3 { + type Output = V3; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0, self.1 + rhs.1, self.2 + rhs.2) + } +} + +impl AddAssign for V3 { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Mul for V3 { + type Output = V3; + + fn mul(self, rhs: f64) -> Self::Output { + Self(self.0 * rhs, self.1 * rhs, self.2 * rhs) + } +} + +impl MulAssign for V3 { + fn mul_assign(&mut self, rhs: f64) { + *self = *self * rhs; + } +} + +impl V3 { + pub fn as_2d(&self) -> V2 { + V2(self.0 / self.2, self.1 / self.2) + } +} + +pub struct Vertex(V3, V3, V3); + +pub struct Cube { + vertices: [Vertex; 12], +} + +impl Cube { + pub fn new(V3(x, y, z): V3, V3(w, h, d): V3) -> Self { + Self { + vertices: [ + Vertex(V3(x, y, z), V3(x, y + h, z), V3(x + w, y + h, z)), + Vertex(V3(x + w, y + h, z), V3(x + w, y, z), V3(x, y + h, z)), + Vertex(V3(x + w, y, z), V3(x + w, y + h, z), V3(x + w, y, z + d)), + Vertex( + V3(x + w, y + h, z + d), + V3(x + w, y, z + d), + V3(x, y + h, z + d), + ), + Vertex(V3(x, y + h, z), V3(x + w, y + h, z), V3(x, y + h, z + d)), + Vertex( + V3(x + w, y + h, z), + V3(x + w, y + h, z + d), + V3(x, y + h, z + d), + ), + Vertex(V3(x, y, z), V3(x, y + h, z), V3(x, y, z + d)), + Vertex(V3(x, y + h, z), V3(x, y + h, z + d), V3(x, y, z + d)), + Vertex(V3(x, y, z), V3(x + w, y, z), V3(x, y, z + d)), + Vertex(V3(x + w, y, z), V3(x + w, y, z + d), V3(x, y, z + d)), + Vertex(V3(x, y, z + d), V3(x + w, y, z + d), V3(x, y + h, z + d)), + Vertex( + V3(x + w, y, z + d), + V3(x + w, y + h, z + d), + V3(x, y + h, z + d), + ), + ], + } + } +} diff --git a/game/src/engine/mod.rs b/game/src/engine/mod.rs index 67c0cc2..d48e81b 100644 --- a/game/src/engine/mod.rs +++ b/game/src/engine/mod.rs @@ -1,4 +1,9 @@ -pub mod object; -pub mod game; +mod error; +mod game; +pub mod math; +mod sdl_io; mod system; -mod error; \ No newline at end of file + +pub use game::{Event, Game, Io, Object, Renderer}; +pub use math::V3; +pub use sdl_io::SdlIo; diff --git a/game/src/engine/object.rs b/game/src/engine/object.rs deleted file mode 100644 index abcacb9..0000000 --- a/game/src/engine/object.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::time::Duration; - -pub trait Object { - fn update(&mut self, delta: Duration); - fn render(&mut self); -} - - diff --git a/game/src/engine/sdl_io.rs b/game/src/engine/sdl_io.rs new file mode 100644 index 0000000..9379d9c --- /dev/null +++ b/game/src/engine/sdl_io.rs @@ -0,0 +1,134 @@ +use std::time::Instant; + +use sdl3::{ + event::Event, + keyboard::Keycode, + pixels::{Color, PixelFormat}, + render::{Canvas, FRect}, + video::Window, + Sdl, VideoSubsystem, +}; + +use crate::engine::{ + error::Error, + game, + math::{V2, V3}, + Object, +}; + +static WIDTH: f64 = 1280.0; +static HEIGHT: f64 = 720.0; + +pub struct SdlIo { + sdl_context: Sdl, + video_subsystem: VideoSubsystem, + canvas: Canvas, +} + +impl SdlIo { + pub fn new() -> Result { + let sdl_context = sdl3::init().unwrap(); + let video_subsystem = sdl_context.video().unwrap(); + let window = video_subsystem + .window("Game", WIDTH as u32, HEIGHT as u32) + .position_centered() + .build() + .unwrap(); + let mut canvas = window.into_canvas(); + + canvas.set_draw_color(Color::BLACK); + canvas.clear(); + canvas.present(); + + Ok(Self { + sdl_context, + video_subsystem, + canvas, + }) + } + + pub fn run>(&mut self, game: &mut game::Game) { + let mut event_pump = self.sdl_context.event_pump().unwrap(); + + let mut time_before = Instant::now(); + let time_per_frame = 1_000_000_000 / 60; + + 'running: loop { + for event in event_pump.poll_iter() { + match event { + Event::Quit { .. } + | Event::KeyDown { + keycode: Some(Keycode::Escape), + .. + } => break 'running, + _ => {} + } + } + + let time_now = Instant::now(); + let delta_time = time_now - time_before; + time_before = time_now; + game.update(delta_time); + + self.canvas.set_draw_color(Color::BLACK); + self.canvas.clear(); + game.render(self); + self.canvas.present(); + } + } + + fn world_to_screen(&self, v: V2) -> V2 { + V2(v.0 + WIDTH / 2.0, HEIGHT / 2.0 - v.1) + } +} + +impl game::Renderer for SdlIo { + fn draw_rect(&mut self, pos: V2, size: V2, color: Color) { + let pos = self.world_to_screen(pos); + self.canvas.set_draw_color(color); + self.canvas + .fill_rect(FRect::new( + (pos.0 - size.0 / 2.0) as _, + (pos.1 - size.1 / 2.0) as _, + size.0 as _, + size.1 as _, + )) + .unwrap(); + } + + fn point(&mut self, pos: V2, color: Color) { + let pos = self.world_to_screen(pos); + let size = V2(10.0, 10.0); + self.canvas.set_draw_color(color); + self.canvas + .fill_rect(FRect::new( + (pos.0 - size.0 / 2.0) as _, + (pos.1 - size.1 / 2.0) as _, + 10.0, + 10.0, + )) + .unwrap(); + } + + fn draw_line(&mut self, from: V2, to: V2, color: Color) { + todo!() + } + + fn draw_cube(&mut self, pos: V3, size: V3, outline_color: Color, fill_color: Color) { + let V3(x, y, z) = pos; + let V3(w, h, d) = size; + let points = [ + pos, + V3(x + w, y, z), + V3(x, y + h, z), + V3(x + w, y + h, z), + V3(x, y, z + d / 100.0), + V3(x + w, y, z + d / 100.0), + V3(x, y + h, z + d / 100.0), + V3(x + w, y + h, z + d / 100.0), + ]; + for point in points { + self.point(point.as_2d(), outline_color); + } + } +} diff --git a/game/src/main.rs b/game/src/main.rs index 306473c..1fc203a 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -1,9 +1,46 @@ +#![allow(dead_code)] + +use std::time::Duration; + +use sdl3::pixels::Color; + +use crate::engine::math::{V2, V3}; + mod engine; -use crate::engine::game::Game; - -fn main() { - // let mut game: Game<(), _> = Game::new(todo!()).unwrap(); - // game.run(); - println!("Hello, world!"); +enum Object { + Player { pos: V3, vel: V3 }, +} + +impl engine::Object for Object { + fn update(&mut self, delta_time: Duration) { + match self { + Object::Player { pos, vel } => { + *pos += *vel * delta_time.as_secs_f64(); + } + } + } + + fn render(&mut self, r: &mut R) { + match self { + Object::Player { pos, .. } => { + // r.draw_rect(V2(pos.0, pos.1), V2(400.0, 400.0)); + r.draw_cube(*pos, V3(100.0, 100.0, 100.0), Color::GREEN, Color::WHITE); + } + } + } +} + +fn main() -> Result<(), Box> { + let mut sdl_io = engine::SdlIo::new()?; + let mut game = engine::Game::::new()?; + + let player = Object::Player { + pos: V3(-50.0, -50.0, 0.0), + vel: V3(0.0, 0.0, 1.0), + }; + game.spawn(player); + + sdl_io.run(&mut game); + Ok(()) }