Create Bevy Relations from LDtk Entity References

LDtk allows entities to point to other entities using a field. This is analogous to a bevy "relation" - a component on one entity that stores the Entity identifier of another entity.

This chapter goes through one possible method for resolving LDtk entity references as such. This code is used in the field_instances cargo example, and facilitates "enemy" entities pointing to another "enemy" entity as their "mother".

Register unresolved reference

First, create a component representing an "unresolved" entity reference, storing the target entity's LDtk iid rather than a bevy Entity:

#![allow(unused)]
fn main() {
use bevy::prelude::*;
use bevy_ecs_ldtk::prelude::*;
#[derive(Debug, Default, Deref, DerefMut, Component)]
pub struct UnresolvedMotherRef(Option<EntityIid>);
}

Create a method for constructing this component from an &EntityInstance. This should retrieve the value of the entity reference field instance on the LDtk entity. Most likely, you'll use a hard-coded field identifier ("mother" in this example) to find it:

#![allow(unused)]
fn main() {
use bevy::prelude::*;
use bevy_ecs_ldtk::prelude::*;
pub struct UnresolvedMotherRef(Option<EntityIid>);
impl UnresolvedMotherRef {
    pub fn from_mother_field(entity_instance: &EntityInstance) -> UnresolvedMotherRef {
        UnresolvedMotherRef(
            entity_instance
                .get_maybe_entity_ref_field("mother")
                .expect("expected entity to have mother entity ref field")
                .as_ref()
                .map(|entity_ref| EntityIid::new(entity_ref.entity_iid.clone())),
        )
    }
}
}

Add this component to the LdtkEntity and configure it to be constructed using this method. This guide assumes that you've already registered this bundle to the app.

#![allow(unused)]
fn main() {
use bevy::prelude::*;
use bevy_ecs_ldtk::prelude::*;
#[derive(Debug, Default, Deref, DerefMut, Component)]
pub struct UnresolvedMotherRef(Option<EntityIid>);
impl UnresolvedMotherRef { fn from_mother_field(_: &EntityInstance) -> UnresolvedMotherRef { todo!() } }
#[derive(Default, Bundle, LdtkEntity)]
pub struct EnemyBundle {
    #[with(UnresolvedMotherRef::from_mother_field)]
    unresolved_mother: UnresolvedMotherRef,
    #[sprite_sheet_bundle]
    sprite_sheet_bundle: LdtkSpriteSheetBundle,
}
}

Resolve reference in post-processing

Create a second relational component that stores the actual bevy Entity that this Unresolved reference should "resolve" to.

#![allow(unused)]
fn main() {
use bevy::prelude::*;
use bevy_ecs_ldtk::prelude::*;
#[derive(Debug, Deref, DerefMut, Component, Reflect)]
pub struct Mother(Entity);
}

Finally, create a "post-processing" system that takes entities with the Unresolved component, finds the entity with the matching EntityIid, and replaces the Unresolved component with the relational component.

#![allow(unused)]
fn main() {
use bevy::prelude::*;
use bevy_ecs_ldtk::prelude::*;
#[derive(Debug, Default, Deref, DerefMut, Component)]
pub struct UnresolvedMotherRef(Option<EntityIid>);
#[derive(Debug, Deref, DerefMut, Component, Reflect)]
pub struct Mother(Entity);
pub fn resolve_mother_references(
    mut commands: Commands,
    unresolved_mothers: Query<(Entity, &UnresolvedMotherRef), Added<UnresolvedMotherRef>>,
    ldtk_entities: Query<(Entity, &EntityIid)>,
) {
    for (child_entity, unresolved_mother_ref) in unresolved_mothers.iter() {
        if let Some(mother_iid) = unresolved_mother_ref.0.as_ref() {
            let (mother_entity, _) = ldtk_entities
                .iter()
                .find(|(_, iid)| *iid == mother_iid)
                .expect("enemy's mother entity should exist");

            commands
                .entity(child_entity)
                .remove::<UnresolvedMotherRef>()
                .insert(Mother(mother_entity));
        } else {
            commands
                .entity(child_entity)
                .remove::<UnresolvedMotherRef>();
        }
    }
}
}