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