Skip to content

Commit

Permalink
First push
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescarterbell committed Dec 18, 2023
1 parent f8c5fa3 commit 7ef94f4
Show file tree
Hide file tree
Showing 47 changed files with 613 additions and 3,340 deletions.
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"rust-analyzer.linkedProjects": [
".\\crates\\bevy_tiles_render\\Cargo.toml",
".\\crates\\bevy_tiles\\Cargo.toml"
]
}
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ opt-level = 3
aery = "0.5.1"
bevy = {version = "0.12", default-features = false}
rstest = "0.18.2"
regex = "1.10.2"
bimap = "0.6.3"

[workspace.lints.clippy]
type_complexity = "allow"
undocumented_unsafe_blocks = "deny"
all = "deny"

[workspace.lints.rust]
unused_imports = "deny"
unused_imports = "warn"
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

A suite of tilemap plugins for [`bevy`](https://bevyengine.org/), focusing on ergonomics and ease of use.

Expect crates in this suite to move a little bit slower than other tilemap crates, I have a busy schedule and a lot of the work here
is meant to be exploration of API design posibilities, rather than aiming for immediate use.

# Crates
* [`bevy_tiles`](crates/bevy_tiles)
[![Crates.io](https://img.shields.io/crates/v/bevy_tiles)](https://crates.io/crates/bevy_tiles)
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_tiles/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ opt-level = 3
[dependencies]
aery = {workspace = true}
bevy = {workspace = true}
bimap = "0.6.3"
bimap = {workspace = true}

[dev-dependencies]
rstest = {workspace = true}
Expand Down
7 changes: 6 additions & 1 deletion crates/bevy_tiles/src/chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub use chunk_query::*;
/// An aery relation on chunks that point towards the map they are a part of.
#[derive(Relation)]
#[aery(Recursive)]
pub struct InMap<L, const N: usize>(std::marker::PhantomData<L>);
pub struct InMap;

/// The coordinate of a given chunk.
/// # Note
Expand Down Expand Up @@ -49,4 +49,9 @@ impl Chunk {
tiles: vec![None; chunk_size],
}
}

/// Gets the total number of tiles this chunk can hold.
pub fn total_size(&self) -> usize {
self.tiles.len()
}
}
8 changes: 4 additions & 4 deletions crates/bevy_tiles/src/chunks/chunk_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use bevy::{

use crate::{
chunks::ChunkCoord,
maps::{MapLabel, TileMap, TileMapLabel},
maps::{MapLabel, TileMapLabel, Tilemap},
prelude::{calculate_chunk_coordinate, Chunk, CoordIterator, InMap},
};

Expand All @@ -26,8 +26,8 @@ where
Q: WorldQuery + 'static,
F: ReadOnlyWorldQuery + 'static,
{
chunk_q: Query<'w, 's, Q, (F, Relations<InMap<L, N>>, With<Chunk>)>,
map_q: Query<'w, 's, &'static TileMap<N>, With<MapLabel<L>>>,
chunk_q: Query<'w, 's, Q, (F, Relations<InMap>, With<Chunk>, With<MapLabel<L>>)>,
map_q: Query<'w, 's, &'static Tilemap<N>, With<MapLabel<L>>>,
}

impl<'w, 's, L, Q, F, const N: usize> Deref for ChunkQuery<'w, 's, L, Q, F, N>
Expand All @@ -36,7 +36,7 @@ where
Q: WorldQuery + 'static,
F: ReadOnlyWorldQuery + 'static,
{
type Target = Query<'w, 's, Q, (F, Relations<InMap<L, N>>, With<Chunk>)>;
type Target = Query<'w, 's, Q, (F, Relations<InMap>, With<Chunk>, With<MapLabel<L>>)>;

#[inline]
fn deref(&self) -> &Self::Target {
Expand Down
80 changes: 42 additions & 38 deletions crates/bevy_tiles/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use std::{
use crate::{
maps::MapLabel,
prelude::{
calculate_chunk_coordinate, calculate_tile_index, Chunk, ChunkCoord, InMap, TileMap,
TileMapLabel,
calculate_chunk_coordinate, calculate_tile_index, Chunk, ChunkCoord, InMap, TileMapLabel,
Tilemap,
},
tiles::{InChunk, TileCoord, TileIndex},
};
Expand Down Expand Up @@ -240,7 +240,7 @@ where
#[inline]
fn spawn_or_remove_chunk<L, const N: usize>(
world: &mut World,
map: &mut TileMap<N>,
map: &mut Tilemap<N>,
map_id: Entity,
chunk_c: [isize; N],
) -> (Entity, Chunk)
Expand All @@ -252,7 +252,7 @@ where
} else {
let chunk_id = world.spawn(ChunkCoord::from(chunk_c)).id();
map.chunks.insert(chunk_c.into(), chunk_id);
Set::<InMap<L, N>>::new(chunk_id, map_id).apply(world);
Set::<InMap>::new(chunk_id, map_id).apply(world);
(chunk_id, Chunk::new(L::CHUNK_SIZE.pow(N as u32)))
}
}
Expand All @@ -261,7 +261,7 @@ where
#[inline]
fn remove_chunk<const N: usize>(
world: &mut World,
map: &mut TileMap<N>,
map: &mut Tilemap<N>,
chunk_c: [isize; N],
) -> Option<(Entity, Chunk)> {
map.chunks
Expand All @@ -273,7 +273,7 @@ fn remove_chunk<const N: usize>(

/// Takes the map out of the world or spawns a new one and returns the entity id to return the map to.
#[inline]
fn spawn_or_remove_map<L, const N: usize>(world: &mut World) -> (Entity, TileMap<N>)
fn spawn_or_remove_map<L, const N: usize>(world: &mut World) -> (Entity, Tilemap<N>)
where
L: TileMapLabel + Send + 'static,
{
Expand All @@ -283,19 +283,19 @@ where
} else {
(
world.spawn(MapLabel::<L>::default()).id(),
TileMap::<N>::default(),
Tilemap::<N>::default(),
)
}
}

/// Takes the map out of the world if it exists.
#[inline]
fn remove_map<L, const N: usize>(world: &mut World) -> Option<(Entity, TileMap<N>)>
fn remove_map<L, const N: usize>(world: &mut World) -> Option<(Entity, Tilemap<N>)>
where
L: TileMapLabel + Send + 'static,
{
world
.query_filtered::<Entity, (With<TileMap<N>>, With<MapLabel<L>>)>()
.query_filtered::<Entity, (With<Tilemap<N>>, With<MapLabel<L>>)>()
.get_single_mut(world)
.ok()
.map(|map_id| {
Expand All @@ -304,7 +304,7 @@ where
world
.get_entity_mut(map_id)
.unwrap()
.take::<TileMap<N>>()
.take::<Tilemap<N>>()
.unwrap(),
)
})
Expand All @@ -331,12 +331,13 @@ where
}
}

Set::<InChunk<L, N>>::new(tile_id, chunk_id).apply(world);
Set::<InChunk>::new(tile_id, chunk_id).apply(world);

world
.get_entity_mut(tile_id)
.unwrap()
.insert((TileIndex::from(tile_i), TileCoord::<N>::new(tile_c)));
world.get_entity_mut(tile_id).unwrap().insert((
TileIndex::from(tile_i),
TileCoord::<N>::new(tile_c),
MapLabel::<L>::default(),
));

world.get_entity_mut(chunk_id).unwrap().insert(chunk);
world.get_entity_mut(map_id).unwrap().insert(map);
Expand Down Expand Up @@ -369,9 +370,9 @@ where
.and_then(|tile| tile.take())
.and_then(|tile_id| world.get_entity_mut(tile_id))
{
tile_e.remove::<(TileIndex, TileCoord)>();
tile_e.remove::<(TileIndex, TileCoord, MapLabel<L>)>();
let tile_id = tile_e.id();
Unset::<InChunk<L, N>>::new(tile_id, chunk_id).apply(world);
Unset::<InChunk>::new(tile_id, chunk_id).apply(world);
Some(tile_id)
} else {
None
Expand Down Expand Up @@ -412,12 +413,13 @@ pub fn insert_tile_batch<L, const N: usize>(
}
}

Set::<InChunk<L, N>>::new(tile_id, chunk_id).apply(world);
Set::<InChunk>::new(tile_id, chunk_id).apply(world);

world
.get_entity_mut(tile_id)
.unwrap()
.insert((TileIndex::from(tile_i), TileCoord::<N>::new(tile_c)));
world.get_entity_mut(tile_id).unwrap().insert((
TileIndex::from(tile_i),
TileCoord::<N>::new(tile_c),
MapLabel::<L>::default(),
));
}

world.get_entity_mut(chunk_id).unwrap().insert(chunk);
Expand Down Expand Up @@ -473,9 +475,9 @@ where
.and_then(|tile| tile.take())
.and_then(|tile_id| world.get_entity_mut(tile_id))
{
tile_e.remove::<(TileIndex, TileCoord)>();
tile_e.remove::<(TileIndex, TileCoord, MapLabel<L>)>();
let tile_id = tile_e.id();
Unset::<InChunk<L, N>>::new(tile_id, chunk_id).apply(world);
Unset::<InChunk>::new(tile_id, chunk_id).apply(world);
tile_ids.push((tile_c, tile_id));
}
}
Expand All @@ -502,8 +504,9 @@ where
world.get_entity_mut(chunk_id).unwrap().insert((
Chunk::new(L::CHUNK_SIZE.pow(N as u32)),
ChunkCoord::from(chunk_c),
MapLabel::<L>::default(),
));
Set::<InMap<L, N>>::new(chunk_id, map_id).apply(world);
Set::<InMap>::new(chunk_id, map_id).apply(world);
map.chunks.insert(chunk_c.into(), chunk_id);

world.entity_mut(map_id).insert(map);
Expand All @@ -526,10 +529,10 @@ where
.remove::<ChunkCoord<N>>(&chunk_c.into())
.and_then(|chunk_id| world.get_entity_mut(chunk_id))
{
chunk_e.remove::<(Chunk, ChunkCoord)>();
chunk_e.remove::<(Chunk, ChunkCoord, MapLabel<L>)>();
let chunk_id = chunk_e.id();
Unset::<InMap<L, N>>::new(chunk_id, map_id).apply(world);
Withdraw::<InChunk<L, N>>::new(chunk_id).apply(world);
Unset::<InMap>::new(chunk_id, map_id).apply(world);
Withdraw::<InChunk>::new(chunk_id).apply(world);
Some(chunk_id)
} else {
None
Expand Down Expand Up @@ -557,13 +560,13 @@ where
.remove::<ChunkCoord<N>>(&chunk_c.into())
.and_then(|chunk_id| world.get_entity_mut(chunk_id))
{
let (chunk, _) = chunk_e.take::<(Chunk, ChunkCoord)>().unwrap();
let (chunk, _, _) = chunk_e.take::<(Chunk, ChunkCoord, MapLabel<L>)>().unwrap();
let chunk_id = chunk_e.id();
for tile_id in chunk.tiles.into_iter().flatten() {
world.despawn(tile_id);
}
Unset::<InMap<L, N>>::new(chunk_id, map_id).apply(world);
Withdraw::<InChunk<L, N>>::new(chunk_id).apply(world);
Unset::<InMap>::new(chunk_id, map_id).apply(world);
Withdraw::<InChunk>::new(chunk_id).apply(world);
Some(chunk_id)
} else {
None
Expand Down Expand Up @@ -594,8 +597,9 @@ pub fn insert_chunk_batch<L, const N: usize>(
world.get_entity_mut(chunk_id).unwrap().insert((
Chunk::new(L::CHUNK_SIZE.pow(N as u32)),
ChunkCoord::from(chunk_c),
MapLabel::<L>::default(),
));
Set::<InMap<L, N>>::new(chunk_id, map_id).apply(world);
Set::<InMap>::new(chunk_id, map_id).apply(world);
map.chunks.insert(chunk_c.into(), chunk_id);
}

Expand Down Expand Up @@ -629,10 +633,10 @@ where
.remove::<ChunkCoord<N>>(&chunk_c.into())
.and_then(|chunk_id| world.get_entity_mut(chunk_id))
{
chunk_e.remove::<(Chunk, ChunkCoord)>();
chunk_e.remove::<(Chunk, ChunkCoord, MapLabel<L>)>();
let chunk_id = chunk_e.id();
Unset::<InMap<L, N>>::new(chunk_id, map_id).apply(world);
Withdraw::<InChunk<L, N>>::new(chunk_id).apply(world);
Unset::<InMap>::new(chunk_id, map_id).apply(world);
Withdraw::<InChunk>::new(chunk_id).apply(world);
chunk_ids.push((chunk_c, chunk_id));
};
}
Expand Down Expand Up @@ -666,13 +670,13 @@ where
.remove::<ChunkCoord<N>>(&chunk_c.into())
.and_then(|chunk_id| world.get_entity_mut(chunk_id))
{
let (chunk, _) = chunk_e.take::<(Chunk, ChunkCoord)>().unwrap();
let (chunk, _, _) = chunk_e.take::<(Chunk, ChunkCoord, MapLabel<L>)>().unwrap();
let chunk_id = chunk_e.id();
for tile_id in chunk.tiles.into_iter().flatten() {
world.despawn(tile_id);
}
Unset::<InMap<L, N>>::new(chunk_id, map_id).apply(world);
Withdraw::<InChunk<L, N>>::new(chunk_id).apply(world);
Unset::<InMap>::new(chunk_id, map_id).apply(world);
Withdraw::<InChunk>::new(chunk_id).apply(world);
chunk_ids.push((chunk_c, chunk_id));
};
}
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_tiles/src/commands/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use aery::edges::CheckedDespawn;
use bevy::ecs::{entity::Entity, query::With, system::Command, world::World};

use crate::{
maps::{MapLabel, TileMap},
maps::{MapLabel, Tilemap},
prelude::TileMapLabel,
};

Expand All @@ -16,7 +16,7 @@ where
{
fn apply(self, world: &mut World) {
if let Ok(map_id) = world
.query_filtered::<Entity, (With<MapLabel<L>>, With<TileMap<N>>)>()
.query_filtered::<Entity, (With<MapLabel<L>>, With<Tilemap<N>>)>()
.get_single(world)
{
CheckedDespawn(map_id).apply(world);
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_tiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub mod maps;
/// Provides tile level utilities.
pub mod tiles;

///
/// Provides most of what you need to get started.
pub mod prelude {
pub use crate::commands::{TileCommandExt, TileCommands};

Expand Down
10 changes: 7 additions & 3 deletions crates/bevy_tiles/src/maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ pub trait TileMapLabel: Send + Sync + 'static {
const CHUNK_SIZE: usize;
}

/// Marks an entity as being part of the tilemap.
/// # Note
/// Manually updating this value, adding it, or removing it from an entity may
/// cause issues, please only mutate map information via commands.
#[derive(Component)]
pub(crate) struct MapLabel<L>(PhantomData<L>);
pub struct MapLabel<L>(PhantomData<L>);

impl<L> Default for MapLabel<L> {
fn default() -> Self {
Expand All @@ -27,11 +31,11 @@ impl<L> Default for MapLabel<L> {
/// Manually updating this value, adding it, or removing it from an entity may
/// cause issues, please only mutate map information via commands.
#[derive(Component)]
pub struct TileMap<const N: usize = 2> {
pub struct Tilemap<const N: usize = 2> {
pub(crate) chunks: HashMap<ChunkCoord<N>, Entity>,
}

impl<const N: usize> Default for TileMap<N> {
impl<const N: usize> Default for Tilemap<N> {
fn default() -> Self {
Self {
chunks: Default::default(),
Expand Down
8 changes: 7 additions & 1 deletion crates/bevy_tiles/src/tiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ impl<const N: usize> TileCoord<N> {
}
}

impl<const N: usize> From<[isize; N]> for TileCoord<N> {
fn from(value: [isize; N]) -> Self {
Self(value)
}
}

impl<const N: usize> Deref for TileCoord<N> {
type Target = [isize; N];

Expand All @@ -51,4 +57,4 @@ impl<const N: usize> Deref for TileCoord<N> {
/// An aery relation on tiles that point towards the chunk they are a part of.
#[derive(Relation)]
#[aery(Recursive)]
pub struct InChunk<L, const N: usize>(std::marker::PhantomData<L>);
pub struct InChunk;
Loading

0 comments on commit 7ef94f4

Please sign in to comment.