Level Selection

Once you have spawned an LdtkWorldBundle with a handle pointing to your LDtk project file, the levels you have selected will spawn as children of the world bundle. You have a couple options for selecting levels, which will be discussed in this chapter.

LevelSelection resource

The highest-level option for selecting a level to spawn is using the LevelSelection resource. This resource allows you to specify a particular level either by its indices in the project/world, its identifier, its iid, or its uid. Once this resource is added or changed, levels will be spawned/despawned in order to match your selection.

One additional feature worth pointing out is loading level neighbors. You can enable this with the settings resource LdtkSettings:

use bevy::prelude::*;
use bevy_ecs_ldtk::prelude::*;

fn main() {
    App::new()
        // other App builders
        .insert_resource(LevelSelection::index(0))
        .insert_resource(LdtkSettings {
            level_spawn_behavior: LevelSpawnBehavior::UseWorldTranslation {
                load_level_neighbors: true
            },
            ..default()
        })
        .run();
}

With this set, the plugin will spawn the currently-selected level's neighbors in addition to the currently-selected level. This can be especially useful for GridVania/Free-style worlds where it's important to have a level spawned before the player traverses to it. Note: this only works if you are using the LevelSelection resource.

LevelSet component

One component in the LdtkWorldBundle is LevelSet. This component can be used for lower-level level selection. Instead of selecting one level globally with a LevelSelection resource, you can select a specific set of levels by their iids. From the level_set cargo example:

use bevy::prelude::*;
use bevy_ecs_ldtk::prelude::*;
const LEVEL_IIDS: [&str; 8] = [
    "a3591db0-66b0-11ec-9cd7-43878cf4d0ab",
    "a35944c0-66b0-11ec-9cd7-6b4e2322a69e",
    "a35992e0-66b0-11ec-9cd7-8b2ebd1b98e2",
    "a359b9f0-66b0-11ec-9cd7-25dfb937d033",
    "a35a2f20-66b0-11ec-9cd7-db6f994e2834",
    "a35aa451-66b0-11ec-9cd7-438de356526d",
    "a35acb61-66b0-11ec-9cd7-f76e35cfda30",
    "a35b8eb0-66b0-11ec-9cd7-3d16ec48af10",
];

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());

    let level_set = LevelSet::from_iids(LEVEL_IIDS);

    commands.spawn(LdtkWorldBundle {
        ldtk_handle: asset_server.load("WorldMap_Free_layout.ldtk"),
        level_set,
        transform: Transform::from_xyz(-256., -144., 0.),
        ..Default::default()
    });
}
fn main() {}

This component is actually used by LevelSelection under the hood. So, in order for this workflow to work properly, no LevelSelection resource can exist in the world. This also implies, as mentioned in the previous section, that load_level_neighbors cannot be used with the LevelSet workflow. However, the LevelSpawnBehavior::UseWorldTranslation option in general does work, and should be used if you plan to spawn multiple levels anyway.

LevelSet is ideal for more complex level-spawning needs. It is an option if you need any level-spawning behavior that LevelSelection/load_level_neighbors are not capable of. Furthermore, if you have more than one LdtkWorldBundle spawned, it can be used to select different levels per-world, which is impossible with global level selection.

When the set of levels in the LevelSet is updated, an extra layer of change-detection is employed to make these changes idempotent/declarative. In other words, the plugin will observe what levels are already spawned before trying to respond to the changes in LevelSet. Only levels in the level set that aren't currently spawned will be spawned - and only levels not in the level set that are currently spawned will be despawned. Everything else will be left alone, remaining spawned or despawned appropriately.