Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/2d/tilemap_chunk.rs
6849 views
1
//! Shows a tilemap chunk rendered with a single draw call.
2
3
use bevy::{
4
color::palettes::tailwind::RED_400,
5
prelude::*,
6
sprite_render::{TileData, TilemapChunk, TilemapChunkTileData},
7
};
8
use rand::{Rng, SeedableRng};
9
use rand_chacha::ChaCha8Rng;
10
11
fn 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)]
23
struct UpdateTimer(Timer);
24
25
#[derive(Resource, Deref, DerefMut)]
26
struct SeededRng(ChaCha8Rng);
27
28
fn setup(mut commands: Commands, assets: Res<AssetServer>) {
29
// We're seeding the PRNG here to make this example deterministic for testing purposes.
30
// This isn't strictly required in practical use unless you need your app to be deterministic.
31
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)]
63
struct MovePlayer;
64
65
fn 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
// second "player" to visually test a non-zero position
85
commands.spawn((
86
Mesh2d(meshes.add(Rectangle::new(8., 8.))),
87
MeshMaterial2d(materials.add(Color::from(RED_400))),
88
transform,
89
));
90
}
91
92
fn 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
111
fn 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
125
fn 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
142
// find the data for an arbitrary tile in the chunk and log its data
143
fn 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
// log when the tile changes
149
if tile_data.tileset_index != *local {
150
info!(?tile_data, "tile_data changed");
151
*local = tile_data.tileset_index;
152
}
153
}
154
155