diff --git a/src/engine/character.rs b/src/engine/character.rs index d7f58f3..798f01f 100644 --- a/src/engine/character.rs +++ b/src/engine/character.rs @@ -8,7 +8,10 @@ use crate::engine::event::{Event, Keyboard}; use crate::engine::math::{clamp, duration_as_f64}; use crate::engine::physics; use crate::engine::scene::Updatable; +use crate::engine::texture::Texture; use crate::engine::vector::Vector; +use crate::engine::Drawable; +use crate::log; /// The different sides a character can face. pub enum Side { @@ -67,7 +70,7 @@ pub struct Character { max_jump: usize, /// The timer of the character's animation. - animation_timer: Instant, + animation_timer: Duration, /// Whether the character is walking or not. walking: bool, @@ -86,7 +89,7 @@ impl Character { side: Side::Right, jump_counter: 1, max_jump: 1, - animation_timer: Instant::now(), + animation_timer: Duration::from_millis(0), can_jump: true, walking: false, } @@ -124,7 +127,7 @@ impl Character { /// Makes the player die. pub fn die(&self) { - panic!(); + log!("dead"); } /// Returns a reference to the controls. @@ -153,13 +156,13 @@ impl Updatable for Character { if let Some(side) = Side::from_force(force) { if !self.walking { - self.animation_timer = Instant::now(); + self.animation_timer = Duration::from_millis(0); } self.walking = true; self.side = side; } else { if self.walking { - self.animation_timer = Instant::now(); + self.animation_timer = Duration::from_millis(0); } self.walking = false; } @@ -210,18 +213,18 @@ impl Updatable for Character { } } -// impl Drawable for Character { -// fn texture(&self) -> Texture { -// Texture::Rusty -// } -// -// fn texture_rect(&self) -> IntRect { -// let frame = duration_as_frame(&Instant::now().duration_since(self.animation_timer), 4); -// let offset = if self.walking { 64 } else { 0 }; -// IntRect::new(self.side.offset() + offset, frame * 32, 32, 32) -// } -// -// fn position(&self) -> Vector { -// self.position -// } -// } +impl Drawable for Character { + fn texture(&self) -> Texture { + Texture::Rusty + } + + fn texture_rect(&self) -> Bbox { + let frame = 0.0; // duration_as_frame(&Instant::now().duration_since(self.animation_timer), 4); + let offset = if self.walking { 64.0 } else { 0.0 }; + Bbox::new(self.side.offset() as f64 + offset, frame * 32.0, 32.0, 32.0) + } + + fn position(&self) -> Vector { + self.position + } +} diff --git a/src/engine/controls.rs b/src/engine/controls.rs index bf8a36f..3770878 100644 --- a/src/engine/controls.rs +++ b/src/engine/controls.rs @@ -2,6 +2,7 @@ use crate::engine::event::{Event, Key, Keyboard}; use crate::engine::vector::Vector; +use crate::log; /// The different actions that a user can do. pub enum Action { diff --git a/src/engine/map.rs b/src/engine/map.rs index 2aee03c..8457366 100644 --- a/src/engine/map.rs +++ b/src/engine/map.rs @@ -2,8 +2,10 @@ use crate::engine::bbox::Bbox; use crate::engine::math::{clamp, Matrix}; +use crate::engine::texture::Texture; use crate::engine::texture::SPRITE_SIZE; use crate::engine::vector::Vector; +use crate::engine::Drawable; use crate::Result; /// Converts the byte with bits corresponding to the neighbours to the offset on the generated @@ -62,7 +64,7 @@ pub fn byte_to_index(byte: u8) -> i32 { } /// This enum represents if the collision happens on the X axis or the Y axis. -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub enum CollisionAxis { /// The X axis. X, @@ -226,20 +228,20 @@ pub struct PositionedTile { pub position: Vector, } -// impl Drawable for PositionedTile { -// fn texture(&self) -> Texture { -// Texture::Overworld -// } -// -// fn texture_rect(&self) -> Bbox { -// let offset = self.graphic.offset(); -// Bbox::new(offset.x, offset.x, SPRITE_SIZE, SPRITE_SIZE) -// } -// -// fn position(&self) -> Vector { -// self.position -// } -// } +impl Drawable for PositionedTile { + fn texture(&self) -> Texture { + Texture::Overworld + } + + fn texture_rect(&self) -> Bbox { + let offset = self.graphic.offset(); + Bbox::new(offset.0 as f64, offset.0 as f64, SPRITE_SIZE, SPRITE_SIZE) + } + + fn position(&self) -> Vector { + self.position + } +} /// The map represents the tiles contained in a level. #[derive(Clone)] diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 761b5d9..a9686ac 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -18,10 +18,14 @@ use std::time::Duration; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; +use crate::engine::bbox::Bbox; +use crate::engine::character::Character; +use crate::engine::controls::Controls; use crate::engine::event::{Key, Keyboard}; use crate::engine::map::Map; use crate::engine::scene::{Scene, State}; -use crate::engine::texture::TextureManager; +use crate::engine::texture::{Texture, TextureManager}; +use crate::engine::vector::Vector; use crate::{error_js, log, Result}; macro_rules! unwrap { @@ -41,6 +45,9 @@ pub struct Engine { /// We need Rc in order to deal with events. pub inner: Rc>, + /// The web page window. + pub window: Rc, + /// The web page document. pub document: Rc, @@ -61,20 +68,24 @@ impl Engine { canvas.set_width(1920); canvas.set_height(1080); - let context = canvas - .get_context("2d")? - .unwrap() - .dyn_into::()?; + let context = + unwrap!(canvas.get_context("2d")?).dyn_into::()?; let map = Map::from_str(include_str!("../../static/levels/level1.lvl")).unwrap(); - let scene = Scene::from_map(map); + let mut scene = Scene::from_map(map); + + let character = Character::with_controls(Controls::default_keyboard()); + scene.add(character); + let inner = InnerEngine::new(&document, scene)?; + let window = Rc::new(window); let document = Rc::new(document); let context = Rc::new(context); Ok(Engine { inner: Rc::new(RefCell::new(inner)), + window, document, context, }) @@ -89,10 +100,7 @@ impl Engine { } }); - web_sys::window() - .unwrap() - .request_animation_frame(cb.as_ref().unchecked_ref()) - .unwrap(); + (*self.window).request_animation_frame(cb.as_ref().unchecked_ref())?; cb.forget(); @@ -115,10 +123,7 @@ impl Engine { } }); - web_sys::window() - .unwrap() - .request_animation_frame(cb.as_ref().unchecked_ref()) - .unwrap(); + (*self.window).request_animation_frame(cb.as_ref().unchecked_ref())?; cb.forget(); @@ -142,29 +147,19 @@ impl Engine { // running = false; } - // Perform update - let mut dx = 0.0; - let mut dy = 0.0; + Ok(()) + } - if inner.keyboard.is_key_pressed(Key::ArrowDown) { - dy += 5.0; - } + /// Draw a drawable. + pub fn draw(&self, drawable: &D) -> Result<()> { + let inner = self.inner.borrow(); + let image = inner.textures.get(drawable.texture()); - if inner.keyboard.is_key_pressed(Key::ArrowUp) { - dy -= 5.0; - } - - if inner.keyboard.is_key_pressed(Key::ArrowLeft) { - dx -= 5.0; - } - - if inner.keyboard.is_key_pressed(Key::ArrowRight) { - dx += 5.0; - } - - inner.x += dx; - inner.y += dy; + let source = drawable.texture_rect(); + let mut dest = source.clone(); + dest.position = drawable.position(); + image.render(source, dest, &self.context)?; Ok(()) } @@ -172,12 +167,32 @@ impl Engine { pub fn render(&self) -> Result<()> { let inner = self.inner.borrow(); - // Perform render + // Clear render self.context.clear_rect(0.0, 0.0, 1920.0, 1080.0); - inner - .textures - .rusty - .render(inner.x, inner.y, &self.context)?; + self.context + .set_fill_style(&JsValue::from_str("rgb(135, 206, 235)")); + self.context.fill_rect(0.0, 0.0, 1920.0, 1080.0); + + // Draw the scene + let map = inner.scene.map(); + let rows = map.rows(); + let cols = map.cols(); + + for i in 0..rows { + for j in 0..cols { + let tile = map.at(i, j); + if tile.graphic.is_visible() { + self.draw(&tile)?; + } + } + } + + // Draw characters + for c in inner.scene.characters() { + if true { + self.draw(c)?; + } + } Ok(()) } @@ -185,12 +200,6 @@ impl Engine { /// The data contained in our engine. pub struct InnerEngine { - /// The x position of the drawing. - pub x: f64, - - /// The y position of the drawing. - pub y: f64, - /// The scene of the engine. pub scene: Scene, @@ -204,13 +213,22 @@ pub struct InnerEngine { impl InnerEngine { /// Initializes the engine. pub fn new(document: &web_sys::Document, scene: Scene) -> Result { - log!("sup"); Ok(InnerEngine { - x: 0.0, - y: 0.0, scene, keyboard: Keyboard::new(document)?, textures: TextureManager::new()?, }) } } + +/// Our custom drawable trait. +pub trait Drawable { + /// Returns the texture of the drawable. + fn texture(&self) -> Texture; + + /// Returns the coordinates to use on the texture. + fn texture_rect(&self) -> Bbox; + + /// Returns the position on which the drawable should be drawn. + fn position(&self) -> Vector; +} diff --git a/src/engine/scene.rs b/src/engine/scene.rs index 1ee32eb..0d567ec 100644 --- a/src/engine/scene.rs +++ b/src/engine/scene.rs @@ -108,7 +108,7 @@ impl Scene { c.ground_collision(); } - c.die(); + // c.die(); } else { c.fall_off(); } diff --git a/src/engine/texture.rs b/src/engine/texture.rs index 691aaa4..3c6c6dd 100644 --- a/src/engine/texture.rs +++ b/src/engine/texture.rs @@ -8,6 +8,7 @@ use wasm_bindgen::JsCast; use web_sys::HtmlImageElement; +use crate::engine::bbox::Bbox; use crate::{log, Result}; /// The size of sprites. @@ -68,28 +69,61 @@ impl Image { /// Renders the image on a context. pub fn render( &self, - x: f64, - y: f64, + source: Bbox, + dest: Bbox, context: &web_sys::CanvasRenderingContext2d, ) -> Result<()> { - context.draw_image_with_html_image_element(&self.inner.borrow().inner, x, y)?; + context.draw_image_with_html_image_element_and_sw_and_sh_and_dx_and_dy_and_dw_and_dh( + &self.inner.borrow().inner, + source.position.x, + source.position.y, + source.size.x, + source.size.y, + dest.position.x, + dest.position.y, + dest.size.x, + dest.size.y, + )?; Ok(()) } } +/// The list of all our textures. +#[derive(Copy, Clone)] +pub enum Texture { + /// The main character sprite. + Rusty, + + /// The sprites from the overworld. + Overworld, +} + /// Our texture manager. /// /// It holds all our resources. pub struct TextureManager { /// The main character sprite. pub rusty: Image, + + /// The sprites from the overworld. + pub overworld: Image, } impl TextureManager { /// Creates and start the loading of all our textures. pub fn new() -> Result { - let rusty = Image::new("static/textures/rusty.png")?; - Ok(TextureManager { rusty }) + Ok(TextureManager { + rusty: Image::new("static/textures/rusty.png")?, + overworld: Image::new("static/textures/overworld.png")?, + }) + } + + /// Returns the image corresponding to a certain texture. + pub fn get(&self, texture: Texture) -> &Image { + match texture { + Texture::Rusty => &self.rusty, + Texture::Overworld => &self.overworld, + } } } diff --git a/src/engine/vector.rs b/src/engine/vector.rs index e4f2375..dba62eb 100644 --- a/src/engine/vector.rs +++ b/src/engine/vector.rs @@ -1,5 +1,6 @@ //! A module for basic maths. +use std::fmt; use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; /// A 2 dimensional vector. @@ -79,3 +80,9 @@ impl Mul for f64 { } } } + +impl fmt::Display for Vector { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "({}, {})", self.x, self.y) + } +} diff --git a/static/textures/overworld.png b/static/textures/overworld.png new file mode 100644 index 0000000..c5e0563 Binary files /dev/null and b/static/textures/overworld.png differ