Merge branch '38-game-configuration' into 'dev'
feat: Game configuration #38 See merge request softwaregrundprojekt/2025-2026/einzelprojekt/tutorium-moritz/bernroider-dominik/bernroider-dominik!7
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -3622,6 +3622,8 @@ dependencies = [
|
||||
"bevy",
|
||||
"bevy_aseprite_ultra",
|
||||
"bevy_dev_tools",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -17,3 +17,5 @@ lto = "thin"
|
||||
bevy = "0.17.2"
|
||||
bevy_aseprite_ultra = "0.7.0"
|
||||
bevy_dev_tools = "0.17.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
5
assets/config.json
Normal file
5
assets/config.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"grid_width": 10,
|
||||
"grid_height": 10,
|
||||
"pom_speed": 2
|
||||
}
|
||||
@@ -24,10 +24,10 @@
|
||||
|
||||
rust-toolchain = with fenix.packages.${system};
|
||||
combine [
|
||||
beta.rustc
|
||||
beta.cargo
|
||||
beta.rust-src
|
||||
beta.rust-analyzer
|
||||
stable.rustc
|
||||
stable.cargo
|
||||
stable.rust-src
|
||||
stable.rust-analyzer
|
||||
];
|
||||
|
||||
bevyDeps = with pkgs; [
|
||||
|
||||
17
src/config.rs
Normal file
17
src/config.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use bevy::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
|
||||
#[derive(Resource, Deserialize, Debug)]
|
||||
pub struct GameConfig {
|
||||
pub grid_width: u32,
|
||||
pub grid_height: u32,
|
||||
pub pom_speed: f32,
|
||||
}
|
||||
|
||||
pub fn read_config() -> Option<GameConfig> {
|
||||
let file = File::open("assets/config.json").ok()?;
|
||||
let reader = BufReader::new(file);
|
||||
serde_json::from_reader(reader).ok()
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod components;
|
||||
pub mod config;
|
||||
pub mod errors;
|
||||
pub mod messages;
|
||||
pub mod plugins;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_aseprite_ultra::prelude::*;
|
||||
use bevy_dev_tools::fps_overlay::*;
|
||||
use pomomon_garden::config::read_config;
|
||||
use pomomon_garden::plugins;
|
||||
|
||||
fn main() {
|
||||
let config = read_config().expect("Error reading config");
|
||||
|
||||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(ImagePlugin::default_nearest()),
|
||||
@@ -29,5 +32,6 @@ fn main() {
|
||||
plugins::PomPlugin,
|
||||
plugins::InputPlugin,
|
||||
))
|
||||
.insert_resource(config)
|
||||
.run();
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
use crate::{
|
||||
components::tile::{Grid, Tile, TileState},
|
||||
config::GameConfig,
|
||||
states::AppState,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use bevy_aseprite_ultra::prelude::AseSlice;
|
||||
|
||||
pub const TILE_SIZE: f32 = 32.0;
|
||||
pub const GRID_WIDTH: u32 = 10;
|
||||
pub const GRID_HEIGHT: u32 = 10;
|
||||
|
||||
pub const GRID_START_X: f32 = -(GRID_WIDTH as f32 * TILE_SIZE) / 2.0 + TILE_SIZE / 2.0;
|
||||
pub const GRID_START_Y: f32 = -(GRID_HEIGHT as f32 * TILE_SIZE) / 2.0 + TILE_SIZE / 2.0;
|
||||
pub fn grid_start_x(grid_width: u32) -> f32 {
|
||||
-(grid_width as f32 * TILE_SIZE) / 2.0 + TILE_SIZE / 2.0
|
||||
}
|
||||
pub fn grid_start_y(grid_height: u32) -> f32 {
|
||||
-(grid_height as f32 * TILE_SIZE) / 2.0 + TILE_SIZE / 2.0
|
||||
}
|
||||
|
||||
pub struct GridPlugin;
|
||||
|
||||
@@ -26,13 +29,15 @@ impl Plugin for GridPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let mut tiles = Vec::with_capacity(GRID_WIDTH as usize);
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>, config: Res<GameConfig>) {
|
||||
let grid_width = config.grid_width;
|
||||
let grid_height = config.grid_height;
|
||||
let mut tiles = Vec::with_capacity(grid_width as usize);
|
||||
|
||||
for x in 0..GRID_WIDTH {
|
||||
let mut column = Vec::with_capacity(GRID_HEIGHT as usize);
|
||||
for x in 0..grid_width {
|
||||
let mut column = Vec::with_capacity(grid_height as usize);
|
||||
|
||||
for y in 0..GRID_HEIGHT {
|
||||
for y in 0..grid_height {
|
||||
let tile_entity = commands
|
||||
.spawn((
|
||||
Tile { x, y },
|
||||
@@ -42,7 +47,13 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
aseprite: asset_server.load("tiles/tile-unclaimed.aseprite"),
|
||||
},
|
||||
Sprite::default(),
|
||||
Transform::from_translation(grid_to_world_coords(x, y, None)),
|
||||
Transform::from_translation(grid_to_world_coords(
|
||||
x,
|
||||
y,
|
||||
None,
|
||||
grid_width,
|
||||
grid_height,
|
||||
)),
|
||||
))
|
||||
.id();
|
||||
column.push(tile_entity);
|
||||
@@ -51,8 +62,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
}
|
||||
|
||||
commands.insert_resource(Grid {
|
||||
width: GRID_WIDTH,
|
||||
height: GRID_HEIGHT,
|
||||
width: grid_width,
|
||||
height: grid_height,
|
||||
tiles,
|
||||
});
|
||||
}
|
||||
@@ -84,27 +95,36 @@ fn update_tile_colors(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn world_to_grid_coords(world_pos: Vec3) -> (u32, u32) {
|
||||
let x = ((world_pos.x - GRID_START_X + TILE_SIZE / 2.0) / TILE_SIZE).floor();
|
||||
let y = ((world_pos.y - GRID_START_Y + TILE_SIZE / 2.0) / TILE_SIZE).floor();
|
||||
pub fn world_to_grid_coords(world_pos: Vec3, grid_width: u32, grid_height: u32) -> (u32, u32) {
|
||||
let start_x = grid_start_x(grid_width);
|
||||
let start_y = grid_start_y(grid_height);
|
||||
|
||||
let x = ((world_pos.x - start_x + TILE_SIZE / 2.0) / TILE_SIZE).floor();
|
||||
let y = ((world_pos.y - start_y + TILE_SIZE / 2.0) / TILE_SIZE).floor();
|
||||
|
||||
let mut x_u32 = x as u32;
|
||||
let mut y_u32 = y as u32;
|
||||
|
||||
if x_u32 >= GRID_WIDTH {
|
||||
x_u32 = GRID_WIDTH - 1;
|
||||
if x_u32 >= grid_width {
|
||||
x_u32 = grid_width - 1;
|
||||
}
|
||||
if y_u32 >= GRID_HEIGHT {
|
||||
y_u32 = GRID_HEIGHT - 1;
|
||||
if y_u32 >= grid_height {
|
||||
y_u32 = grid_height - 1;
|
||||
}
|
||||
|
||||
(x_u32, y_u32)
|
||||
}
|
||||
|
||||
pub fn grid_to_world_coords(grid_x: u32, grid_y: u32, z: Option<f32>) -> Vec3 {
|
||||
pub fn grid_to_world_coords(
|
||||
grid_x: u32,
|
||||
grid_y: u32,
|
||||
z: Option<f32>,
|
||||
grid_width: u32,
|
||||
grid_height: u32,
|
||||
) -> Vec3 {
|
||||
Vec3::new(
|
||||
GRID_START_X + grid_x as f32 * TILE_SIZE,
|
||||
GRID_START_Y + grid_y as f32 * TILE_SIZE,
|
||||
grid_start_x(grid_width) + grid_x as f32 * TILE_SIZE,
|
||||
grid_start_y(grid_height) + grid_y as f32 * TILE_SIZE,
|
||||
z.unwrap_or(0.0),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use bevy::prelude::*;
|
||||
use bevy::window::PrimaryWindow;
|
||||
|
||||
use crate::components::tile::{Grid, TileState};
|
||||
use crate::config::GameConfig;
|
||||
use crate::messages::{InteractStartMessage, InvalidMoveMessage, MoveMessage};
|
||||
use crate::plugins::grid::world_to_grid_coords;
|
||||
use crate::states::AppState;
|
||||
@@ -28,6 +29,7 @@ fn move_click(
|
||||
mouse_btn: Res<ButtonInput<MouseButton>>,
|
||||
window: Single<&Window, With<PrimaryWindow>>,
|
||||
camera: Single<(&Camera, &GlobalTransform), With<Camera2d>>,
|
||||
config: Res<GameConfig>,
|
||||
) {
|
||||
if mouse_btn.just_pressed(MouseButton::Right) {
|
||||
let (cam, cam_transform) = *camera;
|
||||
@@ -38,7 +40,7 @@ fn move_click(
|
||||
let Ok(world_pos) = cam.viewport_to_world(cam_transform, cursor_pos) else {
|
||||
return;
|
||||
};
|
||||
let (x, y) = world_to_grid_coords(world_pos.origin);
|
||||
let (x, y) = world_to_grid_coords(world_pos.origin, config.grid_width, config.grid_height);
|
||||
|
||||
println!("Move Click: ({}, {})", x, y);
|
||||
move_messages.write(MoveMessage { x, y });
|
||||
@@ -50,6 +52,7 @@ fn interact_click(
|
||||
mouse_btn: Res<ButtonInput<MouseButton>>,
|
||||
window: Single<&Window, With<PrimaryWindow>>,
|
||||
camera: Single<(&Camera, &GlobalTransform), With<Camera2d>>,
|
||||
config: Res<GameConfig>,
|
||||
// for debug
|
||||
grid: ResMut<Grid>,
|
||||
tile_query: Query<&mut TileState>,
|
||||
@@ -63,7 +66,7 @@ fn interact_click(
|
||||
let Ok(world_pos) = cam.viewport_to_world(cam_transform, cursor_pos) else {
|
||||
return;
|
||||
};
|
||||
let (x, y) = world_to_grid_coords(world_pos.origin);
|
||||
let (x, y) = world_to_grid_coords(world_pos.origin, config.grid_width, config.grid_height);
|
||||
|
||||
println!("Interact Click: ({}, {})", x, y);
|
||||
interact_messages.write(InteractStartMessage { x, y });
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use crate::components::pom::{GridPosition, MovingState, PathQueue, Pom};
|
||||
use crate::components::tile::{Grid, TileState};
|
||||
use crate::config::GameConfig;
|
||||
use crate::messages::{InvalidMoveMessage, MoveMessage};
|
||||
use crate::plugins::grid::{GRID_WIDTH, grid_to_world_coords};
|
||||
use crate::plugins::grid::{TILE_SIZE, grid_to_world_coords};
|
||||
use crate::states::*;
|
||||
use crate::utils::pathfinding::find_path;
|
||||
use bevy::prelude::*;
|
||||
use bevy_aseprite_ultra::prelude::*;
|
||||
|
||||
const MOVE_SPEED: f32 = 2.0 * GRID_WIDTH as f32;
|
||||
|
||||
pub struct PomPlugin;
|
||||
|
||||
impl Plugin for PomPlugin {
|
||||
@@ -23,7 +22,7 @@ impl Plugin for PomPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>, config: Res<GameConfig>) {
|
||||
commands.spawn((
|
||||
Pom,
|
||||
GridPosition { x: 0, y: 0 },
|
||||
@@ -34,7 +33,13 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
animation: Animation::tag("sleep-sit-start").with_repeat(AnimationRepeat::Loop),
|
||||
},
|
||||
Sprite::default(),
|
||||
Transform::from_translation(grid_to_world_coords(0, 0, Some(1.0))),
|
||||
Transform::from_translation(grid_to_world_coords(
|
||||
0,
|
||||
0,
|
||||
Some(1.0),
|
||||
config.grid_width,
|
||||
config.grid_height,
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -82,12 +87,20 @@ fn move_pom(
|
||||
&mut PathQueue,
|
||||
&mut MovingState,
|
||||
)>,
|
||||
config: Res<GameConfig>,
|
||||
) {
|
||||
let dt = time.delta_secs();
|
||||
let move_speed = config.pom_speed * TILE_SIZE;
|
||||
|
||||
for (mut transform, mut grid_pos, mut path_queue, mut moving_state) in query.iter_mut() {
|
||||
if let Some(&target) = path_queue.steps.front() {
|
||||
let target_pos = grid_to_world_coords(target.0, target.1, Some(1.0));
|
||||
let target_pos = grid_to_world_coords(
|
||||
target.0,
|
||||
target.1,
|
||||
Some(1.0),
|
||||
config.grid_width,
|
||||
config.grid_height,
|
||||
);
|
||||
let distance = transform.translation.distance(target_pos);
|
||||
|
||||
let dx = target.0 as i32 - grid_pos.x as i32;
|
||||
@@ -100,14 +113,14 @@ fn move_pom(
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if distance < MOVE_SPEED * dt {
|
||||
if distance < move_speed * dt {
|
||||
transform.translation = target_pos;
|
||||
grid_pos.x = target.0;
|
||||
grid_pos.y = target.1;
|
||||
path_queue.steps.pop_front();
|
||||
} else {
|
||||
let direction = (target_pos - transform.translation).normalize();
|
||||
transform.translation += direction * MOVE_SPEED * dt;
|
||||
transform.translation += direction * move_speed * dt;
|
||||
}
|
||||
} else {
|
||||
*moving_state = MovingState::Idle;
|
||||
|
||||
Reference in New Issue
Block a user