From 6360cd3beeb9e3a9494cd45862f80647756569cb Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Sat, 30 Jul 2022 16:32:55 +0200 Subject: [PATCH] Starting to get something --- src/engine/character.rs | 43 +++++++------ src/engine/controls.rs | 1 + src/engine/map.rs | 32 +++++----- src/engine/mod.rs | 114 ++++++++++++++++++++-------------- src/engine/scene.rs | 2 +- src/engine/texture.rs | 44 +++++++++++-- src/engine/vector.rs | 7 +++ static/textures/overworld.png | Bin 0 -> 5828 bytes 8 files changed, 154 insertions(+), 89 deletions(-) create mode 100644 static/textures/overworld.png 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 0000000000000000000000000000000000000000..c5e056313c0f4debea4befc318592c669f8a2b4a GIT binary patch literal 5828 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 literal 0 HcmV?d00001