| 1 |
|
| 2 |
|
| 3 |
use bevy::{ |
| 4 |
color::palettes::tailwind::RED_400, |
| 5 |
image::{ImageArrayLayout, ImageLoaderSettings}, |
| 6 |
prelude::*, |
| 7 |
sprite_render::{TileData, TilemapChunk, TilemapChunkTileData}, |
| 8 |
}; |
| 9 |
use chacha20::ChaCha8Rng; |
| 10 |
use rand::{RngExt, SeedableRng}; |
| 11 |
|
| 12 |
fn main() { |
| 13 |
App::new() |
| 14 |
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) |
| 15 |
.add_systems(Startup, (setup, spawn_fake_player).chain()) |
| 16 |
.add_systems(Update, (update_tilemap, move_player, log_tile)) |
| 17 |
.run(); |
| 18 |
} |
| 19 |
|
| 20 |
#[derive(Component, Deref, DerefMut)] |
| 21 |
struct UpdateTimer(Timer); |
| 22 |
|
| 23 |
#[derive(Resource, Deref, DerefMut)] |
| 24 |
struct SeededRng(ChaCha8Rng); |
| 25 |
|
| 26 |
fn setup(mut commands: Commands, assets: Res<AssetServer>) { |
| 27 |
|
| 28 |
|
| 29 |
let mut rng = ChaCha8Rng::seed_from_u64(42); |
| 30 |
|
| 31 |
let chunk_size = UVec2::splat(64); |
| 32 |
let tile_display_size = UVec2::splat(8); |
| 33 |
let tile_data: Vec<Option<TileData>> = (0..chunk_size.element_product()) |
| 34 |
.map(|_| rng.random_range(0..5)) |
| 35 |
.map(|i| { |
| 36 |
if i == 0 { |
| 37 |
None |
| 38 |
} else { |
| 39 |
Some(TileData::from_tileset_index(i - 1)) |
| 40 |
} |
| 41 |
}) |
| 42 |
.collect(); |
| 43 |
|
| 44 |
commands.spawn(( |
| 45 |
TilemapChunk { |
| 46 |
chunk_size, |
| 47 |
tile_display_size, |
| 48 |
tileset: assets |
| 49 |
.load_builder() |
| 50 |
.with_settings(|settings: &mut ImageLoaderSettings| { |
| 51 |
|
| 52 |
|
| 53 |
settings.array_layout = Some(ImageArrayLayout::RowCount { rows: 4 }); |
| 54 |
}) |
| 55 |
.load("textures/array_texture.png"), |
| 56 |
..default() |
| 57 |
}, |
| 58 |
TilemapChunkTileData(tile_data), |
| 59 |
UpdateTimer(Timer::from_seconds(0.1, TimerMode::Repeating)), |
| 60 |
)); |
| 61 |
|
| 62 |
commands.spawn(Camera2d); |
| 63 |
|
| 64 |
commands.insert_resource(SeededRng(rng)); |
| 65 |
} |
| 66 |
|
| 67 |
#[derive(Component)] |
| 68 |
struct MovePlayer; |
| 69 |
|
| 70 |
fn spawn_fake_player( |
| 71 |
mut commands: Commands, |
| 72 |
mut meshes: ResMut<Assets<Mesh>>, |
| 73 |
mut materials: ResMut<Assets<ColorMaterial>>, |
| 74 |
chunk: Single<&TilemapChunk>, |
| 75 |
) { |
| 76 |
let mut transform = chunk.calculate_tile_transform(UVec2::new(0, 0)); |
| 77 |
transform.translation.z = 1.; |
| 78 |
|
| 79 |
commands.spawn(( |
| 80 |
Mesh2d(meshes.add(Rectangle::new(8., 8.))), |
| 81 |
MeshMaterial2d(materials.add(Color::from(RED_400))), |
| 82 |
transform, |
| 83 |
MovePlayer, |
| 84 |
)); |
| 85 |
|
| 86 |
let mut transform = chunk.calculate_tile_transform(UVec2::new(5, 6)); |
| 87 |
transform.translation.z = 1.; |
| 88 |
|
| 89 |
|
| 90 |
commands.spawn(( |
| 91 |
Mesh2d(meshes.add(Rectangle::new(8., 8.))), |
| 92 |
MeshMaterial2d(materials.add(Color::from(RED_400))), |
| 93 |
transform, |
| 94 |
)); |
| 95 |
} |
| 96 |
|
| 97 |
fn move_player( |
| 98 |
mut player: Single<&mut Transform, With<MovePlayer>>, |
| 99 |
time: Res<Time>, |
| 100 |
chunk: Single<&TilemapChunk>, |
| 101 |
) { |
| 102 |
let t = (ops::sin(time.elapsed_secs()) + 1.) / 2.; |
| 103 |
|
| 104 |
let origin = chunk |
| 105 |
.calculate_tile_transform(UVec2::new(0, 0)) |
| 106 |
.translation |
| 107 |
.x; |
| 108 |
let destination = chunk |
| 109 |
.calculate_tile_transform(UVec2::new(63, 0)) |
| 110 |
.translation |
| 111 |
.x; |
| 112 |
|
| 113 |
player.translation.x = origin.lerp(destination, t); |
| 114 |
} |
| 115 |
|
| 116 |
fn update_tilemap( |
| 117 |
time: Res<Time>, |
| 118 |
mut query: Query<(&mut TilemapChunkTileData, &mut UpdateTimer)>, |
| 119 |
mut rng: ResMut<SeededRng>, |
| 120 |
) { |
| 121 |
for (mut tile_data, mut timer) in query.iter_mut() { |
| 122 |
timer.tick(time.delta()); |
| 123 |
|
| 124 |
if timer.just_finished() { |
| 125 |
for _ in 0..50 { |
| 126 |
let index = rng.random_range(0..tile_data.len()); |
| 127 |
tile_data[index] = Some(TileData::from_tileset_index(rng.random_range(0..5))); |
| 128 |
} |
| 129 |
} |
| 130 |
} |
| 131 |
} |
| 132 |
|
| 133 |
|
| 134 |
fn log_tile(tilemap: Single<(&TilemapChunk, &TilemapChunkTileData)>, mut local: Local<u16>) { |
| 135 |
let (chunk, data) = tilemap.into_inner(); |
| 136 |
let Some(tile_data) = data.tile_data_from_tile_pos(chunk.chunk_size, UVec2::new(3, 4)) else { |
| 137 |
return; |
| 138 |
}; |
| 139 |
|
| 140 |
if tile_data.tileset_index != *local { |
| 141 |
info!(?tile_data, "tile_data changed"); |
| 142 |
*local = tile_data.tileset_index; |
| 143 |
} |
| 144 |
} |