From bd0bba5191871686f6f01e41a1e7d354a8bbaf2d Mon Sep 17 00:00:00 2001 From: bob302 <54355416+bob302@users.noreply.github.com> Date: Tue, 13 Feb 2024 19:05:15 +0200 Subject: [PATCH 1/2] resolution Added support for scaling objects for different resolutions --- src/game.rs | 101 ++++++++++++++++++++++++++++++++++++++------------ src/main.rs | 8 +++- src/object.rs | 8 ++-- 3 files changed, 88 insertions(+), 29 deletions(-) diff --git a/src/game.rs b/src/game.rs index a8f2f24..b4e6221 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,5 +1,8 @@ +use std::fmt::Alignment; + +use ggez::mint::Point2; use ggez::{graphics, Context, GameResult, event::EventHandler}; -use ggez::graphics::{Text, TextFragment, Color as GgezColor}; +use ggez::graphics::{Color as GgezColor, Drawable, Text, TextFragment}; use nalgebra::Vector2; use rand::Rng; @@ -7,23 +10,26 @@ use crate::utils::count_objects; use crate::object::{Object, Team, ObjectKind}; use crate::formatter::format_squares_numbers; -pub mod game_constants { +pub mod game_constants { pub const ROW_SIZE: i32 = 20; pub const COLUMN_SIZE: i32 = 10; pub const CIRCLE_SIZE: f32 = 25.0; pub const SQUARE_SIZE: f32 = 50.0; - pub const FIELD_START_Y: f32 = 100.0; - pub const FIELD_START_X: f32 = 128.0; - pub const FIELD_WIDTH: f32 = (SQUARE_SIZE * 2.0) * COLUMN_SIZE as f32; pub const FIELD_HEIGHT: f32 = (SQUARE_SIZE) * ROW_SIZE as f32; pub const MOVEMENT_SPEED: f32 = 15.0; } + pub struct Game { + window_width: f32, + window_height: f32, + scale_factor: f32, + bounds: (f32, f32), + squares: Vec, sun_circle: Object, moon_circle: Object, @@ -31,6 +37,9 @@ pub struct Game { impl Game { pub fn new(ctx: &mut Context) -> Self { + let (window_width, window_height) = ctx.gfx.drawable_size(); + let scale_factor = window_width / game_constants::FIELD_WIDTH; + let mut rng = rand::thread_rng(); let moon_x_sign = if rng.gen::() { 1.0 } else { -1.0 }; @@ -39,35 +48,50 @@ impl Game { let sun_x_sign = if rng.gen::() { 1.0 } else { -1.0 }; let sun_y_sign = if rng.gen::() { 1.0 } else { -1.0 }; + let moon_pos = (window_width * 0.7, window_height * 0.6); + let sun_pos = (window_width * 0.35, window_height * 0.6); + + let field_start_x: f32 = (window_width - game_constants::FIELD_WIDTH) / 2.0; + let field_start_y: f32 = (window_height - game_constants::FIELD_HEIGHT) / 2.0; + + let bounds = (field_start_x, field_start_y); + let moon_circle = Object { - position: (870.0, 650.0), + position: moon_pos, team: Team::MOON, kind: ObjectKind::Circle, direction: Vector2::new( moon_x_sign * 1.5 * game_constants::MOVEMENT_SPEED, moon_y_sign * 1.5 * game_constants::MOVEMENT_SPEED - ) + ), + }; let sun_circle = Object { - position: (385.5, 650.0), + position: sun_pos, team: Team::SUN, kind: ObjectKind::Circle, direction: Vector2::new( sun_x_sign * 1.5 * game_constants::MOVEMENT_SPEED, sun_y_sign * 1.5 * game_constants::MOVEMENT_SPEED - ) + ), + }; + let mut squares = Vec::new(); for row in 0..game_constants::ROW_SIZE { for column in 0..game_constants::COLUMN_SIZE { squares.push(Object { - position: (game_constants::FIELD_START_X + (column as f32) * game_constants::SQUARE_SIZE, game_constants::FIELD_START_Y + (row as f32) * game_constants::SQUARE_SIZE), + position: ( + (field_start_x + (column as f32) * game_constants::SQUARE_SIZE), + (field_start_y + (row as f32) * game_constants::SQUARE_SIZE), + ), team: Team::MOON, kind: ObjectKind::Square, direction: Vector2::new(0.0, 0.0), + }); } } @@ -75,10 +99,14 @@ impl Game { for row in 0..game_constants::ROW_SIZE { for column in 0..game_constants::COLUMN_SIZE { squares.push(Object { - position: (game_constants::FIELD_START_X + (column as f32) * game_constants::SQUARE_SIZE + (game_constants::COLUMN_SIZE as f32) * game_constants::SQUARE_SIZE, game_constants::FIELD_START_Y + (row as f32) * game_constants::SQUARE_SIZE), + position: ( + (field_start_x + (column as f32) * game_constants::SQUARE_SIZE + (game_constants::COLUMN_SIZE as f32) * game_constants::SQUARE_SIZE), + (field_start_y + (row as f32) * game_constants::SQUARE_SIZE), + ), team: Team::SUN, kind: ObjectKind::Square, direction: Vector2::new(0.0, 0.0), + }); } } @@ -94,19 +122,36 @@ impl Game { } Game { + window_height, + window_width, + scale_factor, + bounds, + squares, sun_circle, moon_circle, } } + + fn scale_position(&self, position: (f32, f32)) -> (f32, f32) { + (position.0 * self.scale_factor, position.1 * self.scale_factor) + } + + fn scale_vector(&self, vector: Vector2) -> Vector2 { + Vector2::new(vector.x * self.scale_factor, vector.y * self.scale_factor) + } } impl EventHandler for Game { fn update(&mut self, _: &mut Context) -> GameResult { self.sun_circle.update_position(); self.moon_circle.update_position(); - self.sun_circle.handle_boundary_collision(); - self.moon_circle.handle_boundary_collision(); + + // println!("MOON POS: x: {} y: {}", self.moon_circle.position.0, self.moon_circle.position.1); + // println!("SUN POS: x: {} y: {}", self.sun_circle.position.0, self.sun_circle.position.1); + + self.sun_circle.handle_boundary_collision(self.bounds); + self.moon_circle.handle_boundary_collision(self.bounds); for square in &mut self.squares { self.sun_circle.handle_collision(square); @@ -115,28 +160,38 @@ impl EventHandler for Game { Ok(()) } - + // TODO fix this MF fn draw(&mut self, ctx: &mut Context) -> GameResult { let canvas_color = GgezColor::from_rgb_u32(0x767B91); let mut canvas = graphics::Canvas::from_frame(ctx, canvas_color); - let dest_point = ggez::glam::Vec2::new(428.0, 1180.0); + for i in 0..self.squares.len() { + self.squares[i].draw(ctx, &mut canvas)?; + } + + + self.sun_circle.draw(ctx, &mut canvas)?; + self.moon_circle.draw(ctx, &mut canvas)?; + let font_color = GgezColor::from_rgb_u32(0xE1E5EE); let text_fragment = TextFragment::new(format_squares_numbers(count_objects(&self.squares, Team::MOON), count_objects(&self.squares, Team::SUN))) .font("LiberationMono") - .scale(40.0) + .scale(40.0 * self.scale_factor) .color(font_color); + + let text = Text::new(text_fragment); + let text_dimensions = text.measure(ctx)?; + + let x = (self.window_width - text_dimensions.x) / 2.0; + let y = self.window_height - text_dimensions.y; + + let dest_point = ggez::glam::Vec2::new(x, y); + canvas.draw( - &Text::new(text_fragment), + &text, dest_point, ); - for square in &mut self.squares { - square.draw(ctx, &mut canvas)?; - } - - self.sun_circle.draw(ctx, &mut canvas)?; - self.moon_circle.draw(ctx, &mut canvas)?; canvas.finish(ctx)?; diff --git a/src/main.rs b/src/main.rs index d597ab9..c768cd7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,9 +18,13 @@ fn main() { }; let result = ContextBuilder::new("Moon & Sun", "wedyarit") - .window_mode(conf::WindowMode::default().dimensions(1280.0, 1280.0)) + .window_mode(conf::WindowMode::default() + .maximized(true) + .resizable(false)) .add_resource_path(resource_dir) - .window_setup(conf::WindowSetup::default().title("Moon & Sun").icon("/icon.png")) + .window_setup(conf::WindowSetup::default() + .title("Moon & Sun") + .icon("/icon.png")) .build(); let (mut ctx, event_loop) = match result { Ok((ctx, event_loop)) => (ctx, event_loop), diff --git a/src/object.rs b/src/object.rs index e9c367e..d9bb0ab 100644 --- a/src/object.rs +++ b/src/object.rs @@ -3,7 +3,7 @@ use ggez::{Context, GameResult}; use ggez::glam::*; use nalgebra::Vector2; -use crate::game::{game_constants}; +use crate::game::game_constants; pub enum ObjectKind { Circle, @@ -52,15 +52,15 @@ impl Object { } } - pub fn handle_boundary_collision(&mut self) { + pub fn handle_boundary_collision(&mut self, bounds: (f32, f32)) { let (mut x, mut y) = self.position; - if x - game_constants::CIRCLE_SIZE <= game_constants::FIELD_START_X || x + game_constants::CIRCLE_SIZE >= game_constants::FIELD_START_X + game_constants::FIELD_WIDTH { + if x - game_constants::CIRCLE_SIZE <= bounds.0 || x + game_constants::CIRCLE_SIZE >= bounds.0 + game_constants::FIELD_WIDTH { self.direction.x *= -1.0; x += self.direction.x; } - if y - game_constants::CIRCLE_SIZE <= game_constants::FIELD_START_Y || y + game_constants::CIRCLE_SIZE >= game_constants::FIELD_START_Y + game_constants::FIELD_HEIGHT { + if y - game_constants::CIRCLE_SIZE <= bounds.1 || y + game_constants::CIRCLE_SIZE >= bounds.1 + game_constants::FIELD_HEIGHT { self.direction.y *= -1.0; y += self.direction.y; } From 85a5da5a3a8c3717d3f5d9be5211dabbdb3f0918 Mon Sep 17 00:00:00 2001 From: bob302 <54355416+bob302@users.noreply.github.com> Date: Tue, 13 Feb 2024 21:21:53 +0200 Subject: [PATCH 2/2] background color - Added a rough implementation of changing the background color --- src/formatter.rs | 4 +-- src/game.rs | 84 ++++++++++++++++++++++++++++++++++-------------- src/utils.rs | 10 ++++++ 3 files changed, 71 insertions(+), 27 deletions(-) diff --git a/src/formatter.rs b/src/formatter.rs index fbbf577..d2d5afa 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -1,5 +1,5 @@ pub fn format_squares_numbers(moon_squares: u32, sun_squares: u32) -> String { let moon_formatted = format!("{:03}", moon_squares); let sun_formatted = format!("{:03}", sun_squares); - format!("Moon {} | Sun {}", moon_formatted, sun_formatted) -} + format!("{} | {}", moon_formatted, sun_formatted) +} \ No newline at end of file diff --git a/src/game.rs b/src/game.rs index b4e6221..8f652be 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,12 +1,13 @@ use std::fmt::Alignment; use ggez::mint::Point2; +use ggez::timer; use ggez::{graphics, Context, GameResult, event::EventHandler}; -use ggez::graphics::{Color as GgezColor, Drawable, Text, TextFragment}; +use ggez::graphics::{Color, Drawable, Text, TextFragment}; use nalgebra::Vector2; use rand::Rng; -use crate::utils::count_objects; +use crate::utils::{count_objects, lerp_color}; use crate::object::{Object, Team, ObjectKind}; use crate::formatter::format_squares_numbers; @@ -33,6 +34,11 @@ pub struct Game { squares: Vec, sun_circle: Object, moon_circle: Object, + + background_color: Color, + target_background_color: Color, + transition_duration: f32, + transition_timer: f32, } impl Game { @@ -56,6 +62,11 @@ impl Game { let bounds = (field_start_x, field_start_y); + let background_color = Color::from_rgb(0xF7, 0xC5, 0x9F); + let target_background_color = Color::from_rgb(0x2A, 0x32, 0x4B); + let transition_duration = 2.0; + let transition_timer = 0.0; + let moon_circle = Object { position: moon_pos, team: Team::MOON, @@ -130,26 +141,34 @@ impl Game { squares, sun_circle, moon_circle, + + background_color, + target_background_color, + transition_duration, + transition_timer, } } - fn scale_position(&self, position: (f32, f32)) -> (f32, f32) { - (position.0 * self.scale_factor, position.1 * self.scale_factor) + fn update_background_color(&mut self, dt: f32) { + if self.transition_timer < self.transition_duration { + self.transition_timer += dt; + let t = self.transition_timer / self.transition_duration; + self.background_color = lerp_color(self.background_color, self.target_background_color, t); + } } - fn scale_vector(&self, vector: Vector2) -> Vector2 { - Vector2::new(vector.x * self.scale_factor, vector.y * self.scale_factor) + fn set_target_background_color(&mut self, target_color: Color, duration: f32) { + self.target_background_color = target_color; + self.transition_duration = duration; + self.transition_timer = 0.0; } } impl EventHandler for Game { - fn update(&mut self, _: &mut Context) -> GameResult { + fn update(&mut self, ctx: &mut Context) -> GameResult { self.sun_circle.update_position(); self.moon_circle.update_position(); - // println!("MOON POS: x: {} y: {}", self.moon_circle.position.0, self.moon_circle.position.1); - // println!("SUN POS: x: {} y: {}", self.sun_circle.position.0, self.sun_circle.position.1); - self.sun_circle.handle_boundary_collision(self.bounds); self.moon_circle.handle_boundary_collision(self.bounds); @@ -158,22 +177,40 @@ impl EventHandler for Game { self.moon_circle.handle_collision(square); } + let dt = ctx.time.delta().as_secs_f32(); + self.update_background_color(dt); + Ok(()) } - // TODO fix this MF + fn draw(&mut self, ctx: &mut Context) -> GameResult { - let canvas_color = GgezColor::from_rgb_u32(0x767B91); - let mut canvas = graphics::Canvas::from_frame(ctx, canvas_color); + let captured = count_objects(&self.squares, Team::MOON); + + let max_cells = game_constants::ROW_SIZE * game_constants::COLUMN_SIZE; + + let darkness_level = captured as f32 / max_cells as f32; + let start_color = [0xF7, 0xC5, 0x9F]; + let end_color = [0x2A, 0x32, 0x4B]; + + let interpolated_color = [ + (start_color[0] as f32 + (end_color[0] as f32 - start_color[0] as f32) * darkness_level) as u8, + (start_color[1] as f32 + (end_color[1] as f32 - start_color[1] as f32) * darkness_level) as u8, + (start_color[2] as f32 + (end_color[2] as f32 - start_color[2] as f32) * darkness_level) as u8, + ]; + + let canvas_color = Color::from_rgb(interpolated_color[0], interpolated_color[1], interpolated_color[2]); + + let mut canvas = graphics::Canvas::from_frame(ctx, canvas_color); + for i in 0..self.squares.len() { self.squares[i].draw(ctx, &mut canvas)?; } - - + self.sun_circle.draw(ctx, &mut canvas)?; self.moon_circle.draw(ctx, &mut canvas)?; - - let font_color = GgezColor::from_rgb_u32(0xE1E5EE); + + let font_color = Color::from_rgb_u32(0xE1E5EE); let text_fragment = TextFragment::new(format_squares_numbers(count_objects(&self.squares, Team::MOON), count_objects(&self.squares, Team::SUN))) .font("LiberationMono") .scale(40.0 * self.scale_factor) @@ -187,14 +224,11 @@ impl EventHandler for Game { let dest_point = ggez::glam::Vec2::new(x, y); - canvas.draw( - &text, - dest_point, - ); - - + canvas.draw(&text, dest_point); + canvas.finish(ctx)?; - + Ok(()) } -} \ No newline at end of file + +} diff --git a/src/utils.rs b/src/utils.rs index 02ad727..988e924 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,5 @@ +use ggez::graphics::Color; + use crate::object::{Object, Team}; pub fn count_objects(objects: &Vec, team: Team) -> u32 { @@ -8,4 +10,12 @@ pub fn count_objects(objects: &Vec, team: Team) -> u32 { } } count +} + +pub fn lerp_color(color1: Color, color2: Color, t: f32) -> Color { + let r = (color1.r * (1.0 - t) + color2.r * t).round() as u8; + let g = (color1.g * (1.0 - t) + color2.g * t).round() as u8; + let b = (color1.b * (1.0 - t) + color2.b * t).round() as u8; + let a = (color1.a * (1.0 - t) + color2.a * t).round() as u8; + Color::from_rgba(r, g, b, a) } \ No newline at end of file