1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! This example shows how to map custom tiles and objects properties from Tiled to Bevy Components.
use std::env;
use bevy::prelude::*;
use bevy_ecs_tiled::prelude::*;
mod helper;
fn main() {
// Use a custom file path to export registered types in Tiled format
let mut path = env::current_dir().unwrap();
path.push("exports");
path.push("properties_basic_types.json");
App::new()
// Bevy default plugins: prevent blur effect by changing default sampling
.add_plugins(DefaultPlugins.build().set(ImagePlugin::default_nearest()))
// Add bevy_ecs_tiled plugin: bevy_ecs_tilemap::TilemapPlugin will
// be automatically added as well if it's not already done
// For demonstration purpose, provide a custom path where to export registered types
.add_plugins(TiledPlugin(TiledPluginConfig {
// Note: if you set this setting to `None`
// properties won't be exported anymore but
// you will still be able to load them from the map
tiled_types_export_file: Some(path),
tiled_types_filter: TiledFilter::from(
regex::RegexSet::new([
r"^properties_basic::.*",
r"^bevy_sprite::text2d::Text2d$",
r"^bevy_text::text::TextColor$",
r"^bevy_ecs::name::Name$",
])
.unwrap(),
),
}))
// Examples helper plugins, such as the logic to pan and zoom the camera
// This should not be used directly in your game (but you can always have a look)
.add_plugins(helper::HelperPlugin)
// We need to register all the custom types we want to use
.register_type::<Biome>()
.register_type::<SpawnPoint>()
.register_type::<Resource>()
// Add our systems and run the app!
.add_systems(Startup, startup)
.run();
}
fn startup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
commands
.spawn(TiledMap(
asset_server.load("maps/hexagonal/finite_pointy_top_odd.tmx"),
))
.observe(on_add_spawn)
.observe(on_map_created);
}
// You just have to define your Components and make sure they are properly registered and reflected.
// They will be exported to the Tiled .json file so they can be imported then used from Tiled.
// Next time you load your map, they will be automatically added as components on tiles / objects / layers entities
#[derive(Component, Default, Debug, Reflect)]
#[reflect(Component, Default)]
struct Biome {
ty: BiomeType,
move_cost: usize,
block_line_of_sight: bool,
}
#[derive(Default, Reflect, Debug)]
#[reflect(Default)]
enum BiomeType {
#[default]
Unknown,
Plain,
Desert,
Forest,
Mountain,
Water,
}
#[derive(Component, Default, Debug, Reflect)]
#[reflect(Component, Default)]
enum SpawnPoint {
#[default]
Unknown,
Player {
color: Color,
id: u32,
other_obj: Option<Entity>,
},
Enemy(Color),
Friendly,
}
#[derive(Component, Default, Debug, Reflect)]
#[reflect(Component, Default)]
enum Resource {
#[default]
Unknown,
Wheat,
Strawberry,
Wood,
Copper,
Gold,
}
// This observer will be triggered every time a `SpawnType` component is added to an entity.
// We can use it to spawn additional entity / insert more components
fn on_add_spawn(
add_spawn: On<Add, SpawnPoint>,
spawn_query: Query<(&SpawnPoint, &GlobalTransform)>,
mut _commands: Commands,
) {
// Get the entity that triggered the observer
let spawn_entity = add_spawn.event().entity;
// Retrieve the entity components
let Ok((spawn_type, global_transform)) = spawn_query.get(spawn_entity) else {
return;
};
info!(
"New SpawnPoint [{:?} @ {:?}]",
spawn_type,
global_transform.translation()
);
// Do some stuff based upon the spawn type value
match spawn_type {
SpawnPoint::Enemy(_) => {
// Spawn another entity
// _commands.spawn( ... );
}
SpawnPoint::Player { .. } => {
// Add other components to the same entity
// _commands.entity(spawn_entity).insert( ... );
}
_ => {}
};
}
// This observer will be triggered after our map is loaded and all custom properties have been inserted
// We can use it to do some global initialization
fn on_map_created(
map_created: On<TiledEvent<MapCreated>>,
map_query: Query<&TiledMapStorage, With<TiledMap>>,
tiles_query: Query<(&TilePos, Option<&Biome>, Option<&Resource>)>,
) {
// Get the map entity and storage component
let map_entity = map_created.event().origin;
let Ok(map_storage) = map_query.get(map_entity) else {
return;
};
// We will iterate over all tiles from our map and try to access our custom properties
for ((_tile_id, _tileset_id), entities_list) in map_storage.tiles() {
for tile_entity in entities_list {
let Ok((pos, biome, resource)) = tiles_query.get(*tile_entity) else {
continue;
};
// Here, we only print the content of our tile but we could also do some
// global initialization.
// A typical use case would be to initialize a resource so we can map a tile
// position to a biome and / or a resource (which could be useful for pathfinding)
if let Some(i) = biome {
// Only print the first tile to avoid flooding the console
info_once!("Found Biome [{:?} @ {:?}]", i, pos);
}
if let Some(i) = resource {
// Only print the first tile to avoid flooding the console
info_once!("Found Resource [{:?} @ {:?}]", i, pos);
}
}
}
}