Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/2d/tilemap_chunk.rs
9299 views
1
//! Shows a tilemap chunk rendered with a single draw call.
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 rand::{Rng, SeedableRng};
10
use rand_chacha::ChaCha8Rng;
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
// We're seeding the PRNG here to make this example deterministic for testing purposes.
28
// This isn't strictly required in practical use unless you need your app to be deterministic.
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.load_with_settings(
49
"textures/array_texture.png",
50
|settings: &mut ImageLoaderSettings| {
51
// The tileset texture is expected to be an array of tile textures, so we tell the
52
// `ImageLoader` that our texture is composed of 4 stacked tile images.
53
settings.array_layout = Some(ImageArrayLayout::RowCount { rows: 4 });
54
},
55
),
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
// second "player" to visually test a non-zero position
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
// find the data for an arbitrary tile in the chunk and log its data
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
// log when the tile changes
140
if tile_data.tileset_index != *local {
141
info!(?tile_data, "tile_data changed");
142
*local = tile_data.tileset_index;
143
}
144
}
145
146