mirror of
https://github.com/eliasstepanik/Bevy-TileSystem.git
synced 2026-01-10 21:28:29 +00:00
Working
This commit is contained in:
commit
e42586384a
BIN
assets/fonts/minecraft_font.ttf
Normal file
BIN
assets/fonts/minecraft_font.ttf
Normal file
Binary file not shown.
73
src/components/player.rs
Normal file
73
src/components/player.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
use bevy::input::ButtonInput;
|
||||||
|
use bevy::log::info;
|
||||||
|
use bevy::prelude::{Camera, Camera2dBundle, Commands, Component, GlobalTransform, KeyCode, Query, Res, Time, Transform, Vec3, With, Without};
|
||||||
|
use bevy::utils::default;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Player {
|
||||||
|
pub speed: f32,
|
||||||
|
pub position: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Player {
|
||||||
|
pub fn new(speed: f32, position: Vec3) -> Self {
|
||||||
|
Player { speed, position }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(mut commands: Commands) {
|
||||||
|
// Setup camera
|
||||||
|
info!("Adding Camera");
|
||||||
|
commands.spawn(Camera2dBundle {
|
||||||
|
transform: Transform {
|
||||||
|
scale: Vec3::splat(0.5), // Zoom in by reducing the scale (smaller scale means a larger view)
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Setup player with initial position
|
||||||
|
info!("Spawning player");
|
||||||
|
commands.spawn((
|
||||||
|
Player::new(500.0, Vec3::new(0.0, 0.0, 0.0)), // Initial player speed and position
|
||||||
|
Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
|
||||||
|
GlobalTransform::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_movement(
|
||||||
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||||
|
time: Res<Time>,
|
||||||
|
mut query: Query<(&mut Player, &mut Transform)>,
|
||||||
|
mut camera_query: Query<&mut Transform, (With<Camera>, Without<Player>)>,
|
||||||
|
) {
|
||||||
|
let delta_time = time.delta_seconds(); // Get the time elapsed between frames
|
||||||
|
|
||||||
|
for (mut player, mut player_transform) in query.iter_mut() {
|
||||||
|
// Update the player position based on input
|
||||||
|
if keyboard_input.pressed(KeyCode::KeyW) {
|
||||||
|
player.position.y += player.speed * delta_time;
|
||||||
|
}
|
||||||
|
if keyboard_input.pressed(KeyCode::KeyS) {
|
||||||
|
player.position.y -= player.speed * delta_time;
|
||||||
|
}
|
||||||
|
if keyboard_input.pressed(KeyCode::KeyA) {
|
||||||
|
player.position.x -= player.speed * delta_time;
|
||||||
|
}
|
||||||
|
if keyboard_input.pressed(KeyCode::KeyD) {
|
||||||
|
player.position.x += player.speed * delta_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the player's transform
|
||||||
|
player_transform.translation = player.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the camera transform to match the player's position
|
||||||
|
if let Ok(mut camera_transform) = camera_query.get_single_mut() {
|
||||||
|
if let Ok((player, _)) = query.get_single() {
|
||||||
|
camera_transform.translation.x = player.position.x;
|
||||||
|
camera_transform.translation.y = player.position.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/components/tilemap.rs
Normal file
83
src/components/tilemap.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use noise::{NoiseFn, Perlin};
|
||||||
|
use crate::components::world::CHUNK_SIZE;
|
||||||
|
|
||||||
|
pub struct Chunk {
|
||||||
|
pub tiles: Vec<Tile>,
|
||||||
|
pub chunk_coords: (i64, i64),
|
||||||
|
pub tile_entities: Vec<Entity>, // List of entity IDs for the tiles in this chunk
|
||||||
|
}
|
||||||
|
impl Chunk {
|
||||||
|
pub(crate) fn new(chunk_coords: (i64, i64)) -> Self {
|
||||||
|
info!("Generating chunk at coordinates: {:?}", chunk_coords);
|
||||||
|
|
||||||
|
let perlin = Perlin::new(1321231231);
|
||||||
|
let mut tiles = Vec::new();
|
||||||
|
|
||||||
|
for x in 0..CHUNK_SIZE {
|
||||||
|
for y in 0..CHUNK_SIZE {
|
||||||
|
let world_x = chunk_coords.0 * CHUNK_SIZE as i64 + x as i64;
|
||||||
|
let world_y = chunk_coords.1 * CHUNK_SIZE as i64 + y as i64;
|
||||||
|
|
||||||
|
let noise_value = perlin.get([world_x as f64 * 0.1, world_y as f64 * 0.1]);
|
||||||
|
|
||||||
|
let tile_type = if noise_value > 0.5 {
|
||||||
|
TileType::Grass
|
||||||
|
} else if noise_value > 0.0 {
|
||||||
|
TileType::Dirt
|
||||||
|
} else {
|
||||||
|
TileType::Water
|
||||||
|
};
|
||||||
|
|
||||||
|
tiles.push(Tile {
|
||||||
|
tile_type,
|
||||||
|
position: Vec2::new(x as f32, y as f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunk {
|
||||||
|
tiles,
|
||||||
|
chunk_coords,
|
||||||
|
tile_entities: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum TileType {
|
||||||
|
Grass,
|
||||||
|
Dirt,
|
||||||
|
Water,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Tile {
|
||||||
|
pub(crate) tile_type: TileType,
|
||||||
|
pub(crate) position: Vec2, // Position within the chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct TileMap {
|
||||||
|
pub(crate) chunks: HashMap<(i64, i64), Chunk>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TileMap {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
TileMap {
|
||||||
|
chunks: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn load_chunk(&mut self, chunk_coords: (i64, i64)) {
|
||||||
|
// Load or generate the chunk at chunk_coords
|
||||||
|
if !self.chunks.contains_key(&chunk_coords) {
|
||||||
|
let new_chunk = Chunk::new(chunk_coords);
|
||||||
|
self.chunks.insert(chunk_coords, new_chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unload_chunk(&mut self, chunk_coords: (i64, i64)) {
|
||||||
|
self.chunks.remove(&chunk_coords);
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/components/ui.rs
Normal file
50
src/components/ui.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use bevy::asset::AssetServer;
|
||||||
|
use bevy::prelude::{default, Camera, Color, Commands, Component, Query, Res, Text, TextBundle, TextStyle, Transform, Val, With};
|
||||||
|
use bevy::ui::{PositionType, Style};
|
||||||
|
use crate::components::player::Player;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct SpeedDisplay;
|
||||||
|
|
||||||
|
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
|
||||||
|
// Spawn UI text for debugging speed
|
||||||
|
commands.spawn((
|
||||||
|
TextBundle {
|
||||||
|
text: Text::from_section(
|
||||||
|
"Speed: 5.0", // Initial text
|
||||||
|
TextStyle {
|
||||||
|
font: asset_server.load("fonts/minecraft_font.ttf"), // Load a font
|
||||||
|
font_size: 40.0,
|
||||||
|
color: Color::WHITE,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
style: Style {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
top: Val::Px(10.0),
|
||||||
|
left: Val::Px(10.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
SpeedDisplay,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(
|
||||||
|
player_query: Query<&Player>,
|
||||||
|
mut query_text: Query<&mut Text, With<SpeedDisplay>>,
|
||||||
|
) {
|
||||||
|
let player = player_query.single();
|
||||||
|
let mut text = query_text.single_mut();
|
||||||
|
|
||||||
|
// Update the text with the current speed and position
|
||||||
|
text.sections[0].value = format!(
|
||||||
|
"Speed: {:.2}\nPosition: ({:.2}, {:.2}, {:.2})",
|
||||||
|
player.speed,
|
||||||
|
player.position.x,
|
||||||
|
player.position.y,
|
||||||
|
player.position.z,
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
106
src/components/world.rs
Normal file
106
src/components/world.rs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
use bevy::input::keyboard::KeyboardInput;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::render::mesh::PrimitiveTopology;
|
||||||
|
use bevy::render::render_asset::RenderAssetUsages;
|
||||||
|
use bevy::sprite::{MaterialMesh2dBundle, Mesh2dHandle};
|
||||||
|
use crate::components::tilemap::{Chunk, TileMap, TileType};
|
||||||
|
|
||||||
|
pub const CHUNK_SIZE: i32 = 64;
|
||||||
|
|
||||||
|
pub(crate) fn chunk_loader_system(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut tile_map: ResMut<TileMap>,
|
||||||
|
camera_query: Query<&Transform, With<Camera>>,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
|
) {
|
||||||
|
// Get the camera position
|
||||||
|
let camera_transform = camera_query.single();
|
||||||
|
let camera_position = camera_transform.translation;
|
||||||
|
|
||||||
|
// Calculate the chunk coordinates based on the camera's position
|
||||||
|
let chunk_x = (camera_position.x / (CHUNK_SIZE as f32 * 2.0)).floor() as i64;
|
||||||
|
let chunk_y = (camera_position.y / (CHUNK_SIZE as f32 * 2.0)).floor() as i64;
|
||||||
|
|
||||||
|
let load_radius = 5; // Number of chunks to load around the camera
|
||||||
|
|
||||||
|
// Load chunks within the radius of the camera position
|
||||||
|
for x in (chunk_x - load_radius)..=(chunk_x + load_radius) {
|
||||||
|
for y in (chunk_y - load_radius)..=(chunk_y + load_radius) {
|
||||||
|
if !tile_map.chunks.contains_key(&(x, y)) {
|
||||||
|
let mut new_chunk = Chunk::new((x, y));
|
||||||
|
render_chunk(&mut new_chunk, &mut commands, &mut meshes, &mut materials);
|
||||||
|
tile_map.chunks.insert((x, y), new_chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unload chunks that are outside the load radius
|
||||||
|
let chunks_to_unload: Vec<(i64, i64)> = tile_map
|
||||||
|
.chunks
|
||||||
|
.keys()
|
||||||
|
.filter(|&&(x, y)| (x - chunk_x).abs() > load_radius || (y - chunk_y).abs() > load_radius)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for chunk_coords in chunks_to_unload {
|
||||||
|
if let Some(chunk) = tile_map.chunks.remove(&chunk_coords) {
|
||||||
|
for entity in chunk.tile_entities {
|
||||||
|
commands.entity(entity).despawn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_chunk(
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
commands: &mut Commands,
|
||||||
|
meshes: &mut ResMut<Assets<Mesh>>,
|
||||||
|
materials: &mut ResMut<Assets<ColorMaterial>>,
|
||||||
|
) {
|
||||||
|
let tile_size = 2.0; // Each tile is 2x2 units in size
|
||||||
|
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default());
|
||||||
|
|
||||||
|
// Define vertices and uvs
|
||||||
|
let half_size = tile_size / 2.0;
|
||||||
|
let positions = vec![
|
||||||
|
[half_size, half_size, 0.0],
|
||||||
|
[half_size, -half_size, 0.0],
|
||||||
|
[-half_size, -half_size, 0.0],
|
||||||
|
[-half_size, half_size, 0.0],
|
||||||
|
];
|
||||||
|
|
||||||
|
let indices = vec![0, 1, 3, 1, 2, 3];
|
||||||
|
|
||||||
|
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
|
||||||
|
mesh.insert_indices(bevy::render::mesh::Indices::U32(indices));
|
||||||
|
|
||||||
|
let mesh_handle = meshes.add(mesh);
|
||||||
|
|
||||||
|
// Define materials for each TileType
|
||||||
|
let grass_material = materials.add(ColorMaterial::from(Color::rgb(0.1, 0.8, 0.1)));
|
||||||
|
let dirt_material = materials.add(ColorMaterial::from(Color::rgb(0.5, 0.3, 0.1)));
|
||||||
|
let water_material = materials.add(ColorMaterial::from(Color::rgb(0.1, 0.1, 0.8)));
|
||||||
|
|
||||||
|
// Spawn each tile within the chunk
|
||||||
|
for tile in &chunk.tiles {
|
||||||
|
let material_handle = match tile.tile_type {
|
||||||
|
TileType::Grass => grass_material.clone(),
|
||||||
|
TileType::Dirt => dirt_material.clone(),
|
||||||
|
TileType::Water => water_material.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let entity = commands.spawn(MaterialMesh2dBundle {
|
||||||
|
mesh: Mesh2dHandle(mesh_handle.clone()),
|
||||||
|
material: material_handle,
|
||||||
|
transform: Transform::from_translation(Vec3::new(
|
||||||
|
tile.position.x * tile_size + chunk.chunk_coords.0 as f32 * CHUNK_SIZE as f32 * tile_size,
|
||||||
|
tile.position.y * tile_size + chunk.chunk_coords.1 as f32 * CHUNK_SIZE as f32 * tile_size,
|
||||||
|
0.0,
|
||||||
|
)),
|
||||||
|
..default()
|
||||||
|
}).id();
|
||||||
|
|
||||||
|
chunk.tile_entities.push(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user