From 45a79f0e28e1a60a97353cbb1e60a4504b375bf4 Mon Sep 17 00:00:00 2001 From: Thomas Versteeg Date: Sun, 24 Nov 2024 14:48:33 +0700 Subject: [PATCH] refactor(deps): update chuot to 0.3 --- Cargo.toml | 4 +-- assets/projectile/spear-1.ron | 2 +- src/camera.rs | 26 -------------- src/color.rs | 20 +++++++++++ src/main.rs | 66 +++++++++++++++++------------------ src/projectile.rs | 6 ++-- src/settings.rs | 2 +- src/terrain.rs | 29 ++++----------- src/unit.rs | 9 ++--- 9 files changed, 71 insertions(+), 93 deletions(-) delete mode 100644 src/camera.rs diff --git a/Cargo.toml b/Cargo.toml index ceabddb..3422414 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,9 @@ categories = ["games"] [dependencies] log = "0.4" -chuot = "0.2" +chuot = "0.3" +glamour = "0.15" puffin = "0.19" -glamour = "0.11" nanoserde = "0.1" [workspace] diff --git a/assets/projectile/spear-1.ron b/assets/projectile/spear-1.ron index ef5c597..88f7f0b 100644 --- a/assets/projectile/spear-1.ron +++ b/assets/projectile/spear-1.ron @@ -1 +1 @@ -(offset: Middle) +(pivot_x: Center, pivot_y: Center) diff --git a/src/camera.rs b/src/camera.rs deleted file mode 100644 index 3e4fbe8..0000000 --- a/src/camera.rs +++ /dev/null @@ -1,26 +0,0 @@ -use glamour::Vector2; - -/// Camera view. -/// -/// Offsets rendering. -#[derive(Default, Clone, Copy, PartialEq)] -pub struct Camera(Vector2); - -impl Camera { - /// Pan the camera. - pub fn pan(&mut self, x: f32, y: f32, min_x: f32, max_x: f32) { - self.0.x = (self.0.x + x).clamp(min_x, max_x); - self.0.y += y; - } - - /// Transform a world space vec2 into camera space. - pub fn translate(&self, point: Vector2) -> Vector2 { - point - self.0 - } -} - -impl From for Vector2 { - fn from(val: Camera) -> Self { - Vector2::new(-val.0.x, val.0.y) - } -} diff --git a/src/color.rs b/src/color.rs index f9fcbc4..69b04cb 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,3 +1,5 @@ +use chuot::RGBA8; + /// Different colors. /// /// Based on DB32 scale. @@ -77,6 +79,18 @@ impl Color { Self::DarkForestGreen => 0xff_8a_6f_30, } } + + /// Convert to RGBA8. + pub const fn as_rgba8(self) -> RGBA8 { + let rgba = self.as_u32(); + + RGBA8::new( + ((rgba >> 16) & 0xFF) as u8, + ((rgba >> 8) & 0xFF) as u8, + (rgba & 0xFF) as u8, + ((rgba >> 24) & 0xFF) as u8, + ) + } } impl From for u32 { @@ -84,3 +98,9 @@ impl From for u32 { value.as_u32() } } + +impl From for RGBA8 { + fn from(value: Color) -> Self { + value.as_rgba8() + } +} diff --git a/src/main.rs b/src/main.rs index f6ac040..d472f52 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -mod camera; mod color; mod projectile; mod settings; @@ -6,8 +5,7 @@ mod terrain; mod unit; mod utils; -use camera::Camera; -use chuot::{context::KeyCode, Config, Context, Game}; +use chuot::{Config, Context, Game, KeyCode}; use color::Color; use glamour::Vector2; use projectile::Projectile; @@ -27,8 +25,6 @@ pub enum GameState { Play { /// First level ground. terrain: Terrain, - /// Camera position. - camera: Camera, /// Timer for when a unit should spawn. unit_spawner: Timer, /// Timer for when an enemy unit should spawn. @@ -37,6 +33,8 @@ pub enum GameState { units: Vec, /// Projectiles flying around. projectiles: Vec, + /// Camera position. + camera_x: f32, }, } @@ -53,47 +51,50 @@ impl Game for GameState { match self { GameState::Load {} => { + let camera_x = WIDTH / 2.0; + let terrain = Terrain::new("level.grass-1", ctx.clone()); + + // Initialize camera + ctx.main_camera() + .follow((camera_x, -HEIGHT / 2.0 + terrain.size.height)); + // Load the initial state the first frame *self = GameState::Play { - terrain: Terrain::new("level.grass-1", ctx.clone()), - camera: Camera::default(), unit_spawner: Timer::new(settings.unit_spawn_interval), enemy_unit_spawner: Timer::new(settings.enemy_unit_spawn_interval), units: Vec::new(), projectiles: Vec::new(), + terrain, + camera_x, }; } GameState::Play { - camera, terrain, unit_spawner, enemy_unit_spawner, units, projectiles, + camera_x, } => { // Handle mouse events - if let Some((mouse_x, _mouse_y)) = ctx.mouse() { + if let Some(mouse_x) = ctx.mouse_x() { if mouse_x <= settings.pan_edge_offset { // Pan to the left - camera.pan( - -settings.pan_speed * dt, - 0.0, - 0.0, - terrain.size.width - WIDTH, - ); - } else if mouse_x >= 320.0 - settings.pan_edge_offset { + *camera_x -= settings.pan_speed * dt; + *camera_x = camera_x.max(WIDTH / 2.0); + + ctx.main_camera().follow_x(*camera_x); + } else if mouse_x >= ctx.width() - settings.pan_edge_offset { // Pan to the right - camera.pan( - settings.pan_speed * dt, - 0.0, - 0.0, - terrain.size.width - WIDTH, - ); + *camera_x += settings.pan_speed * dt; + *camera_x = camera_x.min(terrain.size.width - WIDTH / 2.0); + + ctx.main_camera().follow_x(*camera_x); } } // Draw the terrain - terrain.draw(ctx.clone(), *camera); + terrain.draw(ctx.clone()); // Update all projectiles projectiles.retain_mut(|projectile| !projectile.update(terrain, dt, &settings)); @@ -109,7 +110,7 @@ impl Game for GameState { if unit_spawner.update(dt) { // Spawn a unit at the upper edge of the terrain image units.push(Unit::new( - Vector2::new(10.0, terrain.y), + Vector2::new(10.0, 0.0), UnitType::PlayerSpear, ctx.clone(), )); @@ -117,7 +118,7 @@ impl Game for GameState { if enemy_unit_spawner.update(dt) { // Spawn a unit at the upper edge of the terrain image units.push(Unit::new( - (terrain.size.width - 10.0, terrain.y).into(), + (terrain.size.width - 10.0, 0.0).into(), UnitType::EnemySpear, ctx.clone(), )); @@ -129,32 +130,31 @@ impl Game for GameState { fn render(&mut self, ctx: Context) { // Draw a basic FPS counter let fps = ctx.frames_per_second(); - ctx.text("font.torus-sans", &format!("{fps:.1}")).draw(); + ctx.text("font.torus-sans", &format!("{fps:.1}")) + .use_ui_camera() + .draw(); match self { GameState::Load {} => { - ctx.text("font.torus-sans", "Loading game") - .translate((20.0, 20.0)) - .draw(); + ctx.text("font.torus-sans", "Loading game").draw(); } GameState::Play { - camera, terrain, units, projectiles, .. } => { // Draw the terrain - terrain.draw(ctx.clone(), *camera); + terrain.draw(ctx.clone()); // Draw all units units.iter().for_each(|unit| { - unit.render(*camera, ctx.clone()); + unit.render(ctx.clone()); }); // Draw all projectiles projectiles.iter().for_each(|projectile| { - projectile.render(*camera, ctx.clone()); + projectile.render(ctx.clone()); }); } } diff --git a/src/projectile.rs b/src/projectile.rs index 188ec2e..7806684 100644 --- a/src/projectile.rs +++ b/src/projectile.rs @@ -1,7 +1,7 @@ use chuot::Context; use glamour::Vector2; -use crate::{camera::Camera, settings::Settings, terrain::Terrain}; +use crate::{settings::Settings, terrain::Terrain}; /// Spear asset path. const ASSET_PATH: &str = "projectile.spear-1"; @@ -33,12 +33,12 @@ impl Projectile { } /// Draw the projectile. - pub fn render(&self, camera: Camera, ctx: Context) { + pub fn render(&self, ctx: Context) { puffin::profile_function!(); let rotation = self.vel.y.atan2(self.vel.x); ctx.sprite(ASSET_PATH) - .translate(self.pos + camera.into()) + .translate(self.pos) .rotate(rotation) .draw(); } diff --git a/src/settings.rs b/src/settings.rs index 2620e15..ad73dc9 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -5,7 +5,7 @@ use chuot::{ use nanoserde::DeRon; /// Game settings loaded from a file so it's easier to change them with hot-reloading. -#[derive(DeRon)] +#[derive(Debug, DeRon)] pub struct Settings { /// Distance from the edge at which the camera will pan. pub pan_edge_offset: f32, diff --git a/src/terrain.rs b/src/terrain.rs index 3e57f3a..f25f732 100644 --- a/src/terrain.rs +++ b/src/terrain.rs @@ -1,8 +1,6 @@ use chuot::Context; use glamour::{Size2, Vector2}; -use crate::{camera::Camera, HEIGHT}; - /// How many pixels to skip before taking a height sample. const HEIGHT_VALUE_INTERVAL: usize = 3; @@ -14,8 +12,6 @@ pub struct Terrain { pub size: Size2, /// Height values for each couple of pixels. heights: Vec, - /// Where to draw the terrain. - pub y: f32, } impl Terrain { @@ -33,13 +29,7 @@ impl Terrain { let first_height = pixels .iter() .enumerate() - .find_map(|(idx, pixel)| { - if pixel & 0xFF000000 != 0 { - Some(idx) - } else { - None - } - }) + .find_map(|(idx, pixel)| if pixel.a != 0 { Some(idx) } else { None }) .expect("Image is fully transparent") / width; @@ -52,7 +42,7 @@ impl Terrain { for y in first_height..height { let idx = y * width + x; // We only care about transparency - if pixels[idx] & 0xFF000000 != 0 { + if pixels[idx].a != 0 { return y as f32; } } @@ -63,28 +53,21 @@ impl Terrain { let level = level.to_owned(); - let y = HEIGHT - size.height; - Self { heights, level, size, - y, } } - pub fn draw(&mut self, ctx: Context, camera: Camera) { - let camera_offset: Vector2 = camera.into(); - + pub fn draw(&mut self, ctx: Context) { // Draw the level at the bottom of the screen - ctx.sprite(&self.level) - .translate(camera_offset + Vector2::new(0.0, self.y)) - .draw(); + ctx.sprite(&self.level).draw(); } /// Whether a point collides with the terrain pub fn point_collides(&self, point: Vector2) -> bool { - if point.x < 0.0 || point.y < self.y { + if point.x < 0.0 || point.y < 0.0 { return false; } @@ -94,6 +77,6 @@ impl Terrain { // If it's out of bounds there's no collision, otherwise check if the point is inside the terrain self.heights .get(index) - .is_some_and(|height| point.y >= height + self.y) + .is_some_and(|height| point.y >= *height) } } diff --git a/src/unit.rs b/src/unit.rs index f87b811..818bbb4 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -9,7 +9,6 @@ use glamour::{Size2, Vector2}; use nanoserde::DeRon; use crate::{ - camera::Camera, projectile::Projectile, terrain::Terrain, utils::{random::RandomRange, timer::Timer}, @@ -116,7 +115,7 @@ impl Unit { self.hide_hands_delay = hide_hands_delay; Some(Projectile::new( - self.pos + projectile_spawn_offset.into(), + self.pos + Vector2::from_tuple(projectile_spawn_offset), Vector2::new(velocity, -velocity), )) } else { @@ -137,20 +136,22 @@ impl Unit { } /// Draw the unit. - pub fn render(&self, camera: Camera, ctx: Context) { + pub fn render(&self, ctx: Context) { puffin::profile_scope!("Unit render"); let settings = self.settings(ctx.clone()); - let draw_pos = self.pos - self.ground_collision_point() + camera.into(); + let draw_pos = self.pos - self.ground_collision_point(); ctx.sprite(self.r#type.asset_path()) .translate(draw_pos) + .use_main_camera() .draw(); ctx.text("font.debug", &format!("{}", self.health)) .translate(draw_pos) .translate(settings.healthbar_offset) + .use_main_camera() .draw(); if let Some(hands_asset_path) = &settings.hands_asset_path {