Skip to content

Commit

Permalink
docs: write *Anatomy of the World* chapter of book (#285)
Browse files Browse the repository at this point in the history
This new chapter explains in greater detail the results of spawning the
world/levels. This includes the explanation of the hierarchy, worldly
entities, tile metadata, level backgrounds, layers with colliding tiles,
and z ordering.

Redundant explanations in the API reference have been replaced by
references to this chapter.
  • Loading branch information
Trouv authored Jan 31, 2024
1 parent 226c60c commit 29d5e33
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 32 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ jobs:
book/src/README.md
book/src/explanation/game-logic-integration.md
book/src/explanation/level-selection.md
book/src/explanation/anatomy-of-the-world.md
src/lib.rs
src/components/mod.rs
src/components/level_set.rs
src/resources/level_selection.rs
src/app/ldtk_entity.rs
2 changes: 1 addition & 1 deletion book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# Explanation
- [Level Selection](explanation/level-selection.md)
- [Game Logic Integration](explanation/game-logic-integration.md)
- [Hierarchy of the World]()
- [Anatomy of the World](explanation/anatomy-of-the-world.md)
- [Plugin Schedule]()
- [Asset Model]()
# How-To Guides
Expand Down
88 changes: 88 additions & 0 deletions book/src/explanation/anatomy-of-the-world.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Anatomy of the World
Once an [`LdtkWorldBundle`](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/prelude/struct.LdtkWorldBundle.html) is spawned, [levels are selected](level-selection.md), and the associated assets finish loading, the level spawning process begins. <!-- x-release-please-version -->
The result is a deeply nested hierarchy of entities which can be difficult to navigate, but predictable.
It can be useful to write code that makes assumptions about the relationships between `bevy_ecs_ldtk` entities.
To assist with this, this chapter will explain the anatomy of a `bevy_ecs_ldtk` world.

## Hierarchy
The basic hierarchy of spawned entities and their identifying components/bundles are as follows.
This does exclude some special cases which are explained in more detail below.
Each bullet indent indicates a parent/child relationship.
- The world entity, with an [`LdtkWorldBundle`](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/prelude/struct.LdtkWorldBundle.html) bundle. <!-- x-release-please-version -->
- The level entities, with a [`LevelIid`](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/prelude/struct.LevelIid.html) component. <!-- x-release-please-version -->
- For Entity layers - a layer entity with just a [`LayerMetadata`](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/prelude/struct.LayerMetadata.html) component. <!-- x-release-please-version -->
- LDtk Entity entities, with an [`EntityInstance`](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/ldtk/struct.EntityInstance.html) component, or possibly others if you're using [`LdtkEntity` registration](game-logic-integration.html#ldtkentity-and-ldtkintcell-registration). <!-- x-release-please-version -->
- For Tile/AutoTile/IntGrid layers: `bevy_ecs_tilemap` tilemap entities, with a [`TilemapBundle`](https://docs.rs/bevy_ecs_tilemap/latest/bevy_ecs_tilemap/type.TilemapBundle.html) **and** a [`LayerMetadata`](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/prelude/struct.LayerMetadata.html) component. <!-- x-release-please-version -->
- For IntGrid layers - tile entities with an [`IntGridCell`](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/prelude/struct.IntGridCell.html) component, or possibly others if you're using [`LdtkIntCell` registration](game-logic-integration.html#ldtkentity-and-ldtkintcell-registration). <!-- x-release-please-version -->
- For Tile/AutoTile layers (or IntGrid layers with AutoTile functionality) - `bevy_ecs_tilemap` tile entities, with a [`TileBundle`](https://docs.rs/bevy_ecs_tilemap/latest/bevy_ecs_tilemap/tiles/struct.TileBundle.html) bundle.

## Worldly Entities
The [`LdtkEntity` derive macro](game-logic-integration.html#ldtkentity-and-ldtkintcell-registration) allows you to define entities as ["worldly"](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/app/trait.LdtkEntity.html#worldly). <!-- x-release-please-version -->
The intention of this feature is to support entities that are allowed to persist and traverse between levels, like a player in a GridVania layout.

One consequence of an entity being worldly is a change in its placement in the above hierarchy.
Instead of being spawned as a child of the Entity layer entity, worldly entities will be children of the world entity (after one update).
This makes the worldly entity independent of their origin level, so that if the origin level is unloaded, the worldly entity can still persist.

Furthermore, a worldly entity will *not* be spawned if it already exists.
This prevents two of the same worldly entity existing if the origin level is despawned and respawned.
For example, if the worldly player entity traverses far enough away that their origin level is unloaded, then returns to it, there won't suddenly be two players.

## Tile metadata components
LDtk allows you to associate metadata with particular tiles in a tileset.
`bevy_ecs_ldtk` responds to this by adding additional components to tiles that have metadata *in addition to* those described in the [hierarchy](#hierarchy):

- [`TileMetadata`](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/prelude/struct.TileMetadata.html) <!-- x-release-please-version -->
- [`TileEnumTags`](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/prelude/struct.TileEnumTags.html) <!-- x-release-please-version -->

Naturally, this can only occur in Tile/AutoTile layers (or IntGrid layers with AutoTile functionality), since the metadata is defined on tilesets.

## Level backgrounds
LDtk allows you to supply a background color and a background image for individual levels.
`bevy_ecs_ldtk` renders these by default.
The background color is spawned as a normal bevy [`SpriteBundle`](https://docs.rs/bevy/latest/bevy/prelude/struct.SpriteBundle.html), as a child of the level entity.
The background image, if it exists, is also spawned as a `SpriteBundle`.

These background sprites can be disabled (not spawned) using the settings resource [`LdtkSettings`](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/prelude/struct.LdtkSettings.html): <!-- x-release-please-version -->
```rust,no_run
use bevy::prelude::*;
use bevy_ecs_ldtk::prelude::*;
fn main() {
App::new()
// other App builders
.insert_resource(LdtkSettings {
level_background: LevelBackground::Nonexistent,
..default()
})
.run();
}
```

## Layers with colliding tiles
It is possible for LDtk Tile/AutoTile layers to have colliding tiles.
In other words, a single layer can have more than one tile in the same location.

`bevy_ecs_tilemap` tilemaps only allow one tile per position.
So, `bevy_ecs_ldtk` supports layers with colliding tiles by spawning multiple tilemaps.
Each of them will have the same [`LayerMetadata`](https://docs.rs/bevy_ecs_ldtk/0.8.0/bevy_ecs_ldtk/prelude/struct.LayerMetadata.html) component. <!-- x-release-please-version -->
This means that users cannot assume that there will be only one `LayerMetadata` entity per layer.


## Z order
To correctly define the render order of the tiles and entities in a level, `bevy_ecs_ldtk` uses the `z` value of their `Transform` components.
Z order is only applied to [level backgrounds](#level-backgrounds), [layer entities](#layers-with-colliding-tiles), and [worldly entities](#worldly-entities).
Tiles and non-worldly entities will simply inherit the z-ordering in their `GlobalTransform`.

`bevy_ecs_ldtk` begins with a `z` value of 0 for the background-most entities, and increments this by 1 for each layer above that.
This sounds simple, but can actually be pretty difficult to predict thanks to some special cases mentioned above.

[Background colors and background images](#level-backgrounds) will usually get the `z` values of 0 and 1 respectively.
However, if the background image does not exist, the `z` value of 1 will be freed for the next layer instead.
If level backgrounds are disabled entirely, both 0 and 1 will be freed for the next layer.

From here, each layer generally increments the `z` value by 1.
However, note that [there can be multiple layer entities for a single LDtk layer](#layers-with-colliding-tiles).
Each of these additional layer entities will also increment the `z` value by 1.

Since this can be difficult to predict, it is generally recommended to avoid making assumptions about the `z` value of a layer.
4 changes: 3 additions & 1 deletion src/app/ldtk_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ use std::{collections::HashMap, marker::PhantomData};
///
/// [Worldly] entities don't despawn when their birth level despawns, and they don't respawn when
/// their birth level respawns.
/// This is useful for entities that travel across multiple levels, like a player.
/// For a more detailed explanation, please see the
/// [*Worldly Entities*](https://trouv.github.io/bevy_ecs_ldtk/v0.8.0/explanation/anatomy-of-the-world.html#worldly-entities) <!-- x-release-please-version -->
/// section of the `bevy_ecs_ldtk` book.
/// ```
/// # use bevy::prelude::*;
/// # use bevy_ecs_ldtk::prelude::*;
Expand Down
37 changes: 10 additions & 27 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,15 @@ pub struct IntGridCell {
pub value: i32,
}

/// [Component] that indicates that an ldtk entity should be a child of the world, not their layer.
/// [`Component`] that indicates that an ldtk entity should be a child of the world, not their layer.
///
/// By default, [LdtkEntity]s are children of the layer they spawn in.
/// This can be a problem if that entity is supposed to travel across multiple levels, since they
/// will despawn the moment the level they were born in despawns.
/// For a more detailed explanation, please see the
/// [*Worldly Entities*](https://trouv.github.io/bevy_ecs_ldtk/v0.8.0/explanation/anatomy-of-the-world.html#worldly-entities) <!-- x-release-please-version -->
/// section of the `bevy_ecs_ldtk` book.
///
/// This component makes them children of the [LdtkWorldBundle] (after one update),
/// so they can traverse levels without despawning.
/// Furthermore, this component prevents respawns of the same entity if the level they were born in
/// despawns/respawns.
/// For this purpose, it uses the `iid` stored in this component to uniquely identify ldtk
/// entities.
///
/// Implements [LdtkEntity], and can be added to an [LdtkEntity] bundle with the `#[worldly]` field
/// attribute. See [LdtkEntity#worldly] for more details.
/// Implements [`LdtkEntity`], and can be added to an [`LdtkEntity`] bundle with the `#[worldly]`
/// field attribute.
/// See [`LdtkEntity#worldly`] for attribute macro usage.
#[derive(Clone, Eq, PartialEq, Debug, Default, Hash, Component, Reflect)]
#[reflect(Component)]
pub struct Worldly {
Expand Down Expand Up @@ -335,20 +329,9 @@ pub(crate) struct EntityInstanceBundle {

/// `Bundle` for spawning LDtk worlds and their levels. The main bundle for using this plugin.
///
/// After the ldtk file is done loading, the levels you've chosen with [`LevelSelection`] or
/// [`LevelSet`] will begin to spawn.
/// Each level is its own entity, with the [`LdtkWorldBundle`] as its parent.
/// Each level has a [`LevelIid`] component.
///
/// All layers will also spawn as their own entities.
/// Each layer's parent will be the level entity.
/// Each layer will have a [`LayerMetadata`] component.
///
/// AutoTile, Tile, and IntGrid layer entities are bevy_ecs_tilemap TileMaps.
/// Each tile in these layers will have the layer entity as its parent.
///
/// For Entity layers, all LDtk entities will have the layer entity as their parent by default.
/// However, this behavior can be changed by marking them with the [`Worldly`] component.
/// For a more detailed explanation of the resulting world, please see the
/// [*Anatomy of the World*](https://trouv.github.io/bevy_ecs_ldtk/v0.8.0/explanation/anatomy-of-the-world.html) <!-- x-release-please-version -->
/// chapter of the `bevy_ecs_ldtk` book.
#[derive(Clone, Default, Bundle)]
pub struct LdtkWorldBundle {
pub ldtk_handle: Handle<LdtkProject>,
Expand Down
3 changes: 0 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@
//! The `derive`, `render`, and `internal_levels` features are enabled by default.
//! Furthermore, one or both of `internal_levels` and `external_levels` must be enabled.
//!
//! [App]: bevy::prelude::App
//! [Commands]: bevy::prelude::Commands
//! [Transform]: bevy::prelude::Transform
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/features.html#the-features-section
//! [LdtkEntity]: app::LdtkEntity
//! [LdtkIntCell]: app::LdtkEntity
Expand Down

0 comments on commit 29d5e33

Please sign in to comment.